sm8750: init kernel modules repo
This commit is contained in:
239
qcom/opensource/audio-kernel/soc/Kbuild
Normal file
239
qcom/opensource/audio-kernel/soc/Kbuild
Normal file
@@ -0,0 +1,239 @@
|
||||
# We can build either as part of a standalone Kernel build or as
|
||||
# an external module. Determine which mechanism is being used
|
||||
ifeq ($(MODNAME),)
|
||||
KERNEL_BUILD := 1
|
||||
else
|
||||
KERNEL_BUILD := 0
|
||||
endif
|
||||
|
||||
ifeq ($(KERNEL_BUILD), 1)
|
||||
# These are configurable via Kconfig for kernel-based builds
|
||||
# Need to explicitly configure for Android-based builds
|
||||
AUDIO_BLD_DIR := $(shell pwd)/kernel/msm-5.4
|
||||
AUDIO_ROOT := $(AUDIO_BLD_DIR)/techpack/audio
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_SND_SOC_AUTO), y)
|
||||
ifdef CONFIG_SND_SOC_SA8155
|
||||
include $(AUDIO_ROOT)/config/sa8155auto.conf
|
||||
export
|
||||
INCS += -include $(AUDIO_ROOT)/config/sa8155autoconf.h
|
||||
endif
|
||||
ifdef CONFIG_SND_SOC_SA6155
|
||||
include $(AUDIO_ROOT)/config/sa6155auto.conf
|
||||
export
|
||||
INCS += -include $(AUDIO_ROOT)/config/sa6155autoconf.h
|
||||
endif
|
||||
else
|
||||
ifeq ($(KERNEL_BUILD), 0)
|
||||
ifeq ($(CONFIG_ARCH_SM8150), y)
|
||||
ifdef CONFIG_SND_SOC_SA8155
|
||||
include $(AUDIO_ROOT)/config/sa8155auto.conf
|
||||
export
|
||||
INCS += -include $(AUDIO_ROOT)/config/sa8155autoconf.h
|
||||
else
|
||||
include $(AUDIO_ROOT)/config/sm8150auto.conf
|
||||
export
|
||||
INCS += -include $(AUDIO_ROOT)/config/sm8150autoconf.h
|
||||
endif
|
||||
endif
|
||||
ifeq ($(CONFIG_ARCH_KONA), y)
|
||||
include $(AUDIO_ROOT)/config/konaauto.conf
|
||||
INCS += -include $(AUDIO_ROOT)/config/konaautoconf.h
|
||||
endif
|
||||
ifeq ($(CONFIG_ARCH_WAIPIO), y)
|
||||
include $(AUDIO_ROOT)/config/waipioauto.conf
|
||||
INCS += -include $(AUDIO_ROOT)/config/waipioautoconf.h
|
||||
endif
|
||||
ifeq ($(CONFIG_ARCH_KALAMA), y)
|
||||
include $(AUDIO_ROOT)/config/kalamaauto.conf
|
||||
INCS += -include $(AUDIO_ROOT)/config/kalamaautoconf.h
|
||||
endif
|
||||
ifeq ($(CONFIG_ARCH_PINEAPPLE), y)
|
||||
include $(AUDIO_ROOT)/config/pineappleauto.conf
|
||||
INCS += -include $(AUDIO_ROOT)/config/pineappleautoconf.h
|
||||
endif
|
||||
ifeq ($(CONFIG_ARCH_LITO), y)
|
||||
include $(AUDIO_ROOT)/config/litoauto.conf
|
||||
export
|
||||
INCS += -include $(AUDIO_ROOT)/config/litoautoconf.h
|
||||
endif
|
||||
ifeq ($(CONFIG_ARCH_KHAJE), y)
|
||||
include $(AUDIO_ROOT)/config/bengalauto.conf
|
||||
export
|
||||
INCS += -include $(AUDIO_ROOT)/config/bengalautoconf.h
|
||||
endif
|
||||
ifeq ($(CONFIG_ARCH_HOLI), y)
|
||||
include $(AUDIO_ROOT)/config/holiauto.conf
|
||||
INCS += -include $(AUDIO_ROOT)/config/holiautoconf.h
|
||||
endif
|
||||
ifeq ($(CONFIG_ARCH_BLAIR), y)
|
||||
include $(AUDIO_ROOT)/config/holiauto.conf
|
||||
INCS += -include $(AUDIO_ROOT)/config/holiautoconf.h
|
||||
endif
|
||||
ifeq ($(CONFIG_ARCH_SM6150), y)
|
||||
ifdef CONFIG_SND_SOC_SA6155
|
||||
include $(AUDIO_ROOT)/config/sa6155auto.conf
|
||||
export
|
||||
INCS += -include $(AUDIO_ROOT)/config/sa6155autoconf.h
|
||||
else
|
||||
include $(AUDIO_ROOT)/config/sm6150auto.conf
|
||||
export
|
||||
INCS += -include $(AUDIO_ROOT)/config/sm6150autoconf.h
|
||||
endif
|
||||
endif
|
||||
ifeq ($(CONFIG_ARCH_TRINKET), y)
|
||||
include $(AUDIO_ROOT)/config/sm6150auto.conf
|
||||
export
|
||||
INCS += -include $(AUDIO_ROOT)/config/sm6150autoconf.h
|
||||
endif
|
||||
ifeq ($(CONFIG_ARCH_SDMSHRIKE), y)
|
||||
ifdef CONFIG_SND_SOC_SA8155
|
||||
include $(AUDIO_ROOT)/config/sa8155auto.conf
|
||||
export
|
||||
INCS += -include $(AUDIO_ROOT)/config/sa8155autoconf.h
|
||||
else
|
||||
include $(AUDIO_ROOT)/config/sm8150auto.conf
|
||||
export
|
||||
INCS += -include $(AUDIO_ROOT)/config/sm8150autoconf.h
|
||||
endif
|
||||
endif
|
||||
ifeq ($(CONFIG_ARCH_QCS405), y)
|
||||
include $(AUDIO_ROOT)/config/qcs405auto.conf
|
||||
export
|
||||
INCS += -include $(AUDIO_ROOT)/config/qcs405autoconf.h
|
||||
endif
|
||||
ifeq ($(CONFIG_QTI_QUIN_GVM), y)
|
||||
include $(AUDIO_ROOT)/config/gvmauto.conf
|
||||
export
|
||||
INCS += -include $(AUDIO_ROOT)/config/gvmautoconf.h
|
||||
endif
|
||||
ifeq ($(CONFIG_ARCH_SDXLEMUR), y)
|
||||
include $(AUDIO_ROOT)/config/sdxlemurauto.conf
|
||||
export
|
||||
INCS += -include $(AUDIO_ROOT)/config/sdxlemurautoconf.h
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
# As per target team, build is done as follows:
|
||||
# Defconfig : build with default flags
|
||||
# Slub : defconfig + CONFIG_SLUB_DEBUG := y +
|
||||
# CONFIG_SLUB_DEBUG_ON := y + CONFIG_PAGE_POISONING := y
|
||||
# Perf : Using appropriate msmXXXX-perf_defconfig
|
||||
#
|
||||
# Shipment builds (user variants) should not have any debug feature
|
||||
# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds
|
||||
# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since
|
||||
# there is no other way to identify defconfig builds, QTI internal
|
||||
# representation of perf builds (identified using the string 'perf'),
|
||||
# is used to identify if the build is a slub or defconfig one. This
|
||||
# way no critical debug feature will be enabled for perf and shipment
|
||||
# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT
|
||||
# config.
|
||||
|
||||
############ UAPI ############
|
||||
UAPI_DIR := uapi/audio
|
||||
UAPI_INC := -I$(AUDIO_ROOT)/include/$(UAPI_DIR)
|
||||
|
||||
############ COMMON ############
|
||||
COMMON_DIR := include
|
||||
COMMON_INC := -I$(AUDIO_ROOT)/$(COMMON_DIR)
|
||||
|
||||
############ SoC Modules ############
|
||||
|
||||
# for pinctrl WCD driver
|
||||
ifdef CONFIG_PINCTRL_WCD
|
||||
PINCTRL_WCD_OBJS += pinctrl-wcd.o
|
||||
endif
|
||||
|
||||
# for pinctrl LPI driver
|
||||
ifdef CONFIG_PINCTRL_LPI
|
||||
PINCTRL_LPI_OBJS += pinctrl-lpi.o
|
||||
endif
|
||||
|
||||
# for soundwire driver
|
||||
ifdef CONFIG_SOUNDWIRE_WCD_CTRL
|
||||
SWR_CTRL_OBJS += swr-wcd-ctrl.o
|
||||
endif
|
||||
|
||||
# for new soundwire driver
|
||||
ifdef CONFIG_SOUNDWIRE_MSTR_CTRL
|
||||
SWR_CTRL_OBJS += swr-mstr-ctrl.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_SOUNDWIRE
|
||||
INCS += -include $(KERNEL_SRC)/drivers/base/regmap/internal.h
|
||||
SWR_OBJS += regmap-swr.o
|
||||
SWR_OBJS += soundwire.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_SND_EVENT
|
||||
SND_EVENT_OBJS += snd_event.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_WCD_SPI_AC
|
||||
WCD_SPI_ACC_CTL_OBJS += wcd-spi-ac.o
|
||||
WCD_SPI_ACC_CTL_OBJS += wcd_spi_ctl_v01.o
|
||||
endif
|
||||
|
||||
LINUX_INC += -Iinclude/linux
|
||||
|
||||
INCS += $(COMMON_INC) \
|
||||
$(UAPI_INC)
|
||||
|
||||
EXTRA_CFLAGS += $(INCS)
|
||||
|
||||
|
||||
CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \
|
||||
-DANI_LITTLE_BIT_ENDIAN \
|
||||
-DDOT11F_LITTLE_ENDIAN_HOST \
|
||||
-DANI_COMPILER_TYPE_GCC \
|
||||
-DANI_OS_TYPE_ANDROID=6 \
|
||||
-DPTT_SOCK_SVC_ENABLE \
|
||||
-Wall\
|
||||
-Werror\
|
||||
-D__linux__
|
||||
|
||||
KBUILD_CPPFLAGS += $(CDEFINES)
|
||||
|
||||
|
||||
# Currently, for versions of gcc which support it, the kernel Makefile
|
||||
# is disabling the maybe-uninitialized warning. Re-enable it for the
|
||||
# AUDIO driver. Note that we must use EXTRA_CFLAGS here so that it
|
||||
# will override the kernel settings.
|
||||
ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y)
|
||||
EXTRA_CFLAGS += -Wmaybe-uninitialized
|
||||
endif
|
||||
#EXTRA_CFLAGS += -Wmissing-prototypes
|
||||
|
||||
ifeq ($(call cc-option-yn, -Wheader-guard),y)
|
||||
EXTRA_CFLAGS += -Wheader-guard
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_SND_SOC_GCOV), y)
|
||||
GCOV_PROFILE := y
|
||||
endif
|
||||
|
||||
# Module information used by KBuild framework
|
||||
obj-$(CONFIG_PINCTRL_WCD) += pinctrl_wcd_dlkm.o
|
||||
pinctrl_wcd_dlkm-y := $(PINCTRL_WCD_OBJS)
|
||||
|
||||
obj-$(CONFIG_PINCTRL_LPI) += pinctrl_lpi_dlkm.o
|
||||
pinctrl_lpi_dlkm-y := $(PINCTRL_LPI_OBJS)
|
||||
|
||||
obj-$(CONFIG_SOUNDWIRE) += swr_dlkm.o
|
||||
swr_dlkm-y := $(SWR_OBJS)
|
||||
|
||||
obj-$(CONFIG_SND_EVENT) += snd_event_dlkm.o
|
||||
snd_event_dlkm-y := $(SND_EVENT_OBJS)
|
||||
|
||||
obj-$(CONFIG_SOUNDWIRE_WCD_CTRL) += swr_ctrl_dlkm.o
|
||||
obj-$(CONFIG_SOUNDWIRE_MSTR_CTRL) += swr_ctrl_dlkm.o
|
||||
swr_ctrl_dlkm-y := $(SWR_CTRL_OBJS)
|
||||
|
||||
obj-$(CONFIG_WCD_SPI_AC) += wcd_spi_acc_ctl_dlkm.o
|
||||
wcd_spi_acc_ctl_dlkm-y := $(WCD_SPI_ACC_CTL_OBJS)
|
||||
|
||||
# inject some build related information
|
||||
DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\"
|
||||
6
qcom/opensource/audio-kernel/soc/Makefile
Normal file
6
qcom/opensource/audio-kernel/soc/Makefile
Normal file
@@ -0,0 +1,6 @@
|
||||
modules:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(M) modules $(KBUILD_OPTIONS) VERBOSE=1
|
||||
modules_install:
|
||||
$(MAKE) M=$(M) -C $(KERNEL_SRC) modules_install
|
||||
clean:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(M) clean
|
||||
249
qcom/opensource/audio-kernel/soc/core.h
Normal file
249
qcom/opensource/audio-kernel/soc/core.h
Normal file
@@ -0,0 +1,249 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Core private header for the pin control subsystem
|
||||
*
|
||||
* Copyright (C) 2011 ST-Ericsson SA
|
||||
* Written on behalf of Linaro for ST-Ericsson
|
||||
*
|
||||
* Author: Linus Walleij <linus.walleij@linaro.org>
|
||||
*/
|
||||
|
||||
#include <linux/kref.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/radix-tree.h>
|
||||
#include <linux/pinctrl/pinconf.h>
|
||||
#include <linux/pinctrl/machine.h>
|
||||
|
||||
struct pinctrl_gpio_range;
|
||||
|
||||
/**
|
||||
* struct pinctrl_dev - pin control class device
|
||||
* @node: node to include this pin controller in the global pin controller list
|
||||
* @desc: the pin controller descriptor supplied when initializing this pin
|
||||
* controller
|
||||
* @pin_desc_tree: each pin descriptor for this pin controller is stored in
|
||||
* this radix tree
|
||||
* @pin_group_tree: optionally each pin group can be stored in this radix tree
|
||||
* @num_groups: optionally number of groups can be kept here
|
||||
* @pin_function_tree: optionally each function can be stored in this radix tree
|
||||
* @num_functions: optionally number of functions can be kept here
|
||||
* @gpio_ranges: a list of GPIO ranges that is handled by this pin controller,
|
||||
* ranges are added to this list at runtime
|
||||
* @dev: the device entry for this pin controller
|
||||
* @owner: module providing the pin controller, used for refcounting
|
||||
* @driver_data: driver data for drivers registering to the pin controller
|
||||
* subsystem
|
||||
* @p: result of pinctrl_get() for this device
|
||||
* @hog_default: default state for pins hogged by this device
|
||||
* @hog_sleep: sleep state for pins hogged by this device
|
||||
* @mutex: mutex taken on each pin controller specific action
|
||||
* @device_root: debugfs root for this device
|
||||
*/
|
||||
struct pinctrl_dev {
|
||||
struct list_head node;
|
||||
struct pinctrl_desc *desc;
|
||||
struct radix_tree_root pin_desc_tree;
|
||||
#ifdef CONFIG_GENERIC_PINCTRL_GROUPS
|
||||
struct radix_tree_root pin_group_tree;
|
||||
unsigned int num_groups;
|
||||
#endif
|
||||
#ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS
|
||||
struct radix_tree_root pin_function_tree;
|
||||
unsigned int num_functions;
|
||||
#endif
|
||||
struct list_head gpio_ranges;
|
||||
struct device *dev;
|
||||
struct module *owner;
|
||||
void *driver_data;
|
||||
struct pinctrl *p;
|
||||
struct pinctrl_state *hog_default;
|
||||
struct pinctrl_state *hog_sleep;
|
||||
struct mutex mutex;
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *device_root;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pinctrl - per-device pin control state holder
|
||||
* @node: global list node
|
||||
* @dev: the device using this pin control handle
|
||||
* @states: a list of states for this device
|
||||
* @state: the current state
|
||||
* @dt_maps: the mapping table chunks dynamically parsed from device tree for
|
||||
* this device, if any
|
||||
* @users: reference count
|
||||
*/
|
||||
struct pinctrl {
|
||||
struct list_head node;
|
||||
struct device *dev;
|
||||
struct list_head states;
|
||||
struct pinctrl_state *state;
|
||||
struct list_head dt_maps;
|
||||
struct kref users;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pinctrl_state - a pinctrl state for a device
|
||||
* @node: list node for struct pinctrl's @states field
|
||||
* @name: the name of this state
|
||||
* @settings: a list of settings for this state
|
||||
*/
|
||||
struct pinctrl_state {
|
||||
struct list_head node;
|
||||
const char *name;
|
||||
struct list_head settings;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pinctrl_setting_mux - setting data for MAP_TYPE_MUX_GROUP
|
||||
* @group: the group selector to program
|
||||
* @func: the function selector to program
|
||||
*/
|
||||
struct pinctrl_setting_mux {
|
||||
unsigned group;
|
||||
unsigned func;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pinctrl_setting_configs - setting data for MAP_TYPE_CONFIGS_*
|
||||
* @group_or_pin: the group selector or pin ID to program
|
||||
* @configs: a pointer to an array of config parameters/values to program into
|
||||
* hardware. Each individual pin controller defines the format and meaning
|
||||
* of config parameters.
|
||||
* @num_configs: the number of entries in array @configs
|
||||
*/
|
||||
struct pinctrl_setting_configs {
|
||||
unsigned group_or_pin;
|
||||
unsigned long *configs;
|
||||
unsigned num_configs;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pinctrl_setting - an individual mux or config setting
|
||||
* @node: list node for struct pinctrl_settings's @settings field
|
||||
* @type: the type of setting
|
||||
* @pctldev: pin control device handling to be programmed. Not used for
|
||||
* PIN_MAP_TYPE_DUMMY_STATE.
|
||||
* @dev_name: the name of the device using this state
|
||||
* @data: Data specific to the setting type
|
||||
*/
|
||||
struct pinctrl_setting {
|
||||
struct list_head node;
|
||||
enum pinctrl_map_type type;
|
||||
struct pinctrl_dev *pctldev;
|
||||
const char *dev_name;
|
||||
union {
|
||||
struct pinctrl_setting_mux mux;
|
||||
struct pinctrl_setting_configs configs;
|
||||
} data;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pin_desc - pin descriptor for each physical pin in the arch
|
||||
* @pctldev: corresponding pin control device
|
||||
* @name: a name for the pin, e.g. the name of the pin/pad/finger on a
|
||||
* datasheet or such
|
||||
* @dynamic_name: if the name of this pin was dynamically allocated
|
||||
* @drv_data: driver-defined per-pin data. pinctrl core does not touch this
|
||||
* @mux_usecount: If zero, the pin is not claimed, and @owner should be NULL.
|
||||
* If non-zero, this pin is claimed by @owner. This field is an integer
|
||||
* rather than a boolean, since pinctrl_get() might process multiple
|
||||
* mapping table entries that refer to, and hence claim, the same group
|
||||
* or pin, and each of these will increment the @usecount.
|
||||
* @mux_owner: The name of device that called pinctrl_get().
|
||||
* @mux_setting: The most recent selected mux setting for this pin, if any.
|
||||
* @gpio_owner: If pinctrl_gpio_request() was called for this pin, this is
|
||||
* the name of the GPIO that "owns" this pin.
|
||||
*/
|
||||
struct pin_desc {
|
||||
struct pinctrl_dev *pctldev;
|
||||
const char *name;
|
||||
bool dynamic_name;
|
||||
void *drv_data;
|
||||
/* These fields only added when supporting pinmux drivers */
|
||||
#ifdef CONFIG_PINMUX
|
||||
unsigned mux_usecount;
|
||||
const char *mux_owner;
|
||||
const struct pinctrl_setting_mux *mux_setting;
|
||||
const char *gpio_owner;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pinctrl_maps - a list item containing part of the mapping table
|
||||
* @node: mapping table list node
|
||||
* @maps: array of mapping table entries
|
||||
* @num_maps: the number of entries in @maps
|
||||
*/
|
||||
struct pinctrl_maps {
|
||||
struct list_head node;
|
||||
const struct pinctrl_map *maps;
|
||||
unsigned num_maps;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_GENERIC_PINCTRL_GROUPS
|
||||
|
||||
/**
|
||||
* struct group_desc - generic pin group descriptor
|
||||
* @name: name of the pin group
|
||||
* @pins: array of pins that belong to the group
|
||||
* @num_pins: number of pins in the group
|
||||
* @data: pin controller driver specific data
|
||||
*/
|
||||
struct group_desc {
|
||||
const char *name;
|
||||
int *pins;
|
||||
int num_pins;
|
||||
void *data;
|
||||
};
|
||||
|
||||
int pinctrl_generic_get_group_count(struct pinctrl_dev *pctldev);
|
||||
|
||||
const char *pinctrl_generic_get_group_name(struct pinctrl_dev *pctldev,
|
||||
unsigned int group_selector);
|
||||
|
||||
int pinctrl_generic_get_group_pins(struct pinctrl_dev *pctldev,
|
||||
unsigned int group_selector,
|
||||
const unsigned int **pins,
|
||||
unsigned int *npins);
|
||||
|
||||
struct group_desc *pinctrl_generic_get_group(struct pinctrl_dev *pctldev,
|
||||
unsigned int group_selector);
|
||||
|
||||
int pinctrl_generic_add_group(struct pinctrl_dev *pctldev, const char *name,
|
||||
int *gpins, int ngpins, void *data);
|
||||
|
||||
int pinctrl_generic_remove_group(struct pinctrl_dev *pctldev,
|
||||
unsigned int group_selector);
|
||||
|
||||
#endif /* CONFIG_GENERIC_PINCTRL_GROUPS */
|
||||
|
||||
struct pinctrl_dev *get_pinctrl_dev_from_devname(const char *dev_name);
|
||||
struct pinctrl_dev *get_pinctrl_dev_from_of_node(struct device_node *np);
|
||||
int pin_get_from_name(struct pinctrl_dev *pctldev, const char *name);
|
||||
const char *pin_get_name(struct pinctrl_dev *pctldev, const unsigned pin);
|
||||
int pinctrl_get_group_selector(struct pinctrl_dev *pctldev,
|
||||
const char *pin_group);
|
||||
|
||||
static inline struct pin_desc *pin_desc_get(struct pinctrl_dev *pctldev,
|
||||
unsigned int pin)
|
||||
{
|
||||
return radix_tree_lookup(&pctldev->pin_desc_tree, pin);
|
||||
}
|
||||
|
||||
extern struct pinctrl_gpio_range *
|
||||
pinctrl_find_gpio_range_from_pin_nolock(struct pinctrl_dev *pctldev,
|
||||
unsigned int pin);
|
||||
|
||||
extern int pinctrl_force_sleep(struct pinctrl_dev *pctldev);
|
||||
extern int pinctrl_force_default(struct pinctrl_dev *pctldev);
|
||||
|
||||
extern struct mutex pinctrl_maps_mutex;
|
||||
extern struct list_head pinctrl_maps;
|
||||
|
||||
#define for_each_maps(_maps_node_, _i_, _map_) \
|
||||
list_for_each_entry(_maps_node_, &pinctrl_maps, node) \
|
||||
for (_i_ = 0, _map_ = &_maps_node_->maps[_i_]; \
|
||||
_i_ < _maps_node_->num_maps; \
|
||||
_i_++, _map_ = &_maps_node_->maps[_i_])
|
||||
1031
qcom/opensource/audio-kernel/soc/pinctrl-lpi.c
Normal file
1031
qcom/opensource/audio-kernel/soc/pinctrl-lpi.c
Normal file
File diff suppressed because it is too large
Load Diff
43
qcom/opensource/audio-kernel/soc/pinctrl-utils.h
Normal file
43
qcom/opensource/audio-kernel/soc/pinctrl-utils.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Utils functions to implement the pincontrol driver.
|
||||
*
|
||||
* Copyright (c) 2013, NVIDIA Corporation.
|
||||
*
|
||||
* Author: Laxman Dewangan <ldewangan@nvidia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
|
||||
* whether express or implied; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307, USA
|
||||
*/
|
||||
#ifndef __PINCTRL_UTILS_H__
|
||||
#define __PINCTRL_UTILS_H__
|
||||
|
||||
int pinctrl_utils_reserve_map(struct pinctrl_dev *pctldev,
|
||||
struct pinctrl_map **map, unsigned *reserved_maps,
|
||||
unsigned *num_maps, unsigned reserve);
|
||||
int pinctrl_utils_add_map_mux(struct pinctrl_dev *pctldev,
|
||||
struct pinctrl_map **map, unsigned *reserved_maps,
|
||||
unsigned *num_maps, const char *group,
|
||||
const char *function);
|
||||
int pinctrl_utils_add_map_configs(struct pinctrl_dev *pctldev,
|
||||
struct pinctrl_map **map, unsigned *reserved_maps,
|
||||
unsigned *num_maps, const char *group,
|
||||
unsigned long *configs, unsigned num_configs,
|
||||
enum pinctrl_map_type type);
|
||||
int pinctrl_utils_add_config(struct pinctrl_dev *pctldev,
|
||||
unsigned long **configs, unsigned *num_configs,
|
||||
unsigned long config);
|
||||
void pinctrl_utils_free_map(struct pinctrl_dev *pctldev,
|
||||
struct pinctrl_map *map, unsigned num_maps);
|
||||
|
||||
#endif /* __PINCTRL_UTILS_H__ */
|
||||
428
qcom/opensource/audio-kernel/soc/pinctrl-wcd.c
Normal file
428
qcom/opensource/audio-kernel/soc/pinctrl-wcd.c
Normal file
@@ -0,0 +1,428 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2016-2017, 2019, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pinctrl/pinconf-generic.h>
|
||||
#include <linux/pinctrl/pinconf.h>
|
||||
#include <linux/pinctrl/pinmux.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <asoc/wcd934x_registers.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "pinctrl-utils.h"
|
||||
|
||||
#define WCD_REG_DIR_CTL WCD934X_CHIP_TIER_CTRL_GPIO_CTL_OE
|
||||
#define WCD_REG_VAL_CTL WCD934X_CHIP_TIER_CTRL_GPIO_CTL_DATA
|
||||
#define WCD_GPIO_PULL_UP 1
|
||||
#define WCD_GPIO_PULL_DOWN 2
|
||||
#define WCD_GPIO_BIAS_DISABLE 3
|
||||
#define WCD_GPIO_STRING_LEN 20
|
||||
|
||||
/**
|
||||
* struct wcd_gpio_pad - keep current GPIO settings
|
||||
* @offset: offset of gpio.
|
||||
* @is_valid: Set to false, when GPIO in high Z state.
|
||||
* @value: value of a pin
|
||||
* @output_enabled: Set to true if GPIO is output and false if it is input
|
||||
* @pullup: Constant current which flow through GPIO output buffer.
|
||||
* @strength: Drive strength of a pin
|
||||
*/
|
||||
struct wcd_gpio_pad {
|
||||
u16 offset;
|
||||
bool is_valid;
|
||||
bool value;
|
||||
bool output_enabled;
|
||||
unsigned int pullup;
|
||||
unsigned int strength;
|
||||
};
|
||||
|
||||
struct wcd_gpio_priv {
|
||||
struct device *dev;
|
||||
struct regmap *map;
|
||||
struct pinctrl_dev *ctrl;
|
||||
struct gpio_chip chip;
|
||||
};
|
||||
|
||||
static int wcd_gpio_read(struct wcd_gpio_priv *priv_data,
|
||||
struct wcd_gpio_pad *pad, unsigned int addr)
|
||||
{
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(priv_data->map, addr, &val);
|
||||
if (ret < 0)
|
||||
dev_err(priv_data->dev, "%s: read 0x%x failed\n",
|
||||
__func__, addr);
|
||||
else
|
||||
ret = (val >> pad->offset);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wcd_gpio_write(struct wcd_gpio_priv *priv_data,
|
||||
struct wcd_gpio_pad *pad, unsigned int addr,
|
||||
unsigned int val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(priv_data->map, addr, (1 << pad->offset),
|
||||
val << pad->offset);
|
||||
if (ret < 0)
|
||||
dev_err(priv_data->dev, "write 0x%x failed\n", addr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wcd_get_groups_count(struct pinctrl_dev *pctldev)
|
||||
{
|
||||
return pctldev->desc->npins;
|
||||
}
|
||||
|
||||
static const char *wcd_get_group_name(struct pinctrl_dev *pctldev,
|
||||
unsigned int pin)
|
||||
{
|
||||
return pctldev->desc->pins[pin].name;
|
||||
}
|
||||
|
||||
static int wcd_get_group_pins(struct pinctrl_dev *pctldev, unsigned int pin,
|
||||
const unsigned int **pins, unsigned int *num_pins)
|
||||
{
|
||||
*pins = &pctldev->desc->pins[pin].number;
|
||||
*num_pins = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pinctrl_ops wcd_pinctrl_ops = {
|
||||
.get_groups_count = wcd_get_groups_count,
|
||||
.get_group_name = wcd_get_group_name,
|
||||
.get_group_pins = wcd_get_group_pins,
|
||||
.dt_node_to_map = pinconf_generic_dt_node_to_map_group,
|
||||
.dt_free_map = pinctrl_utils_free_map,
|
||||
};
|
||||
|
||||
static int wcd_config_get(struct pinctrl_dev *pctldev,
|
||||
unsigned int pin, unsigned long *config)
|
||||
{
|
||||
unsigned int param = pinconf_to_config_param(*config);
|
||||
struct wcd_gpio_pad *pad;
|
||||
unsigned int arg;
|
||||
|
||||
pad = pctldev->desc->pins[pin].drv_data;
|
||||
|
||||
switch (param) {
|
||||
case PIN_CONFIG_BIAS_PULL_DOWN:
|
||||
arg = pad->pullup == WCD_GPIO_PULL_DOWN;
|
||||
break;
|
||||
case PIN_CONFIG_BIAS_DISABLE:
|
||||
arg = pad->pullup = WCD_GPIO_BIAS_DISABLE;
|
||||
break;
|
||||
case PIN_CONFIG_BIAS_PULL_UP:
|
||||
arg = pad->pullup == WCD_GPIO_PULL_UP;
|
||||
break;
|
||||
case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
|
||||
arg = !pad->is_valid;
|
||||
break;
|
||||
case PIN_CONFIG_INPUT_ENABLE:
|
||||
arg = pad->output_enabled;
|
||||
break;
|
||||
case PIN_CONFIG_OUTPUT:
|
||||
arg = pad->value;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*config = pinconf_to_config_packed(param, arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wcd_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
|
||||
unsigned long *configs, unsigned int nconfs)
|
||||
{
|
||||
struct wcd_gpio_priv *priv_data = pinctrl_dev_get_drvdata(pctldev);
|
||||
struct wcd_gpio_pad *pad;
|
||||
unsigned int param, arg;
|
||||
int i, ret;
|
||||
|
||||
pad = pctldev->desc->pins[pin].drv_data;
|
||||
|
||||
for (i = 0; i < nconfs; i++) {
|
||||
param = pinconf_to_config_param(configs[i]);
|
||||
arg = pinconf_to_config_argument(configs[i]);
|
||||
|
||||
dev_dbg(priv_data->dev, "%s: param: %d arg: %d",
|
||||
__func__, param, arg);
|
||||
|
||||
switch (param) {
|
||||
case PIN_CONFIG_BIAS_DISABLE:
|
||||
pad->pullup = WCD_GPIO_BIAS_DISABLE;
|
||||
break;
|
||||
case PIN_CONFIG_BIAS_PULL_UP:
|
||||
pad->pullup = WCD_GPIO_PULL_UP;
|
||||
break;
|
||||
case PIN_CONFIG_BIAS_PULL_DOWN:
|
||||
pad->pullup = WCD_GPIO_PULL_DOWN;
|
||||
break;
|
||||
case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
|
||||
pad->is_valid = false;
|
||||
break;
|
||||
case PIN_CONFIG_INPUT_ENABLE:
|
||||
pad->output_enabled = false;
|
||||
break;
|
||||
case PIN_CONFIG_OUTPUT:
|
||||
pad->output_enabled = true;
|
||||
pad->value = arg;
|
||||
break;
|
||||
case PIN_CONFIG_DRIVE_STRENGTH:
|
||||
pad->strength = arg;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (pad->output_enabled) {
|
||||
ret = wcd_gpio_write(priv_data, pad, WCD_REG_DIR_CTL,
|
||||
pad->output_enabled);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
ret = wcd_gpio_write(priv_data, pad, WCD_REG_VAL_CTL,
|
||||
pad->value);
|
||||
} else
|
||||
ret = wcd_gpio_write(priv_data, pad, WCD_REG_DIR_CTL,
|
||||
pad->output_enabled);
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct pinconf_ops wcd_pinconf_ops = {
|
||||
.is_generic = true,
|
||||
.pin_config_group_get = wcd_config_get,
|
||||
.pin_config_group_set = wcd_config_set,
|
||||
};
|
||||
|
||||
static int wcd_gpio_direction_input(struct gpio_chip *chip, unsigned int pin)
|
||||
{
|
||||
struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip);
|
||||
unsigned long config;
|
||||
|
||||
config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1);
|
||||
|
||||
return wcd_config_set(priv_data->ctrl, pin, &config, 1);
|
||||
}
|
||||
|
||||
static int wcd_gpio_direction_output(struct gpio_chip *chip,
|
||||
unsigned int pin, int val)
|
||||
{
|
||||
struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip);
|
||||
unsigned long config;
|
||||
|
||||
config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val);
|
||||
|
||||
return wcd_config_set(priv_data->ctrl, pin, &config, 1);
|
||||
}
|
||||
|
||||
static int wcd_gpio_get(struct gpio_chip *chip, unsigned int pin)
|
||||
{
|
||||
struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip);
|
||||
struct wcd_gpio_pad *pad;
|
||||
int value;
|
||||
|
||||
pad = priv_data->ctrl->desc->pins[pin].drv_data;
|
||||
|
||||
if (!pad->is_valid)
|
||||
return -EINVAL;
|
||||
|
||||
value = wcd_gpio_read(priv_data, pad, WCD_REG_VAL_CTL);
|
||||
return value;
|
||||
}
|
||||
|
||||
static void wcd_gpio_set(struct gpio_chip *chip, unsigned int pin, int value)
|
||||
{
|
||||
struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip);
|
||||
unsigned long config;
|
||||
|
||||
config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value);
|
||||
|
||||
wcd_config_set(priv_data->ctrl, pin, &config, 1);
|
||||
}
|
||||
|
||||
static const struct gpio_chip wcd_gpio_chip = {
|
||||
.direction_input = wcd_gpio_direction_input,
|
||||
.direction_output = wcd_gpio_direction_output,
|
||||
.get = wcd_gpio_get,
|
||||
.set = wcd_gpio_set,
|
||||
};
|
||||
|
||||
static int wcd_pinctrl_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct pinctrl_pin_desc *pindesc;
|
||||
struct pinctrl_desc *pctrldesc;
|
||||
struct wcd_gpio_pad *pad, *pads;
|
||||
struct wcd_gpio_priv *priv_data;
|
||||
int ret, i, j;
|
||||
u32 npins;
|
||||
char **name;
|
||||
|
||||
ret = of_property_read_u32(dev->of_node, "qcom,gpios-count", &npins);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s: Looking up %s property in node %s failed\n",
|
||||
__func__, "qcom,gpios-count", dev->of_node->full_name);
|
||||
ret = -EINVAL;
|
||||
goto err_priv_alloc;
|
||||
}
|
||||
if (!npins) {
|
||||
dev_err(dev, "%s: no.of pins are 0\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto err_priv_alloc;
|
||||
}
|
||||
|
||||
priv_data = devm_kzalloc(dev, sizeof(*priv_data), GFP_KERNEL);
|
||||
if (!priv_data) {
|
||||
ret = -ENOMEM;
|
||||
goto err_priv_alloc;
|
||||
}
|
||||
|
||||
priv_data->dev = dev;
|
||||
priv_data->map = dev_get_regmap(dev->parent, NULL);
|
||||
if (!priv_data->map) {
|
||||
dev_err(dev, "%s: failed to get regmap\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto err_regmap;
|
||||
}
|
||||
|
||||
pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL);
|
||||
if (!pindesc) {
|
||||
ret = -ENOMEM;
|
||||
goto err_pinsec_alloc;
|
||||
}
|
||||
|
||||
pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL);
|
||||
if (!pads) {
|
||||
ret = -ENOMEM;
|
||||
goto err_pads_alloc;
|
||||
}
|
||||
|
||||
pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL);
|
||||
if (!pctrldesc) {
|
||||
ret = -ENOMEM;
|
||||
goto err_pinctrl_alloc;
|
||||
}
|
||||
|
||||
pctrldesc->pctlops = &wcd_pinctrl_ops;
|
||||
pctrldesc->confops = &wcd_pinconf_ops;
|
||||
pctrldesc->owner = THIS_MODULE;
|
||||
pctrldesc->name = dev_name(dev);
|
||||
pctrldesc->pins = pindesc;
|
||||
pctrldesc->npins = npins;
|
||||
|
||||
name = devm_kcalloc(dev, npins, sizeof(char *), GFP_KERNEL);
|
||||
if (!name) {
|
||||
ret = -ENOMEM;
|
||||
goto err_name_alloc;
|
||||
}
|
||||
for (i = 0; i < npins; i++, pindesc++) {
|
||||
name[i] = devm_kzalloc(dev, sizeof(char) * WCD_GPIO_STRING_LEN,
|
||||
GFP_KERNEL);
|
||||
if (!name[i]) {
|
||||
ret = -ENOMEM;
|
||||
goto err_pin;
|
||||
}
|
||||
pad = &pads[i];
|
||||
pindesc->drv_data = pad;
|
||||
pindesc->number = i;
|
||||
snprintf(name[i], (WCD_GPIO_STRING_LEN - 1), "gpio%d", (i+1));
|
||||
pindesc->name = name[i];
|
||||
pad->offset = i;
|
||||
pad->is_valid = true;
|
||||
}
|
||||
|
||||
priv_data->chip = wcd_gpio_chip;
|
||||
priv_data->chip.parent = dev;
|
||||
priv_data->chip.base = -1;
|
||||
priv_data->chip.ngpio = npins;
|
||||
priv_data->chip.label = dev_name(dev);
|
||||
priv_data->chip.of_gpio_n_cells = 2;
|
||||
priv_data->chip.can_sleep = false;
|
||||
|
||||
priv_data->ctrl = devm_pinctrl_register(dev, pctrldesc, priv_data);
|
||||
if (IS_ERR(priv_data->ctrl)) {
|
||||
dev_err(dev, "%s: failed to register to pinctrl\n", __func__);
|
||||
ret = PTR_ERR(priv_data->ctrl);
|
||||
goto err_pin;
|
||||
}
|
||||
|
||||
ret = gpiochip_add_data(&priv_data->chip, priv_data);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s: can't add gpio chip\n", __func__);
|
||||
goto err_pin;
|
||||
}
|
||||
|
||||
ret = gpiochip_add_pin_range(&priv_data->chip, dev_name(dev), 0, 0,
|
||||
npins);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s: failed to add pin range\n", __func__);
|
||||
goto err_range;
|
||||
}
|
||||
platform_set_drvdata(pdev, priv_data);
|
||||
|
||||
return 0;
|
||||
|
||||
err_range:
|
||||
gpiochip_remove(&priv_data->chip);
|
||||
err_pin:
|
||||
for (j = 0; j < i; j++)
|
||||
devm_kfree(dev, name[j]);
|
||||
devm_kfree(dev, name);
|
||||
err_name_alloc:
|
||||
devm_kfree(dev, pctrldesc);
|
||||
err_pinctrl_alloc:
|
||||
devm_kfree(dev, pads);
|
||||
err_pads_alloc:
|
||||
devm_kfree(dev, pindesc);
|
||||
err_pinsec_alloc:
|
||||
err_regmap:
|
||||
devm_kfree(dev, priv_data);
|
||||
err_priv_alloc:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wcd_pinctrl_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct wcd_gpio_priv *priv_data = platform_get_drvdata(pdev);
|
||||
|
||||
gpiochip_remove(&priv_data->chip);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id wcd_pinctrl_of_match[] = {
|
||||
{ .compatible = "qcom,wcd-pinctrl" },
|
||||
{ },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, wcd_pinctrl_of_match);
|
||||
|
||||
static struct platform_driver wcd_pinctrl_driver = {
|
||||
.driver = {
|
||||
.name = "qcom-wcd-pinctrl",
|
||||
.of_match_table = wcd_pinctrl_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = wcd_pinctrl_probe,
|
||||
.remove = wcd_pinctrl_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(wcd_pinctrl_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Qualcomm Technologies, Inc WCD GPIO pin control driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
283
qcom/opensource/audio-kernel/soc/regmap-swr.c
Normal file
283
qcom/opensource/audio-kernel/soc/regmap-swr.c
Normal file
@@ -0,0 +1,283 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <soc/soundwire.h>
|
||||
|
||||
#define ADDR_BYTES (2)
|
||||
#define ADDR_BYTES_4 (4)
|
||||
#define VAL_BYTES (1)
|
||||
#define PAD_BYTES (0)
|
||||
#define SCP1_ADDRESS_VAL_MASK (0x7f800000)
|
||||
#define SCP2_ADDRESS_VAL_MASK (0x007f8000)
|
||||
#define BIT_WIDTH_CHECK_MASK (0xffff0000)
|
||||
#define SCP1_ADDRESS_VAL_SHIFT (23)
|
||||
#define SCP2_ADDRESS_VAL_SHIFT (15)
|
||||
#define SCP1_ADDRESS (0X48)
|
||||
#define SCP2_ADDRESS (0X49)
|
||||
#define SDCA_READ_WRITE_BIT (0x8000)
|
||||
static DEFINE_MUTEX(swr_rw_lock);
|
||||
|
||||
static int regmap_swr_reg_address_get(struct swr_device *swr,
|
||||
u16 *reg_addr, const void *reg, size_t reg_size)
|
||||
{
|
||||
u8 scp1_val = 0, scp2_val = 0;
|
||||
u32 temp = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (reg_size == ADDR_BYTES_4) {
|
||||
temp = (*(u32 *)reg) & SCP1_ADDRESS_VAL_MASK;
|
||||
scp1_val = temp >> SCP1_ADDRESS_VAL_SHIFT;
|
||||
|
||||
temp = (*(u32 *)reg) & SCP2_ADDRESS_VAL_MASK;
|
||||
scp2_val = temp >> SCP2_ADDRESS_VAL_SHIFT;
|
||||
|
||||
if (scp1_val || scp2_val) {
|
||||
if (scp1_val != swr->g_scp1_val) {
|
||||
ret = swr_write(swr, swr->dev_num, SCP1_ADDRESS, &scp1_val);
|
||||
if (ret < 0) {
|
||||
dev_err(&swr->dev, "%s: write reg scp1_address failed, err %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
swr->g_scp1_val = scp1_val;
|
||||
}
|
||||
|
||||
if (scp2_val != swr->g_scp2_val) {
|
||||
ret = swr_write(swr, swr->dev_num, SCP2_ADDRESS, &scp2_val);
|
||||
if (ret < 0) {
|
||||
dev_err(&swr->dev, "%s: write reg scp2_address failed, err %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
swr->g_scp2_val = scp2_val;
|
||||
}
|
||||
*reg_addr = (*(u16 *)reg | SDCA_READ_WRITE_BIT);
|
||||
dev_dbg(&swr->dev, "%s: reg: 0x%x, scp1_val: 0x%x, scp2_val: 0x%x, reg_addr: 0x%x\n",
|
||||
__func__, *(u32 *)reg, scp1_val, scp2_val, *reg_addr);
|
||||
} else {
|
||||
*reg_addr = *(u16 *)reg;
|
||||
}
|
||||
} else {
|
||||
*reg_addr = *(u16 *)reg;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int regmap_swr_gather_write(void *context,
|
||||
const void *reg, size_t reg_size,
|
||||
const void *val, size_t val_len)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct swr_device *swr = to_swr_device(dev);
|
||||
struct regmap *map = dev_get_regmap(dev, NULL);
|
||||
int i, ret = 0;
|
||||
u16 reg_addr = 0;
|
||||
u8 *value;
|
||||
|
||||
if (map == NULL) {
|
||||
dev_err_ratelimited(dev, "%s: regmap is NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (swr == NULL) {
|
||||
dev_err_ratelimited(dev, "%s: swr device is NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((reg_size != ADDR_BYTES) && (reg_size != ADDR_BYTES_4)) {
|
||||
dev_err_ratelimited(dev, "%s: reg size %zd bytes not supported\n",
|
||||
__func__, reg_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&swr_rw_lock);
|
||||
ret = regmap_swr_reg_address_get(swr, ®_addr, reg, reg_size);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&swr_rw_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* val_len = VAL_BYTES * val_count */
|
||||
for (i = 0; i < (val_len / VAL_BYTES); i++) {
|
||||
value = (u8 *)val + (VAL_BYTES * i);
|
||||
ret = swr_write(swr, swr->dev_num, (reg_addr + i), value);
|
||||
if (ret < 0) {
|
||||
dev_err_ratelimited(dev, "%s: write reg 0x%x failed, err %d\n",
|
||||
__func__, (reg_addr + i), ret);
|
||||
break;
|
||||
}
|
||||
dev_dbg(dev, "%s: dev_num: 0x%x, gather write reg: 0x%x, value: 0x%x\n",
|
||||
__func__, swr->dev_num, (reg_addr + i), *value);
|
||||
}
|
||||
mutex_unlock(&swr_rw_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int regmap_swr_raw_multi_reg_write(void *context, const void *data,
|
||||
size_t count)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct swr_device *swr = to_swr_device(dev);
|
||||
struct regmap *map = dev_get_regmap(dev, NULL);
|
||||
size_t num_regs;
|
||||
int i = 0;
|
||||
int ret = 0;
|
||||
u16 *reg;
|
||||
u8 *val;
|
||||
u8 *buf;
|
||||
|
||||
if (swr == NULL) {
|
||||
dev_err_ratelimited(dev, "%s: swr device is NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (map == NULL) {
|
||||
dev_err_ratelimited(dev, "%s: regmap is NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ADDR_BYTES + VAL_BYTES + PAD_BYTES == 0) {
|
||||
dev_err_ratelimited(dev, "%s: sum of addr, value and pad is 0\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
num_regs = count / (ADDR_BYTES + VAL_BYTES + PAD_BYTES);
|
||||
|
||||
reg = kcalloc(num_regs, sizeof(u16), GFP_KERNEL);
|
||||
if (!reg)
|
||||
return -ENOMEM;
|
||||
|
||||
val = kcalloc(num_regs, sizeof(u8), GFP_KERNEL);
|
||||
if (!val) {
|
||||
ret = -ENOMEM;
|
||||
goto mem_fail;
|
||||
}
|
||||
|
||||
buf = (u8 *)data;
|
||||
for (i = 0; i < num_regs; i++) {
|
||||
reg[i] = *(u16 *)buf;
|
||||
buf += (ADDR_BYTES + PAD_BYTES);
|
||||
val[i] = *buf;
|
||||
buf += VAL_BYTES;
|
||||
}
|
||||
ret = swr_bulk_write(swr, swr->dev_num, reg, val, num_regs);
|
||||
if (ret)
|
||||
dev_err_ratelimited(dev, "%s: multi reg write failed\n", __func__);
|
||||
|
||||
kfree(val);
|
||||
mem_fail:
|
||||
kfree(reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int regmap_swr_write(void *context, const void *data, size_t count)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct swr_device *swr = to_swr_device(dev);
|
||||
struct regmap *map = dev_get_regmap(dev, NULL);
|
||||
int addr_bytes = 0;
|
||||
|
||||
if (map == NULL) {
|
||||
dev_err_ratelimited(dev, "%s: regmap is NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (swr == NULL) {
|
||||
dev_err_ratelimited(dev, "%s: swr is NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
addr_bytes = (swr->paging_support ? ADDR_BYTES_4 : ADDR_BYTES);
|
||||
|
||||
WARN_ON(count < addr_bytes);
|
||||
|
||||
if (count > (addr_bytes + VAL_BYTES + PAD_BYTES))
|
||||
return regmap_swr_raw_multi_reg_write(context, data, count);
|
||||
else
|
||||
return regmap_swr_gather_write(context, data, addr_bytes,
|
||||
(data + addr_bytes),
|
||||
(count - addr_bytes));
|
||||
}
|
||||
|
||||
static int regmap_swr_read(void *context,
|
||||
const void *reg, size_t reg_size,
|
||||
void *val, size_t val_size)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct swr_device *swr = to_swr_device(dev);
|
||||
struct regmap *map = dev_get_regmap(dev, NULL);
|
||||
int ret = 0;
|
||||
u16 reg_addr = 0;
|
||||
|
||||
if (map == NULL) {
|
||||
dev_err_ratelimited(dev, "%s: regmap is NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (swr == NULL) {
|
||||
dev_err_ratelimited(dev, "%s: swr is NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((reg_size != ADDR_BYTES) && (reg_size != ADDR_BYTES_4)) {
|
||||
dev_err_ratelimited(dev, "%s: reg size %zd bytes not supported\n",
|
||||
__func__, reg_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&swr_rw_lock);
|
||||
ret = regmap_swr_reg_address_get(swr, ®_addr, reg, reg_size);
|
||||
if (ret < 0) {
|
||||
dev_err_ratelimited(dev,
|
||||
"%s: regmap_swr_reg_address_get failed, reg: 0x%x\n",
|
||||
__func__, *(u32 *)reg);
|
||||
mutex_unlock(&swr_rw_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = swr_read(swr, swr->dev_num, reg_addr, val, val_size);
|
||||
if (ret < 0)
|
||||
dev_err_ratelimited(dev, "%s: codec reg 0x%x read failed %d\n",
|
||||
__func__, reg_addr, ret);
|
||||
|
||||
mutex_unlock(&swr_rw_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct regmap_bus regmap_swr = {
|
||||
.write = regmap_swr_write,
|
||||
.gather_write = regmap_swr_gather_write,
|
||||
.read = regmap_swr_read,
|
||||
.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
|
||||
.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
|
||||
};
|
||||
|
||||
struct regmap *__regmap_init_swr(struct swr_device *swr,
|
||||
const struct regmap_config *config,
|
||||
struct lock_class_key *lock_key,
|
||||
const char *lock_name)
|
||||
{
|
||||
return __regmap_init(&swr->dev, ®map_swr, &swr->dev, config,
|
||||
lock_key, lock_name);
|
||||
}
|
||||
EXPORT_SYMBOL(__regmap_init_swr);
|
||||
|
||||
struct regmap *__devm_regmap_init_swr(struct swr_device *swr,
|
||||
const struct regmap_config *config,
|
||||
struct lock_class_key *lock_key,
|
||||
const char *lock_name)
|
||||
{
|
||||
return __devm_regmap_init(&swr->dev, ®map_swr, &swr->dev, config,
|
||||
lock_key, lock_name);
|
||||
}
|
||||
EXPORT_SYMBOL(__devm_regmap_init_swr);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
504
qcom/opensource/audio-kernel/soc/snd_event.c
Normal file
504
qcom/opensource/audio-kernel/soc/snd_event.c
Normal file
@@ -0,0 +1,504 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2018, 2020 The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <soc/snd_event.h>
|
||||
|
||||
struct snd_event_client {
|
||||
struct list_head node;
|
||||
|
||||
struct device *dev;
|
||||
const struct snd_event_ops *ops;
|
||||
void *data;
|
||||
|
||||
bool attached;
|
||||
bool state;
|
||||
};
|
||||
|
||||
struct snd_event_client_array {
|
||||
struct device *dev;
|
||||
struct snd_event_client *clnt;
|
||||
void *data;
|
||||
int (*compare)(struct device *, void *);
|
||||
};
|
||||
|
||||
struct snd_event_clients {
|
||||
size_t num_clients;
|
||||
struct snd_event_client_array *cl_arr;
|
||||
};
|
||||
|
||||
struct snd_master {
|
||||
struct device *dev;
|
||||
const struct snd_event_ops *ops;
|
||||
void *data;
|
||||
|
||||
bool state;
|
||||
bool fwk_state;
|
||||
bool clients_found;
|
||||
struct snd_event_clients *clients;
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(snd_event_mutex);
|
||||
static LIST_HEAD(snd_event_client_list);
|
||||
static struct snd_master *master;
|
||||
|
||||
static struct snd_event_client *find_snd_event_client(struct device *dev)
|
||||
{
|
||||
struct snd_event_client *c;
|
||||
|
||||
list_for_each_entry(c, &snd_event_client_list, node)
|
||||
if ((c->dev == dev) && c->ops)
|
||||
return c;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int check_and_update_fwk_state(void)
|
||||
{
|
||||
bool new_fwk_state = true;
|
||||
struct snd_event_client *c;
|
||||
int ret = 0;
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < master->clients->num_clients; i++) {
|
||||
c = master->clients->cl_arr[i].clnt;
|
||||
new_fwk_state &= c->state;
|
||||
}
|
||||
new_fwk_state &= master->state;
|
||||
|
||||
if (master->fwk_state ^ new_fwk_state) {
|
||||
if (new_fwk_state) {
|
||||
for (i = 0; i < master->clients->num_clients; i++) {
|
||||
c = master->clients->cl_arr[i].clnt;
|
||||
if (c->ops->enable) {
|
||||
ret = c->ops->enable(c->dev, c->data);
|
||||
if (ret) {
|
||||
dev_err_ratelimited(c->dev,
|
||||
"%s: enable failed\n",
|
||||
__func__);
|
||||
goto dev_en_failed;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (master->ops->enable) {
|
||||
ret = master->ops->enable(master->dev,
|
||||
master->data);
|
||||
if (ret) {
|
||||
dev_err_ratelimited(master->dev,
|
||||
"%s: enable failed\n",
|
||||
__func__);
|
||||
goto mstr_en_failed;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (master->ops->disable)
|
||||
master->ops->disable(master->dev,
|
||||
master->data);
|
||||
for (i = 0; i < master->clients->num_clients; i++) {
|
||||
c = master->clients->cl_arr[i].clnt;
|
||||
if (c->ops->disable)
|
||||
c->ops->disable(c->dev, c->data);
|
||||
}
|
||||
}
|
||||
master->fwk_state = new_fwk_state;
|
||||
}
|
||||
goto exit;
|
||||
|
||||
mstr_en_failed:
|
||||
i = master->clients->num_clients;
|
||||
dev_en_failed:
|
||||
for (; i > 0; i--) {
|
||||
c = master->clients->cl_arr[i - 1].clnt;
|
||||
if (c->ops->disable)
|
||||
c->ops->disable(c->dev, c->data);
|
||||
}
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int snd_event_find_clients(struct snd_master *master)
|
||||
{
|
||||
struct snd_event_clients *clients = master->clients;
|
||||
int i = 0;
|
||||
int ret = 0;
|
||||
|
||||
for (i = 0; i < clients->num_clients; i++) {
|
||||
struct snd_event_client_array *c_arr = &clients->cl_arr[i];
|
||||
struct snd_event_client *c;
|
||||
|
||||
if (c_arr->dev) {
|
||||
pr_err_ratelimited("%s: client already present dev=%pK\n",
|
||||
__func__, c_arr->dev);
|
||||
continue;
|
||||
}
|
||||
|
||||
list_for_each_entry(c, &snd_event_client_list, node) {
|
||||
if (c->attached)
|
||||
continue;
|
||||
|
||||
if (c_arr->compare(c->dev, c_arr->data)) {
|
||||
dev_dbg(master->dev,
|
||||
"%s: found client, dev=%pK\n",
|
||||
__func__, c->dev);
|
||||
c_arr->dev = c->dev;
|
||||
c_arr->clnt = c;
|
||||
c->attached = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!c_arr->dev) {
|
||||
dev_dbg(master->dev,
|
||||
"%s: failed to find some client\n",
|
||||
__func__);
|
||||
ret = -ENXIO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* snd_event_client_register - Register a client with the SND event FW
|
||||
*
|
||||
* @dev: Pointer to the "struct device" associated with the client
|
||||
* @snd_ev_ops: Pointer to the snd_event_ops struct for the client containing
|
||||
* callback functions
|
||||
* @data: Pointer to any additional data that the caller wants to get back
|
||||
* with callback functions
|
||||
*
|
||||
* Returns 0 on success or error on failure.
|
||||
*/
|
||||
int snd_event_client_register(struct device *dev,
|
||||
const struct snd_event_ops *snd_ev_ops,
|
||||
void *data)
|
||||
{
|
||||
struct snd_event_client *c;
|
||||
|
||||
if (!dev) {
|
||||
pr_err_ratelimited("%s: dev is NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
c = kzalloc(sizeof(*c), GFP_KERNEL);
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
|
||||
c->dev = dev;
|
||||
c->ops = snd_ev_ops;
|
||||
c->data = data;
|
||||
|
||||
dev_dbg(dev, "%s: adding client to SND event FW (ops %pK)\n",
|
||||
__func__, snd_ev_ops);
|
||||
|
||||
mutex_lock(&snd_event_mutex);
|
||||
list_add_tail(&c->node, &snd_event_client_list);
|
||||
|
||||
if (master && !master->clients_found) {
|
||||
if (snd_event_find_clients(master)) {
|
||||
dev_dbg(dev, "%s: Failed to find all clients\n",
|
||||
__func__);
|
||||
goto exit;
|
||||
}
|
||||
master->clients_found = true;
|
||||
}
|
||||
|
||||
exit:
|
||||
mutex_unlock(&snd_event_mutex);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_event_client_register);
|
||||
|
||||
/*
|
||||
* snd_event_client_deregister - Remove a client from the SND event FW
|
||||
*
|
||||
* @dev: Pointer to the "struct device" associated with the client
|
||||
*
|
||||
* Returns 0 on success or error on failure.
|
||||
*/
|
||||
int snd_event_client_deregister(struct device *dev)
|
||||
{
|
||||
struct snd_event_client *c;
|
||||
int ret = 0;
|
||||
int i = 0;
|
||||
|
||||
if (!dev) {
|
||||
pr_err_ratelimited("%s: dev is NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "%s: removing client to SND event FW \n",
|
||||
__func__);
|
||||
|
||||
mutex_lock(&snd_event_mutex);
|
||||
if (list_empty(&snd_event_client_list)) {
|
||||
dev_dbg(dev, "%s: No SND client registered\n", __func__);
|
||||
ret = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
c = find_snd_event_client(dev);
|
||||
if (!c || (c->dev != dev)) {
|
||||
dev_dbg(dev, "%s: No matching snd dev found\n", __func__);
|
||||
ret = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
c->state = false;
|
||||
|
||||
if (master) {
|
||||
struct snd_event_client *d;
|
||||
bool dev_found = false;
|
||||
|
||||
for (i = 0; i < master->clients->num_clients; i++) {
|
||||
d = master->clients->cl_arr[i].clnt;
|
||||
if (c->dev == d->dev) {
|
||||
dev_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dev_found ) {
|
||||
if(master->clients_found) {
|
||||
ret = check_and_update_fwk_state();
|
||||
master->clients_found = false;
|
||||
}
|
||||
master->clients->cl_arr[i].dev = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
list_del(&c->node);
|
||||
kfree(c);
|
||||
exit:
|
||||
mutex_unlock(&snd_event_mutex);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_event_client_deregister);
|
||||
|
||||
/*
|
||||
* snd_event_mstr_add_client - Add a client to the master's list of clients
|
||||
*
|
||||
* @snd_clients: list of clients associated with this master
|
||||
* @compare: Pointer to the compare callback function that master will use to
|
||||
* confirm the clients
|
||||
* @data: Address to any additional data that the master wants to get back with
|
||||
* compare callback functions
|
||||
*/
|
||||
void snd_event_mstr_add_client(struct snd_event_clients **snd_clients,
|
||||
int (*compare)(struct device *, void *),
|
||||
void *data)
|
||||
{
|
||||
struct snd_event_clients *client = *snd_clients;
|
||||
|
||||
if (IS_ERR(client)) {
|
||||
pr_err_ratelimited("%s: snd_clients is invalid\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!client) {
|
||||
client = kzalloc(sizeof(*client), GFP_KERNEL);
|
||||
if (!client) {
|
||||
*snd_clients = ERR_PTR(-ENOMEM);
|
||||
return;
|
||||
}
|
||||
client->cl_arr = kzalloc(sizeof(struct snd_event_client_array),
|
||||
GFP_KERNEL);
|
||||
if (!client->cl_arr) {
|
||||
*snd_clients = ERR_PTR(-ENOMEM);
|
||||
return;
|
||||
}
|
||||
*snd_clients = client;
|
||||
} else {
|
||||
struct snd_event_client_array *new;
|
||||
|
||||
new = krealloc(client->cl_arr,
|
||||
(client->num_clients + 1) * sizeof(*new),
|
||||
GFP_KERNEL | __GFP_ZERO);
|
||||
if (!new) {
|
||||
*snd_clients = ERR_PTR(-ENOMEM);
|
||||
return;
|
||||
}
|
||||
client->cl_arr = new;
|
||||
}
|
||||
|
||||
client->cl_arr[client->num_clients].dev = NULL;
|
||||
client->cl_arr[client->num_clients].data = data;
|
||||
client->cl_arr[client->num_clients].compare = compare;
|
||||
client->num_clients++;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_event_mstr_add_client);
|
||||
|
||||
/*
|
||||
* snd_event_master_register - Register a master with the SND event FW
|
||||
*
|
||||
* @dev: Pointer to the "struct device" associated with the master
|
||||
* @ops: Pointer to the snd_event_ops struct for the master containing
|
||||
* callback functions
|
||||
* @clients: List of clients for the master
|
||||
* @data: Pointer to any additional data that the caller wants to get back
|
||||
* with callback functions
|
||||
*
|
||||
* Returns 0 on success or error on failure.
|
||||
*
|
||||
* Prerequisite:
|
||||
* clients list must not be empty.
|
||||
* All clients for the master must have to be registered by calling
|
||||
* snd_event_mstr_add_client() before calling this API to register a
|
||||
* master with SND event fwk.
|
||||
*/
|
||||
int snd_event_master_register(struct device *dev,
|
||||
const struct snd_event_ops *ops,
|
||||
struct snd_event_clients *clients,
|
||||
void *data)
|
||||
{
|
||||
struct snd_master *new_master;
|
||||
int ret = 0;
|
||||
|
||||
if (!dev) {
|
||||
pr_err_ratelimited("%s: dev is NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&snd_event_mutex);
|
||||
if (master) {
|
||||
dev_err(dev, "%s: master already allocated with %pK\n",
|
||||
__func__, master->dev);
|
||||
ret = -EALREADY;
|
||||
goto exit;
|
||||
}
|
||||
mutex_unlock(&snd_event_mutex);
|
||||
|
||||
if (!clients || IS_ERR(clients)) {
|
||||
dev_err(dev, "%s: Invalid clients ptr\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
new_master = kzalloc(sizeof(*new_master), GFP_KERNEL);
|
||||
if (!new_master)
|
||||
return -ENOMEM;
|
||||
|
||||
new_master->dev = dev;
|
||||
new_master->ops = ops;
|
||||
new_master->data = data;
|
||||
new_master->clients = clients;
|
||||
|
||||
dev_dbg(dev, "adding master to SND event FW (ops %pK)\n", ops);
|
||||
|
||||
mutex_lock(&snd_event_mutex);
|
||||
|
||||
master = new_master;
|
||||
|
||||
ret = snd_event_find_clients(master);
|
||||
if (ret) {
|
||||
dev_dbg(dev, "%s: Failed to find all clients\n", __func__);
|
||||
ret = 0;
|
||||
goto exit;
|
||||
}
|
||||
master->clients_found = true;
|
||||
|
||||
exit:
|
||||
mutex_unlock(&snd_event_mutex);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_event_master_register);
|
||||
|
||||
/*
|
||||
* snd_event_master_deregister - Remove a master from the SND event FW
|
||||
*
|
||||
* @dev: Pointer to the "struct device" associated with the master
|
||||
*
|
||||
* Returns 0 on success or error on failure.
|
||||
*/
|
||||
int snd_event_master_deregister(struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!dev) {
|
||||
pr_err_ratelimited("%s: dev is NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&snd_event_mutex);
|
||||
if (!master) {
|
||||
dev_dbg(dev, "%s: No master found\n", __func__);
|
||||
ret = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (master->dev != dev) {
|
||||
dev_dbg(dev, "%s: device is not a Master\n", __func__);
|
||||
ret = -ENXIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
master->state = false;
|
||||
|
||||
if (master && master->clients_found)
|
||||
ret = check_and_update_fwk_state();
|
||||
|
||||
kfree(master->clients->cl_arr);
|
||||
kfree(master->clients);
|
||||
kfree(master);
|
||||
master = NULL;
|
||||
exit:
|
||||
mutex_unlock(&snd_event_mutex);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_event_master_deregister);
|
||||
|
||||
/*
|
||||
* snd_event_notify - Update the state of the Master/client in the SND event FW
|
||||
*
|
||||
* @dev: Pointer to the "struct device" associated with the master/client
|
||||
* @state: UP/DOWN state of the caller (master/client)
|
||||
*
|
||||
* Returns 0 on success or error on failure.
|
||||
*/
|
||||
int snd_event_notify(struct device *dev, unsigned int state)
|
||||
{
|
||||
struct snd_event_client *c;
|
||||
int ret = 0;
|
||||
|
||||
if (!dev) {
|
||||
pr_err_ratelimited("%s: dev is NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "%s: snd_event_notify (state %u)\n",
|
||||
__func__, state);
|
||||
|
||||
mutex_lock(&snd_event_mutex);
|
||||
if (list_empty(&snd_event_client_list) && !master) {
|
||||
dev_err_ratelimited(dev, "%s: No device registered\n", __func__);
|
||||
ret = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
c = find_snd_event_client(dev);
|
||||
if (!c && (!master || (master->dev != dev))) {
|
||||
dev_err_ratelimited(dev, "%s: No snd dev entry found\n", __func__);
|
||||
ret = -ENXIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (c)
|
||||
c->state = !!state;
|
||||
else
|
||||
master->state = !!state;
|
||||
|
||||
if (master && master->clients_found)
|
||||
ret = check_and_update_fwk_state();
|
||||
|
||||
exit:
|
||||
mutex_unlock(&snd_event_mutex);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_event_notify);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("SND event module");
|
||||
1141
qcom/opensource/audio-kernel/soc/soundwire.c
Normal file
1141
qcom/opensource/audio-kernel/soc/soundwire.c
Normal file
File diff suppressed because it is too large
Load Diff
4601
qcom/opensource/audio-kernel/soc/swr-mstr-ctrl.c
Normal file
4601
qcom/opensource/audio-kernel/soc/swr-mstr-ctrl.c
Normal file
File diff suppressed because it is too large
Load Diff
211
qcom/opensource/audio-kernel/soc/swr-mstr-ctrl.h
Normal file
211
qcom/opensource/audio-kernel/soc/swr-mstr-ctrl.h
Normal file
@@ -0,0 +1,211 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2023-2024, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _SWR_WCD_CTRL_H
|
||||
#define _SWR_WCD_CTRL_H
|
||||
#include <linux/module.h>
|
||||
#include <soc/swr-wcd.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/pm.h>
|
||||
#include <soc/swr-common.h>
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#define SWR_MSTR_MAX_REG_ADDR 0x60A8
|
||||
#define SWR_MSTR_START_REG_ADDR 0x00
|
||||
#define SWR_MSTR_MAX_BUF_LEN 32
|
||||
#define BYTES_PER_LINE 12
|
||||
#define SWR_MSTR_RD_BUF_LEN 8
|
||||
#define SWR_MSTR_WR_BUF_LEN 32
|
||||
#endif
|
||||
|
||||
#define SWR_ROW_48 0
|
||||
#define SWR_ROW_50 1
|
||||
#define SWR_ROW_64 3
|
||||
#define SWR_COL_04 1 /* Cols = 4 */
|
||||
#define SWR_MAX_COL 7 /* Cols = 16 */
|
||||
#define SWR_MIN_COL 0 /* Cols = 2 */
|
||||
|
||||
#define SWR_NAME "swr-mgr"
|
||||
|
||||
#define SWR_MSTR_PORT_LEN 13 /* Number of master ports */
|
||||
|
||||
#define SWRM_VERSION_1_0 0x01010000
|
||||
#define SWRM_VERSION_1_2 0x01030000
|
||||
#define SWRM_VERSION_1_3 0x01040000
|
||||
#define SWRM_VERSION_1_5 0x01050000
|
||||
#define SWRM_VERSION_1_5_1 0x01050001
|
||||
#define SWRM_VERSION_1_6 0x01060000
|
||||
#define SWRM_VERSION_1_7 0x01070000
|
||||
#define SWRM_VERSION_2_0 0x02000000
|
||||
|
||||
#define SWR_MAX_CH_PER_PORT 8
|
||||
|
||||
#define SWRM_NUM_AUTO_ENUM_SLAVES 11
|
||||
|
||||
enum {
|
||||
SWR_MSTR_PAUSE,
|
||||
SWR_MSTR_RESUME,
|
||||
SWR_MSTR_UP,
|
||||
SWR_MSTR_DOWN,
|
||||
SWR_MSTR_SSR,
|
||||
SWR_MSTR_SSR_RESET,
|
||||
};
|
||||
|
||||
enum swrm_pm_state {
|
||||
SWRM_PM_SLEEPABLE,
|
||||
SWRM_PM_AWAKE,
|
||||
SWRM_PM_ASLEEP,
|
||||
};
|
||||
|
||||
enum {
|
||||
SWR_IRQ_FREE,
|
||||
SWR_IRQ_REGISTER,
|
||||
};
|
||||
|
||||
enum {
|
||||
SWR_PDM = 0,
|
||||
SWR_PCM,
|
||||
SWR_PDM_32,
|
||||
};
|
||||
|
||||
struct usecase {
|
||||
u8 num_port;
|
||||
u8 num_ch;
|
||||
u32 chrate;
|
||||
};
|
||||
|
||||
struct swrm_mports {
|
||||
struct list_head port_req_list;
|
||||
bool port_en;
|
||||
u8 ch_en;
|
||||
u8 req_ch;
|
||||
u8 offset1;
|
||||
u8 offset2;
|
||||
u16 sinterval;
|
||||
u8 hstart;
|
||||
u8 hstop;
|
||||
u8 blk_grp_count;
|
||||
u8 blk_pack_mode;
|
||||
u8 word_length;
|
||||
u8 lane_ctrl;
|
||||
u8 dir;
|
||||
u8 stream_type;
|
||||
u32 ch_rate;
|
||||
};
|
||||
|
||||
struct swrm_port_type {
|
||||
u8 port_type;
|
||||
u8 ch_mask;
|
||||
};
|
||||
|
||||
struct swr_ctrl_platform_data {
|
||||
void *handle; /* holds priv data */
|
||||
int (*read)(void *handle, int reg);
|
||||
int (*write)(void *handle, int reg, int val);
|
||||
int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len);
|
||||
int (*clk)(void *handle, bool enable);
|
||||
int (*core_vote)(void *handle, bool enable);
|
||||
int (*reg_irq)(void *handle, irqreturn_t(*irq_handler)(int irq,
|
||||
void *data), void *swr_handle, int type);
|
||||
};
|
||||
|
||||
struct swr_mstr_ctrl {
|
||||
struct swr_master master;
|
||||
struct device *dev;
|
||||
struct resource *supplies;
|
||||
struct clk *mclk;
|
||||
int clk_ref_count;
|
||||
struct completion clk_off_complete;
|
||||
struct completion reset;
|
||||
struct completion broadcast;
|
||||
struct mutex clklock;
|
||||
struct mutex iolock;
|
||||
struct mutex devlock;
|
||||
struct mutex mlock;
|
||||
struct mutex reslock;
|
||||
struct mutex pm_lock;
|
||||
struct mutex irq_lock;
|
||||
struct mutex runtime_lock;
|
||||
u32 swrm_base_reg;
|
||||
char __iomem *swrm_dig_base;
|
||||
char __iomem *swrm_hctl_reg;
|
||||
u8 rcmd_id;
|
||||
u8 wcmd_id;
|
||||
u8 cmd_id;
|
||||
u32 master_id;
|
||||
u32 ee_val;
|
||||
u32 dynamic_port_map_supported;
|
||||
void *handle; /* SWR Master handle from client for read and writes */
|
||||
int (*read)(void *handle, int reg);
|
||||
int (*write)(void *handle, int reg, int val);
|
||||
int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len);
|
||||
int (*clk)(void *handle, bool enable);
|
||||
int (*core_vote)(void *handle, bool enable);
|
||||
int (*reg_irq)(void *handle, irqreturn_t(*irq_handler)(int irq,
|
||||
void *data), void *swr_handle, int type);
|
||||
int irq;
|
||||
int wake_irq;
|
||||
int version;
|
||||
int mclk_freq;
|
||||
int bus_clk;
|
||||
u32 num_dev;
|
||||
int slave_status;
|
||||
struct swrm_mports mport_cfg[SWR_MAX_MSTR_PORT_NUM];
|
||||
struct list_head port_req_list;
|
||||
unsigned long port_req_pending;
|
||||
int state;
|
||||
struct platform_device *pdev;
|
||||
int num_rx_chs;
|
||||
u8 num_cfg_devs;
|
||||
struct mutex force_down_lock;
|
||||
int force_down_state;
|
||||
struct notifier_block event_notifier;
|
||||
struct work_struct dc_presence_work;
|
||||
u8 num_ports;
|
||||
struct swrm_port_type
|
||||
port_mapping[SWR_MSTR_PORT_LEN + 1][SWR_MAX_CH_PER_PORT];
|
||||
int swr_irq;
|
||||
u32 clk_stop_mode0_supp;
|
||||
struct work_struct wakeup_work;
|
||||
u32 ipc_wakeup;
|
||||
bool dev_up;
|
||||
bool ipc_wakeup_triggered;
|
||||
bool req_clk_switch;
|
||||
struct pm_qos_request pm_qos_req;
|
||||
enum swrm_pm_state pm_state;
|
||||
wait_queue_head_t pm_wq;
|
||||
int wlock_holders;
|
||||
u32 intr_mask;
|
||||
struct port_params **port_param;
|
||||
struct clk *lpass_core_hw_vote;
|
||||
struct clk *lpass_core_audio;
|
||||
u8 num_usecase;
|
||||
u32 swr_irq_wakeup_capable;
|
||||
int hw_core_clk_en;
|
||||
int aud_core_clk_en;
|
||||
int clk_src;
|
||||
u32 disable_div2_clk_switch;
|
||||
u32 rd_fifo_depth;
|
||||
u32 wr_fifo_depth;
|
||||
u32 num_auto_enum;
|
||||
bool enable_slave_irq;
|
||||
u32 is_always_on;
|
||||
bool clk_stop_wakeup;
|
||||
struct swr_port_params pp[SWR_UC_MAX][SWR_MAX_MSTR_PORT_NUM];/*max_devNum * max_ports 11 * 14 */
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *debugfs_swrm_dent;
|
||||
struct dentry *debugfs_peek;
|
||||
struct dentry *debugfs_poke;
|
||||
struct dentry *debugfs_reg_dump;
|
||||
unsigned int read_data;
|
||||
#endif
|
||||
struct proc_dir_entry *swr_mstr_ctrl_proc_entry;
|
||||
};
|
||||
|
||||
#endif /* _SWR_WCD_CTRL_H */
|
||||
185
qcom/opensource/audio-kernel/soc/swr-mstr-registers.h
Normal file
185
qcom/opensource/audio-kernel/soc/swr-mstr-registers.h
Normal file
@@ -0,0 +1,185 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2015, 2018-2021 The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _SWRM_REGISTERS_H
|
||||
#define _SWRM_REGISTERS_H
|
||||
|
||||
#define SWRM_BASE 0x00
|
||||
#define SWRM_COMP_HW_VERSION (SWRM_BASE+0x0000)
|
||||
#define SWRM_COMP_CFG (SWRM_BASE+0x0004)
|
||||
#define SWRM_COMP_SW_RESET (SWRM_BASE+0x0008)
|
||||
#define SWRM_COMP_POWER_CFG (SWRM_BASE+0x000C)
|
||||
#define SWRM_COMP_FEATURE_CFG (SWRM_BASE+0x0010)
|
||||
#define SWRM_COMP_STATUS (SWRM_BASE+0x0014)
|
||||
#define SWRM_LINK_MANAGER_EE (SWRM_BASE+0x0018)
|
||||
#define SWRM_COMP_PARAMS (SWRM_BASE+0x0100)
|
||||
#define SWRM_COMP_MASTER_ID (SWRM_BASE+0x0104)
|
||||
#define MM_SYNC_CONFIG (SWRM_BASE+0x0108)
|
||||
#define SWRM_COMP_NPL_PARAMS (SWRM_BASE+0x0120)
|
||||
|
||||
#ifdef CONFIG_SWRM_VER_2P0
|
||||
#define SWRM_INTERRUPT_STATUS(n) (SWRM_BASE+0x4000+0x1000*n)
|
||||
#define SWRM_INTERRUPT_EN(n) (SWRM_BASE+0x4004+0x1000*n)
|
||||
#define SWRM_INTERRUPT_CLEAR(n) (SWRM_BASE+0x4008+0x1000*n)
|
||||
#define SWRM_CMD_FIFO_WR_CMD(n) (SWRM_BASE+0x4020+0x1000*n)
|
||||
#define SWRM_CMD_FIFO_RD_CMD(n) (SWRM_BASE+0x4024+0x1000*n)
|
||||
#define SWRM_NUM_ENTRIES_CMD_FIFO(n) (SWRM_BASE+0x4028+0x1000*n)
|
||||
#define SWRM_CMD_FIFO_RD_FIFO(n) (SWRM_BASE+0x4040+0x1000*n)
|
||||
#define SWRM_NUM_ENTRIES_RD_FIFO(n) (SWRM_BASE+0x4044+0x1000*n)
|
||||
#define SWRM_CMD_FIFO_STATUS(n) (SWRM_BASE+0x4050+0x1000*n)
|
||||
#define SWRM_CMD_RESPONSE(n) (SWRM_BASE+0x4054+0x1000*n)
|
||||
#define SWRM_CLK_CTRL(n) (SWRM_BASE+0x4060+0x1000*n)
|
||||
#define SWRM_LINK_STATUS(n) (SWRM_BASE+0x4064+0x1000*n)
|
||||
#define SWRM_TO_CPU_SW_MESSAGE(n, m) (SWRM_BASE+0x4090+0x1000*n+0x4*m)
|
||||
#define SWRM_TO_CPU_SW_MESSAGE_READ(n, m) (SWRM_BASE+0x40A0+0x1000*n+0x4*m)
|
||||
#define SWRM_DOUT_DP_FIFO_WATERMARK_CTRL(n) (SWRM_BASE+0x1060+0x100*n)
|
||||
#define SWRM_INTERRUPT_STATUS_RD_FIFO_OVERFLOW 0x10
|
||||
#define SWRM_INTERRUPT_STATUS_RD_FIFO_UNDERFLOW 0x20
|
||||
#define SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED 0x400
|
||||
#define SWRM_INTERRUPT_STATUS_MASK 0x1DFDFD
|
||||
#define SWRM_INTERRUPT_MAX 0x17
|
||||
|
||||
#else
|
||||
#define SWRM_INTERRUPT_STATUS(n) (SWRM_BASE+0x0200*n)
|
||||
#define SWRM_INTERRUPT_CLEAR(n) (SWRM_BASE+0x0208*n)
|
||||
#define SWRM_INTERRUPT_STATUS_1(n) (SWRM_BASE+0x0220*n)
|
||||
#define SWRM_INTERRUPT_CLEAR_1(n) (SWRM_BASE+0x0228*n)
|
||||
#define SWRM_CPU1_INTERRUPT_EN(n) (SWRM_BASE+0x0210*n)
|
||||
#define SWRM_CPU1_INTERRUPT_EN_1(n) (SWRM_BASE+0x0230*n)
|
||||
#define SWRM_CPU0_CMD_RESPONSE(n) (SWRM_BASE+0x0250*n)
|
||||
|
||||
#define SWRM_CPU1_CMD_FIFO_WR_CMD(n) (SWRM_BASE+0x031C*n)
|
||||
#define SWRM_CPU1_CMD_FIFO_RD_CMD(n) (SWRM_BASE+0x0320*n)
|
||||
#define SWRM_CPU1_CMD_FIFO_STATUS(n) (SWRM_BASE+0x0328*n)
|
||||
#define SWRM_CPU1_CMD_FIFO_RD_FIFO(n) (SWRM_BASE+0x0334*n)
|
||||
|
||||
#ifdef CONFIG_SWRM_VER_1P7
|
||||
#define SWRM_INTERRUPT_EN SWRM_CPU1_INTERRUPT_EN
|
||||
#define SWRM_INTERRUPT_EN_1 SWRM_CPU1_INTERRUPT_EN_1
|
||||
#define SWRM_CMD_FIFO_WR_CMD SWRM_CPU1_CMD_FIFO_WR_CMD
|
||||
#define SWRM_CMD_FIFO_RD_CMD SWRM_CPU1_CMD_FIFO_RD_CMD
|
||||
#define SWRM_CMD_FIFO_STATUS SWRM_CPU1_CMD_FIFO_STATUS
|
||||
#define SWRM_CMD_FIFO_RD_FIFO SWRM_CPU1_CMD_FIFO_RD_FIFO
|
||||
#define SWRM_INTERRUPT_STATUS_MASK 0x1DFDFD
|
||||
#define SWRM_INTERRUPT_STATUS_RD_FIFO_OVERFLOW 0x40000
|
||||
#define SWRM_INTERRUPT_STATUS_RD_FIFO_UNDERFLOW 0x80000
|
||||
#define SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED 0x100000
|
||||
#define SWRM_INTERRUPT_MAX 0x20
|
||||
#else
|
||||
#define SWRM_INTERRUPT_EN(n) SWRM_CPU1_INTERRUPT_EN(n)
|
||||
#define SWRM_INTERRUPT_EN_1(n) (SWRM_BASE+0x0224*n)
|
||||
#define SWRM_CMD_FIFO_WR_CMD(n) (SWRM_BASE+0x0300*n)
|
||||
#define SWRM_CMD_FIFO_RD_CMD(n) (SWRM_BASE+0x0304*n)
|
||||
#define SWRM_CMD_FIFO_STATUS(n) (SWRM_BASE+0x030C*n)
|
||||
#define SWRM_CMD_FIFO_RD_FIFO(n) (SWRM_BASE+0x0318*n)
|
||||
#define SWRM_INTERRUPT_STATUS_MASK 0x1FDFD
|
||||
#define SWRM_INTERRUPT_STATUS_RD_FIFO_OVERFLOW 0x10
|
||||
#define SWRM_INTERRUPT_STATUS_RD_FIFO_UNDERFLOW 0x20
|
||||
#define SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED 0x400
|
||||
#define SWRM_INTERRUPT_MAX 0x11
|
||||
#endif /* CONFIG_SWRM_VER_1P7 */
|
||||
#endif /* CONFIG_SWRM_VER_2P0 */
|
||||
#define SWRM_CMD_FIFO_CMD (SWRM_BASE+0x0308)
|
||||
#define SWRM_CMD_FIFO_CFG (SWRM_BASE+0x0314)
|
||||
#define SWRM_CPU_NUM_ENTRIES_WR_CMD_FIFO (SWRM_BASE+0x0370)
|
||||
#define SWRM_CPU0_SW_INTERRUPT_SET (SWRM_BASE+0x0374)
|
||||
#define SWRM_CPU0_SW_MESSAGE0 (SWRM_BASE+0x0384)
|
||||
#define SWRM_CPU0_SW_MESSAGE1 (SWRM_BASE+0x0394)
|
||||
#define SWRM_ENUMERATOR_CFG (SWRM_BASE+0x0500)
|
||||
#define SWRM_ENUMERATOR_STATUS (SWRM_BASE+0x0504)
|
||||
#define SWRM_ENUMERATOR_PRE_ENUM_CFG (SWRM_BASE+0x0530)
|
||||
#define SWRM_ENUMERATOR_SLAVE_DEV_ID_1(m) (SWRM_BASE+0x0530+0x8*m)
|
||||
#define SWRM_ENUMERATOR_SLAVE_DEV_ID_2(m) (SWRM_BASE+0x0534+0x8*m)
|
||||
#define SWRM_CTRL_W_GEN_STATUS (SWRM_BASE+0x0600)
|
||||
#define SWRM_SW_RESET_STATUS (SWRM_BASE+0x0700)
|
||||
#define SWRM_FORCE_BANK_SWITCH_SUCCESS (SWRM_BASE+0x0704)
|
||||
#define SWRM_SILENCE_TONE_REPEAT_VALUE_THRESHOLD (SWRM_BASE+0x0710)
|
||||
#define SWRM_SELF_GENERATE_FRAME_SYNC (SWRM_BASE+0x0714)
|
||||
#define SWRM_MCP_FRAME_CTRL_BANK(m) (SWRM_BASE+0x101C+0x40*m)
|
||||
#define SWRM_MCP_BUS_CTRL (SWRM_BASE+0x1044)
|
||||
#define SWRM_MCP_CFG (SWRM_BASE+0x1048)
|
||||
#define SWRM_MCP_STATUS (SWRM_BASE+0x104C)
|
||||
#define SWRM_MCP_SLV_STATUS (SWRM_BASE+0x1090)
|
||||
|
||||
|
||||
#define SWRM_DP_PORT_CONTROL(n) (SWRM_BASE+0x1020+0x100*n)
|
||||
#define SWRM_DP_PORT_CTRL_BANK(n, m) (SWRM_BASE+0x1024+0x100*n+0x40*m)
|
||||
#define SWRM_DP_PORT_CTRL_2_BANK(n, m) (SWRM_BASE+0x1028+0x100*n+0x40*m)
|
||||
#define SWRM_DP_BLOCK_CTRL_1(n) (SWRM_BASE+0x102C+0x100*n)
|
||||
#define SWRM_DP_BLOCK_CTRL2_BANK(n, m) (SWRM_BASE+0x1030+0x100*n+0x40*m)
|
||||
#define SWRM_DP_PORT_HCTRL_BANK(n, m) (SWRM_BASE+0x1034+0x100*n+0x40*m)
|
||||
#define SWRM_DP_BLOCK_CTRL3_BANK(n, m) (SWRM_BASE+0x1038+0x100*n+0x40*m)
|
||||
#define SWRM_DP_SAMPLECTRL2_BANK(n, m) (SWRM_BASE+0x103C+0x100*n+0x40*m)
|
||||
|
||||
|
||||
#define SWRM_DOUT_DP_INT_STATUS(n) (SWRM_BASE+0x1000+0x100*n)
|
||||
#define SWRM_DOUT_DP_INT_CLEAR(n) (SWRM_BASE+0x1008+0x100*n)
|
||||
#define SWRM_DOUT_DP_FEATURES_EN(n) (SWRM_BASE+0x104C+0x100*n)
|
||||
#define SWRM_DOUT_DP_SILENCE_TONE_CFG(n) (SWRM_BASE+0x1050+0x100*n)
|
||||
#define SWRM_DOUT_DP_PCM_PORT_CTRL(n) (SWRM_BASE+0x1054+0x100*n)
|
||||
|
||||
#define SWRM_DIN_DP_INT_STATUS(n) (SWRM_BASE+0x1000+0x100*n)
|
||||
#define SWRM_DIN_DP_INT_CLEAR(n) (SWRM_BASE+0x1008+0x100*n)
|
||||
#define SWRM_DIN_DP_FEATURES_EN(n) (SWRM_BASE+0x104C+0x100*n)
|
||||
#define SWRM_DIN_DP_PCM_PORT_CTRL(n) (SWRM_BASE+0x1054+0x100*n)
|
||||
|
||||
#define SWRM_DP_FLOW_CTRL_M_VALID_SAMPLE(n) (SWRM_BASE+0x1080+0x100*n)
|
||||
#define SWRM_DP_FLOW_CTRL_N_REPEAT_PERIOD(n) (SWRM_BASE+0x1084+0x100*n)
|
||||
|
||||
#define SWRM_DP_PORT_CONTROL__FLOW_MODE_PUSH (0x8)
|
||||
#define SWRM_DP_PORT_CONTROL__FLOW_MODE_PULL (0x10)
|
||||
#define SWRM_DOUT_DP_PCM_PORT_CTRL__SELF_GEN_SUB_RATE_EN (0x4)
|
||||
|
||||
#ifdef CONFIG_SWRM_VER_2P0
|
||||
#define SWRM_MAX_REGISTER SWRM_TO_CPU_SW_MESSAGE_READ(2, 2)
|
||||
#else
|
||||
#define SWRM_MAX_REGISTER SWRM_DIN_DP_PCM_PORT_CTRL(9)
|
||||
#endif /* CONFIG_SWRM_VER_2P0 */
|
||||
|
||||
|
||||
#define SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ 0x1
|
||||
#define SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED 0x2
|
||||
#define SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS 0x4
|
||||
#define SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET 0x8
|
||||
#define SWRM_INTERRUPT_STATUS_WR_CMD_FIFO_OVERFLOW 0x40
|
||||
#define SWRM_INTERRUPT_STATUS_CMD_ERROR 0x80
|
||||
#define SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION 0x100
|
||||
#define SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH 0x200
|
||||
|
||||
#ifdef CONFIG_SWRM_VER_1P1
|
||||
#define SWRM_INTERRUPT_STATUS_NEW_SLAVE_AUTO_ENUM_FINISHED 0x800
|
||||
#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_FAILED 0x1000
|
||||
#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_TABLE_IS_FULL 0x2000
|
||||
#define SWRM_INTERRUPT_STATUS_BUS_RESET_FINISHED 0x4000
|
||||
#define SWRM_INTERRUPT_STATUS_CLK_STOP_FINISHED 0x8000
|
||||
#define SWRM_INTERRUPT_STATUS_ERROR_PORT_TEST 0x10000
|
||||
#else
|
||||
#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_FAILED 0x800
|
||||
#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_TABLE_IS_FULL 0x1000
|
||||
#define SWRM_INTERRUPT_STATUS_BUS_RESET_FINISHED 0x2000
|
||||
#define SWRM_INTERRUPT_STATUS_CLK_STOP_FINISHED 0x4000
|
||||
#define SWRM_INTERRUPT_STATUS_ERROR_PORT_TEST 0x8000
|
||||
#endif /* CONFIG_SWRM_VER_1P1 */
|
||||
|
||||
#define SWRM_INTERRUPT_STATUS_EXT_CLK_STOP_WAKEUP 0x10000
|
||||
#define SWRM_INTERRUPT_STATUS_DOUT_RATE_MISMATCH 0x20000
|
||||
#define SWRM_INTERRUPT_STATUS_BUS_REL_REQUEST_NON_MONITOR_MODE 0x40000
|
||||
#define SWRM_INTERRUPT_STATUS_CMD_IGNORED_AND_EXEC_CONTINUED 0x80000
|
||||
#define SWRM_INTERRUPT_STATUS_SW_INTERRUPT_FROM_CPU0 0x100000
|
||||
#define SWRM_INTERRUPT_STATUS_SW_INTERRUPT_FROM_CPU1 0x200000
|
||||
#define SWRM_INTERRUPT_STATUS_SW_INTERRUPT_FROM_CPU2 0x400000
|
||||
#define SWRM_INTERRUPT_STATUS_SW_INTERRUPT_FROM_CPU3 0x800000
|
||||
|
||||
|
||||
#define SWRM_COMP_PARAMS_WR_FIFO_DEPTH 0x00007C00
|
||||
#define SWRM_COMP_PARAMS_RD_FIFO_DEPTH 0x000F8000
|
||||
|
||||
#define SWRM_COMP_FEATURE_CFG_DEFAULT_VAL 0x06
|
||||
#define SWRM_COMP_FEATURE_CFG_DEFAULT_VAL_V1P7 0x406
|
||||
#define SWRM_COMP_FEATURE_CFG_DEFAULT_VAL_V2P0 0x40E
|
||||
#define SWRM_COMP_FEATURE_CFG_PCM_EN_MASK 0x18
|
||||
#define SWRM_COMP_PARAMS_AUTO_ENUM_SLAVES 0x00F00000
|
||||
#endif /* _SWRM_REGISTERS_H */
|
||||
78
qcom/opensource/audio-kernel/soc/swr-slave-registers.h
Normal file
78
qcom/opensource/audio-kernel/soc/swr-slave-registers.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2015, 2018-2020 The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _SWR_SLAVE_REGISTERS_H
|
||||
#define _SWR_SLAVE_REGISTERS_H
|
||||
|
||||
#define SWRS_BASE 0x00
|
||||
|
||||
#define SWRS_SCP_INT_STATUS_1 (SWRS_BASE+0x0040)
|
||||
#define SWRS_SCP_INT_CLEAR_1 (SWRS_BASE+0x0040)
|
||||
#define SWRS_SCP_INT_MASK_1 (SWRS_BASE+0x0041)
|
||||
#define SWRS_SCP_INT_STATUS_2 (SWRS_BASE+0x0042)
|
||||
#define SWRS_SCP_INT_STATUS_3 (SWRS_BASE+0x0043)
|
||||
#define SWRS_SCP_CONTROL (SWRS_BASE+0x0044)
|
||||
#define SWRS_SCP_STATUS (SWRS_BASE+0x0044)
|
||||
#define SWRS_SCP_CLOCK_STOP_CONTROL (SWRS_BASE+0x0045)
|
||||
#define SWRS_SCP_DEV_NUMBER (SWRS_BASE+0x0046)
|
||||
#define SWRS_SCP_KEEPER_EN (SWRS_BASE+0x004A)
|
||||
#define SWRS_SCP_DEVICE_ID_0 (SWRS_BASE+0x0050)
|
||||
#define SWRS_SCP_DEVICE_ID_1 (SWRS_BASE+0x0051)
|
||||
#define SWRS_SCP_DEVICE_ID_2 (SWRS_BASE+0x0052)
|
||||
#define SWRS_SCP_DEVICE_ID_3 (SWRS_BASE+0x0053)
|
||||
#define SWRS_SCP_DEVICE_ID_4 (SWRS_BASE+0x0054)
|
||||
#define SWRS_SCP_DEVICE_ID_5 (SWRS_BASE+0x0055)
|
||||
#define SWRS_SCP_FRAME_CTRL_BANK(m) (SWRS_BASE+0x0060+0x10*m)
|
||||
#define SWRS_SCP_IMPDEF_SWR_INTERRUPT_DETECT_TYPE (SWRS_BASE+0x00C8)
|
||||
#define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m) (SWRS_BASE+0x00E0+0x10*m)
|
||||
#define SWRS_DP_INT_STATUS(n) (SWRS_BASE+0x100+0x0100*n)
|
||||
#define SWRS_DP_INT_STATUS_MASK(n) (SWRS_BASE+0x101+0x100*n)
|
||||
#define SWRS_DP_PORT_CONTROL(n) (SWRS_BASE+0x102+0x100*n)
|
||||
#define SWRS_DP_BLOCK_CONTROL_1(n) (SWRS_BASE+0x103+0x100*n)
|
||||
#define SWRS_DP_PREPARE_STATUS(n) (SWRS_BASE+0x104+0x100*n)
|
||||
#define SWRS_DP_PREPARE_CONTROL(n) (SWRS_BASE+0x105+0x100*n)
|
||||
#define SWRS_DP_CHANNEL_ENABLE_BANK(n, m) (SWRS_BASE+0x120+0x100*n+0x10*m)
|
||||
#define SWRS_DP_BLOCK_CONTROL_2_BANK(n, m) (SWRS_BASE+0x121+0x100*n+0x10*m)
|
||||
#define SWRS_DP_SAMPLE_CONTROL_1_BANK(n, m) (SWRS_BASE+0x122+0x100*n+0x10*m)
|
||||
#define SWRS_DP_SAMPLE_CONTROL_2_BANK(n, m) (SWRS_BASE+0x123+0x100*n+0x10*m)
|
||||
#define SWRS_DP_OFFSET_CONTROL_1_BANK(n, m) (SWRS_BASE+0x124+0x100*n+0x10*m)
|
||||
#define SWRS_DP_OFFSET_CONTROL_2_BANK(n, m) (SWRS_BASE+0x125+0x100*n+0x10*m)
|
||||
#define SWRS_DP_HCONTROL_BANK(n, m) (SWRS_BASE+0x126+0x100*n+0x10*m)
|
||||
#define SWRS_DP_BLOCK_CONTROL_3_BANK(n, m) (SWRS_BASE+0x127+0x100*n+0x10*m)
|
||||
#define SWRS_DP_LANE_CONTROL_BANK(n, m) (SWRS_BASE+0x128+0x100*n+0x10*m)
|
||||
#define SWRS_DP_ALL_INT_STATUS (SWRS_BASE+0x0F00)
|
||||
#define SWRS_DP_ALL_INT_STATUS_MASK (SWRS_BASE+0x0F01)
|
||||
#define SWRS_DP_ALL_PORT_CONTROL (SWRS_BASE+0x0F02)
|
||||
#define SWRS_DP_ALL_BLOCK_CONTROL_1 (SWRS_BASE+0x0F03)
|
||||
#define SWRS_DP_ALL_PREPARE_STATUS (SWRS_BASE+0x0F04)
|
||||
#define SWRS_DP_ALL_PREPARE_CONTROL (SWRS_BASE+0x0F05)
|
||||
#define SWRS_DP_ALL_CHANNEL_ENABLE_BANK(m) (SWRS_BASE+0x0F20+0x10*m)
|
||||
#define SWRS_DP_ALL_BLOCK_CONTROL_2_BANK(m) (SWRS_BASE+0x0F21+0x10*m)
|
||||
#define SWRS_DP_ALL_SAMPLE_CONTROL_1_BANK(m) (SWRS_BASE+0x0F22+0x10*m)
|
||||
#define SWRS_DP_ALL_SAMPLE_CONTROL_2_BANK(m) (SWRS_BASE+0x0F23+0x10*m)
|
||||
#define SWRS_DP_ALL_OFFSET_CONTROL_1_BANK(m) (SWRS_BASE+0x0F24+0x10*m)
|
||||
#define SWRS_DP_ALL_OFFSET_CONTROL_2_BANK(m) (SWRS_BASE+0x0F25+0x10*m)
|
||||
#define SWRS_DP_ALL_HCONTROL_BANK(m) (SWRS_BASE+0x0F26+0x10*m)
|
||||
#define SWRS_DP_ALL_BLOCK_CONTROL_3_BANK(m) (SWRS_BASE+0x0F27+0x10*m)
|
||||
#define SWRS_DP_ALL_LANE_CONTROL_BANK(m) (SWRS_BASE+0x0F28+0x10*m)
|
||||
#define SWRS_COMP_HW_VERSION_MAJOR (SWRS_BASE+0x2000)
|
||||
#define SWRS_COMP_HW_VERSION_MINOR (SWRS_BASE+0x2001)
|
||||
#define SWRS_COMP_HW_VERSION_STEP (SWRS_BASE+0x2002)
|
||||
#define SWRS_COMP_STATUS (SWRS_BASE+0x2003)
|
||||
#define SWRS_COMP_POWER_CFG (SWRS_BASE+0x2004)
|
||||
#define SWRS_COMP_FEATURE_CFG (SWRS_BASE+0x2005)
|
||||
#define SWRS_COMP_PARAMS (SWRS_BASE+0x2006)
|
||||
#define SWRS_TEST_BUS_CTL (SWRS_BASE+0x2007)
|
||||
#define SWRS_TEST_BUS_STATUS_LOW (SWRS_BASE+0x200A)
|
||||
#define SWRS_TEST_BUS_STATUS_HIGH (SWRS_BASE+0x200B)
|
||||
#define SWRS_LOOPBACK_CTL (SWRS_BASE+0x2009)
|
||||
#define SWRS_DPn_FEATURE_EN(n) (SWRS_BASE+0x000001D4+0x100*n)
|
||||
#define SWRS_DPn_FLOW_CTRL_N_REPEAT_PERIOD(n) (SWRS_BASE+0x000001CC+0x100*n)
|
||||
#define SWRS_DPn_FLOW_CTRL_M_VALID_SAMPLE(n) (SWRS_BASE+0x000001C4+0x100*n)
|
||||
#define SWRS_SCP_BASE_CLK_BASE (SWRS_BASE+0x4d)
|
||||
#define SWRS_SCP_BUSCLOCK_SCALE(m) (SWRS_BASE+0x062+0x10*m)
|
||||
|
||||
#endif /* _SWR_SLAVE_REGISTERS_H */
|
||||
1956
qcom/opensource/audio-kernel/soc/swr-wcd-ctrl.c
Normal file
1956
qcom/opensource/audio-kernel/soc/swr-wcd-ctrl.c
Normal file
File diff suppressed because it is too large
Load Diff
109
qcom/opensource/audio-kernel/soc/swr-wcd-ctrl.h
Normal file
109
qcom/opensource/audio-kernel/soc/swr-wcd-ctrl.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _SWR_WCD_CTRL_H
|
||||
#define _SWR_WCD_CTRL_H
|
||||
#include <linux/module.h>
|
||||
#include <soc/swr-wcd.h>
|
||||
|
||||
#define SWR_MAX_ROW 0 /* Rows = 48 */
|
||||
#define SWR_MAX_COL 7 /* Cols = 16 */
|
||||
#define SWR_MIN_COL 0 /* Cols = 2 */
|
||||
|
||||
#define SWR_WCD_NAME "swr-wcd"
|
||||
|
||||
#define SWR_MSTR_PORT_LEN 8 /* Number of master ports */
|
||||
|
||||
#define SWR_MAX_SLAVE_DEVICES 11
|
||||
|
||||
#define SWRM_VERSION_1_0 0x01010000
|
||||
#define SWRM_VERSION_1_2 0x01030000
|
||||
#define SWRM_VERSION_1_3 0x01040000
|
||||
|
||||
enum {
|
||||
SWR_MSTR_PAUSE,
|
||||
SWR_MSTR_RESUME,
|
||||
SWR_MSTR_UP,
|
||||
SWR_MSTR_DOWN,
|
||||
SWR_MSTR_SSR,
|
||||
};
|
||||
|
||||
enum {
|
||||
SWR_IRQ_FREE,
|
||||
SWR_IRQ_REGISTER,
|
||||
};
|
||||
|
||||
enum {
|
||||
SWR_DAC_PORT,
|
||||
SWR_COMP_PORT,
|
||||
SWR_BOOST_PORT,
|
||||
SWR_VISENSE_PORT,
|
||||
};
|
||||
|
||||
struct usecase {
|
||||
u8 num_port;
|
||||
u8 num_ch;
|
||||
u32 chrate;
|
||||
};
|
||||
|
||||
struct port_params {
|
||||
u8 si;
|
||||
u8 off1;
|
||||
u8 off2;
|
||||
};
|
||||
|
||||
struct swrm_mports {
|
||||
struct list_head list;
|
||||
u8 id;
|
||||
};
|
||||
|
||||
struct swr_ctrl_platform_data {
|
||||
void *handle; /* holds priv data */
|
||||
int (*read)(void *handle, int reg);
|
||||
int (*write)(void *handle, int reg, int val);
|
||||
int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len);
|
||||
int (*clk)(void *handle, bool enable);
|
||||
int (*reg_irq)(void *handle, irqreturn_t(*irq_handler)(int irq,
|
||||
void *data), void *swr_handle, int type);
|
||||
};
|
||||
|
||||
struct swr_mstr_ctrl {
|
||||
struct swr_master master;
|
||||
struct device *dev;
|
||||
struct resource *supplies;
|
||||
struct clk *mclk;
|
||||
int clk_ref_count;
|
||||
struct completion reset;
|
||||
struct completion broadcast;
|
||||
struct mutex mlock;
|
||||
struct mutex reslock;
|
||||
u8 rcmd_id;
|
||||
u8 wcmd_id;
|
||||
void *handle; /* SWR Master handle from client for read and writes */
|
||||
int (*read)(void *handle, int reg);
|
||||
int (*write)(void *handle, int reg, int val);
|
||||
int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len);
|
||||
int (*clk)(void *handle, bool enable);
|
||||
int (*reg_irq)(void *handle, irqreturn_t(*irq_handler)(int irq,
|
||||
void *data), void *swr_handle, int type);
|
||||
int irq;
|
||||
int version;
|
||||
u32 num_dev;
|
||||
int num_enum_slaves;
|
||||
int slave_status;
|
||||
struct swr_mstr_port *mstr_port;
|
||||
struct list_head mport_list;
|
||||
int state;
|
||||
struct platform_device *pdev;
|
||||
int num_rx_chs;
|
||||
u8 num_cfg_devs;
|
||||
struct mutex force_down_lock;
|
||||
int force_down_state;
|
||||
|
||||
struct notifier_block event_notifier;
|
||||
struct work_struct dc_presence_work;
|
||||
};
|
||||
|
||||
#endif /* _SWR_WCD_CTRL_H */
|
||||
252
qcom/opensource/audio-kernel/soc/swrm_registers.h
Normal file
252
qcom/opensource/audio-kernel/soc/swrm_registers.h
Normal file
@@ -0,0 +1,252 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2015, 2018-2019 The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _SWRM_REGISTERS_H
|
||||
#define _SWRM_REGISTERS_H
|
||||
|
||||
#define SWRM_BASE_ADDRESS 0x00
|
||||
|
||||
#define SWRM_COMP_HW_VERSION SWRM_BASE_ADDRESS
|
||||
#define SWRM_COMP_CFG_ADDR (SWRM_BASE_ADDRESS+0x00000004)
|
||||
#define SWRM_COMP_CFG_RMSK 0x3
|
||||
#define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_BMSK 0x2
|
||||
#define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_SHFT 0x1
|
||||
#define SWRM_COMP_CFG_ENABLE_BMSK 0x1
|
||||
#define SWRM_COMP_CFG_ENABLE_SHFT 0x0
|
||||
|
||||
#define SWRM_COMP_SW_RESET (SWRM_BASE_ADDRESS+0x00000008)
|
||||
#define SWRM_COMP_STATUS (SWRM_BASE_ADDRESS+0x00000014)
|
||||
|
||||
#define SWRM_COMP_PARAMS (SWRM_BASE_ADDRESS+0x100)
|
||||
#define SWRM_COMP_PARAMS_DOUT_PORTS_MASK 0x0000001F
|
||||
#define SWRM_COMP_PARAMS_DIN_PORTS_MASK 0x000003E0
|
||||
#define SWRM_COMP_PARAMS_WR_FIFO_DEPTH 0x00007C00
|
||||
#define SWRM_COMP_PARAMS_RD_FIFO_DEPTH 0x000F8000
|
||||
#define SWRM_COMP_PARAMS_AUTO_ENUM_SLAVES 0x00F00000
|
||||
#define SWRM_COMP_PARAMS_DATA_LANES 0x07000000
|
||||
|
||||
#define SWRM_COMP_MASTER_ID (SWRM_BASE_ADDRESS+0x104)
|
||||
|
||||
#define SWRM_INTERRUPT_STATUS (SWRM_BASE_ADDRESS+0x00000200)
|
||||
#define SWRM_INTERRUPT_STATUS_RMSK 0x1FFFD
|
||||
|
||||
#define SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ 0x1
|
||||
#define SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED 0x2
|
||||
#define SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS 0x4
|
||||
#define SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET 0x8
|
||||
#define SWRM_INTERRUPT_STATUS_RD_FIFO_OVERFLOW 0x10
|
||||
#define SWRM_INTERRUPT_STATUS_RD_FIFO_UNDERFLOW 0x20
|
||||
#define SWRM_INTERRUPT_STATUS_WR_CMD_FIFO_OVERFLOW 0x40
|
||||
#define SWRM_INTERRUPT_STATUS_CMD_ERROR 0x80
|
||||
#define SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION 0x100
|
||||
#define SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH 0x200
|
||||
#define SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED 0x400
|
||||
#define SWRM_INTERRUPT_STATUS_NEW_SLAVE_AUTO_ENUM_FINISHED 0x800
|
||||
#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_FAILED 0x1000
|
||||
#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_TABLE_IS_FULL 0x2000
|
||||
#define SWRM_INTERRUPT_STATUS_BUS_RESET_FINISHED 0x4000
|
||||
#define SWRM_INTERRUPT_STATUS_CLK_STOP_FINISHED 0x8000
|
||||
#define SWRM_INTERRUPT_STATUS_ERROR_PORT_TEST 0x10000
|
||||
|
||||
#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_FAILED_V2 0x800
|
||||
#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_TABLE_IS_FULL_V2 0x1000
|
||||
#define SWRM_INTERRUPT_STATUS_BUS_RESET_FINISHED_V2 0x2000
|
||||
#define SWRM_INTERRUPT_STATUS_CLK_STOP_FINISHED_V2 0x4000
|
||||
#define SWRM_INTERRUPT_STATUS_ERROR_PORT_TEST_V2 0x8000
|
||||
#define SWRM_INTERRUPT_STATUS_EXT_CLK_STOP_WAKEUP 0x10000
|
||||
|
||||
#define SWRM_INTERRUPT_MASK_ADDR (SWRM_BASE_ADDRESS+0x00000204)
|
||||
#define SWRM_INTERRUPT_MASK_RMSK 0x1FFFF
|
||||
|
||||
#define SWRM_INTERRUPT_MASK_SLAVE_PEND_IRQ_BMSK 0x1
|
||||
#define SWRM_INTERRUPT_MASK_SLAVE_PEND_IRQ_SHFT 0x0
|
||||
|
||||
#define SWRM_INTERRUPT_MASK_NEW_SLAVE_ATTACHED_BMSK 0x2
|
||||
#define SWRM_INTERRUPT_MASK_NEW_SLAVE_ATTACHED_SHFT 0x1
|
||||
|
||||
#define SWRM_INTERRUPT_MASK_CHANGE_ENUM_SLAVE_STATUS_BMSK 0x4
|
||||
#define SWRM_INTERRUPT_MASK_CHANGE_ENUM_SLAVE_STATUS_SHFT 0x2
|
||||
|
||||
#define SWRM_INTERRUPT_MASK_MASTER_CLASH_DET_BMSK 0x8
|
||||
#define SWRM_INTERRUPT_MASK_MASTER_CLASH_DET_SHFT 0x3
|
||||
|
||||
#define SWRM_INTERRUPT_MASK_RD_FIFO_OVERFLOW_BMSK 0x10
|
||||
#define SWRM_INTERRUPT_MASK_RD_FIFO_OVERFLOW_SHFT 0x4
|
||||
|
||||
#define SWRM_INTERRUPT_MASK_RD_FIFO_UNDERFLOW_BMSK 0x20
|
||||
#define SWRM_INTERRUPT_MASK_RD_FIFO_UNDERFLOW_SHFT 0x5
|
||||
|
||||
#define SWRM_INTERRUPT_MASK_WR_CMD_FIFO_OVERFLOW_BMSK 0x40
|
||||
#define SWRM_INTERRUPT_MASK_WR_CMD_FIFO_OVERFLOW_SHFT 0x6
|
||||
|
||||
#define SWRM_INTERRUPT_MASK_CMD_ERROR_BMSK 0x80
|
||||
#define SWRM_INTERRUPT_MASK_CMD_ERROR_SHFT 0x7
|
||||
|
||||
#define SWRM_INTERRUPT_MASK_DOUT_PORT_COLLISION_BMSK 0x100
|
||||
#define SWRM_INTERRUPT_MASK_DOUT_PORT_COLLISION_SHFT 0x8
|
||||
|
||||
#define SWRM_INTERRUPT_MASK_READ_EN_RD_VALID_MISMATCH_BMSK 0x200
|
||||
#define SWRM_INTERRUPT_MASK_READ_EN_RD_VALID_MISMATCH_SHFT 0x9
|
||||
|
||||
#define SWRM_INTERRUPT_MASK_SPECIAL_CMD_ID_FINISHED_BMSK 0x400
|
||||
#define SWRM_INTERRUPT_MASK_SPECIAL_CMD_ID_FINISHED_SHFT 0xA
|
||||
|
||||
#define SWRM_INTERRUPT_MASK_NEW_SLAVE_AUTO_ENUM_FINISHED_BMSK 0x800
|
||||
#define SWRM_INTERRUPT_MASK_NEW_SLAVE_AUTO_ENUM_FINISHED_SHFT 0xB
|
||||
|
||||
#define SWRM_INTERRUPT_MASK_AUTO_ENUM_FAILED_BMSK 0x1000
|
||||
#define SWRM_INTERRUPT_MASK_AUTO_ENUM_FAILED_SHFT 0xC
|
||||
|
||||
#define SWRM_INTERRUPT_MASK_AUTO_ENUM_TABLE_IS_FULL_BMSK 0x2000
|
||||
#define SWRM_INTERRUPT_MASK_AUTO_ENUM_TABLE_IS_FULL_SHFT 0xD
|
||||
|
||||
#define SWRM_INTERRUPT_MASK_BUS_RESET_FINISHED_BMSK 0x4000
|
||||
#define SWRM_INTERRUPT_MASK_BUS_RESET_FINISHED_SHFT 0xE
|
||||
|
||||
#define SWRM_INTERRUPT_MASK_CLK_STOP_FINISHED_BMSK 0x8000
|
||||
#define SWRM_INTERRUPT_MASK_CLK_STOP_FINISHED_SHFT 0xF
|
||||
|
||||
#define SWRM_INTERRUPT_MASK_ERROR_PORT_TEST_BMSK 0x10000
|
||||
#define SWRM_INTERRUPT_MASK_ERROR_PORT_TEST_SHFT 0x10
|
||||
|
||||
#define SWRM_INTERRUPT_MAX 0x11
|
||||
|
||||
#define SWRM_INTERRUPT_CLEAR (SWRM_BASE_ADDRESS+0x00000208)
|
||||
|
||||
#define SWR_MSTR_RX_SWRM_CPU_INTERRUPT_EN (SWRM_BASE_ADDRESS+0x00000210)
|
||||
|
||||
#define SWRM_CMD_FIFO_WR_CMD (SWRM_BASE_ADDRESS + 0x00000300)
|
||||
#define SWRM_CMD_FIFO_WR_CMD_MASK 0xFFFFFFFF
|
||||
#define SWRM_CMD_FIFO_RD_CMD (SWRM_BASE_ADDRESS + 0x00000304)
|
||||
#define SWRM_CMD_FIFO_RD_CMD_MASK 0xFFFFFFF
|
||||
#define SWRM_CMD_FIFO_CMD (SWRM_BASE_ADDRESS + 0x00000308)
|
||||
#define SWRM_CMD_FIFO_STATUS (SWRM_BASE_ADDRESS + 0x0000030C)
|
||||
|
||||
#define SWRM_CMD_FIFO_STATUS_WR_CMD_FIFO_CNT_MASK 0x1F00
|
||||
#define SWRM_CMD_FIFO_STATUS_RD_CMD_FIFO_CNT_MASK 0x7C00000
|
||||
|
||||
#define SWRM_CMD_FIFO_CFG_ADDR (SWRM_BASE_ADDRESS+0x00000314)
|
||||
#define SWRM_CMD_FIFO_CFG_NUM_OF_CMD_RETRY_BMSK 0x7
|
||||
#define SWRM_CMD_FIFO_CFG_NUM_OF_CMD_RETRY_SHFT 0x0
|
||||
|
||||
#define SWRM_CMD_FIFO_RD_FIFO_ADDR (SWRM_BASE_ADDRESS + 0x00000318)
|
||||
|
||||
#define SWRM_ENUMERATOR_CFG_ADDR (SWRM_BASE_ADDRESS+0x00000500)
|
||||
#define SWRM_ENUMERATOR_CFG_AUTO_ENUM_EN_BMSK 0x1
|
||||
#define SWRM_ENUMERATOR_CFG_AUTO_ENUM_EN_SHFT 0x0
|
||||
|
||||
#define SWRM_ENUMERATOR_STATUS (SWRM_BASE_ADDRESS+0x00000504)
|
||||
#define SWRM_ENUMERATOR_SLAVE_DEV_ID_1(m) (SWRM_BASE_ADDRESS+0x530+0x8*m)
|
||||
#define SWRM_ENUMERATOR_SLAVE_DEV_ID_2(m) (SWRM_BASE_ADDRESS+0x534+0x8*m)
|
||||
|
||||
#define SWRM_MCP_FRAME_CTRL_BANK_ADDR(m) (SWRM_BASE_ADDRESS+0x101C+0x40*m)
|
||||
#define SWRM_MCP_FRAME_CTRL_BANK_RMSK 0x00ff07ff
|
||||
#define SWRM_MCP_FRAME_CTRL_BANK_SHFT 0
|
||||
#define SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_BMSK 0xff0000
|
||||
#define SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_SHFT 16
|
||||
#define SWRM_MCP_FRAME_CTRL_BANK_PHASE_BMSK 0xf800
|
||||
#define SWRM_MCP_FRAME_CTRL_BANK_PHASE_SHFT 11
|
||||
#define SWRM_MCP_FRAME_CTRL_BANK_CLK_DIV_VALUE_BMSK 0x700
|
||||
#define SWRM_MCP_FRAME_CTRL_BANK_CLK_DIV_VALUE_SHFT 8
|
||||
#define SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK 0xF8
|
||||
#define SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT 3
|
||||
#define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK 0x7
|
||||
#define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT 0
|
||||
|
||||
#define SWRM_MCP_BUS_CTRL_ADDR (SWRM_BASE_ADDRESS+0x00001044)
|
||||
#define SWRM_MCP_BUS_CTRL_BUS_RESET_BMSK 0x1
|
||||
#define SWRM_MCP_BUS_CTRL_BUS_RESET_SHFT 0x0
|
||||
#define SWRM_MCP_BUS_CTRL_CLK_START_BMSK 0x2
|
||||
#define SWRM_MCP_BUS_CTRL_CLK_START_SHFT 0x1
|
||||
|
||||
#define SWRM_MCP_CFG_ADDR (SWRM_BASE_ADDRESS+0x00001048)
|
||||
#define SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_BMSK 0x3E0000
|
||||
#define SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_SHFT 0x11
|
||||
#define SWRM_MCP_CFG_BUS_CLK_PAUSE_BMSK 0x02
|
||||
|
||||
#define SWRM_MCP_STATUS (SWRM_BASE_ADDRESS+0x104C)
|
||||
#define SWRM_MCP_STATUS_BANK_NUM_MASK 0x01
|
||||
|
||||
#define SWRM_MCP_SLV_STATUS (SWRM_BASE_ADDRESS+0x1090)
|
||||
#define SWRM_MCP_SLV_STATUS_MASK 0x03
|
||||
|
||||
#define SWRM_DP_PORT_CTRL_BANK(n, m) (SWRM_BASE_ADDRESS + \
|
||||
0x00001124 + \
|
||||
0x100*(n-1) + \
|
||||
0x40*m)
|
||||
#define SWRM_DP_PORT_CTRL_BANK_MASK 0xFFFFFFFF
|
||||
#define SWRM_DP_PORT_CTRL_EN_CHAN_MASK 0xFF000000
|
||||
#define SWRM_DP_PORT_CTRL_EN_CHAN_SHFT 0x18
|
||||
#define SWRM_DP_PORT_CTRL_OFFSET2_SHFT 0x10
|
||||
#define SWRM_DP_PORT_CTRL_OFFSET1_SHFT 0x08
|
||||
#define SWRM_DP_PORT_CTRL_SAMPLE_INTERVAL 0x00
|
||||
|
||||
#define SWRM_DP_PORT_CTRL_2_BANK(n, m) (SWRM_BASE_ADDRESS + \
|
||||
0x00001128 + \
|
||||
0x100*(n-1) + \
|
||||
0x40*m)
|
||||
|
||||
#define SWRM_DP_BLOCK_CTRL_1(n) (SWRM_BASE_ADDRESS + \
|
||||
0x0000112C + \
|
||||
0x100*(n-1))
|
||||
|
||||
#define SWRM_DP_BLOCK_CTRL2_BANK(n, m) (SWRM_BASE_ADDRESS + \
|
||||
0x00001130 + \
|
||||
0x100*(n-1) + \
|
||||
0x40*m)
|
||||
|
||||
#define SWRM_DP_PORT_HCTRL_BANK(n, m) (SWRM_BASE_ADDRESS + \
|
||||
0x00001134 + \
|
||||
0x100*(n-1) + \
|
||||
0x40*m)
|
||||
|
||||
#define SWRM_DP_BLOCK_CTRL3_BANK(n, m) (SWRM_BASE_ADDRESS + \
|
||||
0x00001138 + \
|
||||
0x100*(n-1) + \
|
||||
0x40*m)
|
||||
|
||||
|
||||
#define SWRM_DIN_DPn_PCM_PORT_CTRL(n) (SWRM_BASE_ADDRESS + \
|
||||
0x00001054 + 0x100*(n-1))
|
||||
|
||||
#define SWRM_MAX_REGISTER SWRM_DIN_DPn_PCM_PORT_CTRL(7)
|
||||
|
||||
/* Soundwire Slave Register definition */
|
||||
|
||||
#define SWRS_BASE_ADDRESS 0x00
|
||||
|
||||
#define SWRS_DP_REG_OFFSET(port, bank) ((0x100*port)+(0x10*bank))
|
||||
|
||||
#define SWRS_SCP_INT_STATUS_CLEAR_1 0x40
|
||||
#define SWRS_SCP_INT_STATUS_MASK_1 0x41
|
||||
|
||||
#define SWRS_SCP_CONTROL 0x44
|
||||
#define SWRS_DP_BLOCK_CONTROL_1(n) (SWRS_BASE_ADDRESS + 0x103 + \
|
||||
0x100 * n)
|
||||
|
||||
#define SWRS_DP_CHANNEL_ENABLE_BANK(n, m) (SWRS_BASE_ADDRESS + 0x120 + \
|
||||
SWRS_DP_REG_OFFSET(n, m))
|
||||
#define SWRS_DP_BLOCK_CONTROL_2_BANK(n, m) (SWRS_BASE_ADDRESS + 0x121 + \
|
||||
SWRS_DP_REG_OFFSET(n, m))
|
||||
#define SWRS_DP_SAMPLE_CONTROL_1_BANK(n, m) (SWRS_BASE_ADDRESS + 0x122 + \
|
||||
SWRS_DP_REG_OFFSET(n, m))
|
||||
#define SWRS_DP_SAMPLE_CONTROL_2_BANK(n, m) (SWRS_BASE_ADDRESS + 0x123 + \
|
||||
SWRS_DP_REG_OFFSET(n, m))
|
||||
#define SWRS_DP_OFFSET_CONTROL_1_BANK(n, m) (SWRS_BASE_ADDRESS + 0x124 + \
|
||||
SWRS_DP_REG_OFFSET(n, m))
|
||||
#define SWRS_DP_OFFSET_CONTROL_2_BANK(n, m) (SWRS_BASE_ADDRESS + 0x125 + \
|
||||
SWRS_DP_REG_OFFSET(n, m))
|
||||
#define SWRS_DP_HCONTROL_BANK(n, m) (SWRS_BASE_ADDRESS + 0x126 + \
|
||||
SWRS_DP_REG_OFFSET(n, m))
|
||||
#define SWRS_DP_BLOCK_CONTROL_3_BANK(n, m) (SWRS_BASE_ADDRESS + 0x127 + \
|
||||
SWRS_DP_REG_OFFSET(n, m))
|
||||
#define SWRS_DP_LANE_CONTROL_BANK(n, m) (SWRS_BASE_ADDRESS + 0x128 + \
|
||||
SWRS_DP_REG_OFFSET(n, m))
|
||||
#define SWRS_SCP_FRAME_CTRL_BANK(m) (SWRS_BASE_ADDRESS + 0x60 + \
|
||||
0x10*m)
|
||||
#define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m) (SWRS_BASE_ADDRESS + 0xE0 + \
|
||||
0x10*m)
|
||||
|
||||
#endif /* _SWRM_REGISTERS_H */
|
||||
991
qcom/opensource/audio-kernel/soc/wcd-spi-ac.c
Normal file
991
qcom/opensource/audio-kernel/soc/wcd-spi-ac.c
Normal file
@@ -0,0 +1,991 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <audio/linux/wcd-spi-ac-params.h>
|
||||
#include <soc/wcd-spi-ac.h>
|
||||
#include <soc/qcom/msm_qmi_interface.h>
|
||||
|
||||
#include "wcd_spi_ctl_v01.h"
|
||||
|
||||
#define WCD_SPI_AC_PFS_ENTRY_MAX_LEN 16
|
||||
#define WCD_SPI_AC_WRITE_CMD_MIN_SIZE \
|
||||
(sizeof(struct wcd_spi_ac_write_cmd))
|
||||
#define WCD_SPI_AC_WRITE_CMD_MAX_SIZE \
|
||||
(WCD_SPI_AC_WRITE_CMD_MIN_SIZE + \
|
||||
(WCD_SPI_AC_MAX_BUFFERS * \
|
||||
sizeof(struct wcd_spi_ac_buf_data)))
|
||||
|
||||
#define WCD_SPI_AC_MUTEX_LOCK(dev, lock) \
|
||||
{ \
|
||||
dev_dbg(dev, "%s: mutex_lock(%s)\n", \
|
||||
__func__, __stringify_1(lock)); \
|
||||
mutex_lock(&lock); \
|
||||
}
|
||||
|
||||
#define WCD_SPI_AC_MUTEX_UNLOCK(dev, lock) \
|
||||
{ \
|
||||
dev_dbg(dev, "%s: mutex_unlock(%s)\n", \
|
||||
__func__, __stringify_1(lock)); \
|
||||
mutex_unlock(&lock); \
|
||||
}
|
||||
|
||||
/*
|
||||
* All bits of status should be cleared for SPI access
|
||||
* to be released.
|
||||
*/
|
||||
#define WCD_SPI_AC_STATUS_RELEASE_ACCESS 0x00
|
||||
#define WCD_SPI_AC_LOCAL_ACCESS 0x00
|
||||
#define WCD_SPI_AC_REMOTE_ACCESS 0x01
|
||||
#define WCD_SPI_CTL_INS_ID 0
|
||||
#define WCD_SPI_AC_QMI_TIMEOUT_MS 100
|
||||
|
||||
struct wcd_spi_ac_priv {
|
||||
|
||||
/* Pointer to device for this driver */
|
||||
struct device *dev;
|
||||
|
||||
/* Pointer to parent's device */
|
||||
struct device *parent;
|
||||
|
||||
/* char dev related */
|
||||
struct class *cls;
|
||||
struct device *chardev;
|
||||
struct cdev cdev;
|
||||
dev_t cdev_num;
|
||||
|
||||
/* proc entry related */
|
||||
struct proc_dir_entry *pfs_root;
|
||||
struct proc_dir_entry *pfs_status;
|
||||
|
||||
/* service status related */
|
||||
u8 svc_offline;
|
||||
u8 svc_offline_change;
|
||||
wait_queue_head_t svc_poll_wait;
|
||||
struct mutex status_lock;
|
||||
|
||||
/* state maintenence related */
|
||||
u32 state;
|
||||
struct mutex state_lock;
|
||||
u8 current_access;
|
||||
|
||||
/* qmi related */
|
||||
struct qmi_handle *qmi_hdl;
|
||||
struct work_struct svc_arr_work;
|
||||
struct work_struct svc_exit_work;
|
||||
struct notifier_block nb;
|
||||
struct mutex svc_lock;
|
||||
struct workqueue_struct *qmi_wq;
|
||||
struct work_struct recv_msg_work;
|
||||
};
|
||||
|
||||
|
||||
static void wcd_spi_ac_status_change(struct wcd_spi_ac_priv *ac,
|
||||
u8 online)
|
||||
{
|
||||
WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->status_lock);
|
||||
ac->svc_offline = !online;
|
||||
/* Make sure the write is complete */
|
||||
wmb();
|
||||
xchg(&ac->svc_offline_change, 1);
|
||||
wake_up_interruptible(&ac->svc_poll_wait);
|
||||
dev_dbg(ac->dev,
|
||||
"%s request %u offline %u off_change %u\n",
|
||||
__func__, online, ac->svc_offline,
|
||||
ac->svc_offline_change);
|
||||
WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->status_lock);
|
||||
}
|
||||
|
||||
static int wcd_spi_ac_status_open(struct inode *inode,
|
||||
struct file *file)
|
||||
{
|
||||
struct wcd_spi_ac_priv *ac = PDE_DATA(inode);
|
||||
|
||||
file->private_data = ac;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t wcd_spi_ac_status_read(struct file *file,
|
||||
char __user *buffer,
|
||||
size_t count, loff_t *offset)
|
||||
{
|
||||
struct wcd_spi_ac_priv *ac;
|
||||
char buf[WCD_SPI_AC_PFS_ENTRY_MAX_LEN];
|
||||
int len, ret;
|
||||
u8 offline;
|
||||
|
||||
ac = (struct wcd_spi_ac_priv *) file->private_data;
|
||||
if (!ac) {
|
||||
pr_err("%s: Invalid private data for status\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->status_lock);
|
||||
offline = ac->svc_offline;
|
||||
/* Make sure the read is complete */
|
||||
rmb();
|
||||
dev_dbg(ac->dev, "%s: offline = %sline\n",
|
||||
__func__, offline ? "off" : "on");
|
||||
len = snprintf(buf, sizeof(buf), "%s\n",
|
||||
offline ? "OFFLINE" : "ONLINE");
|
||||
ret = simple_read_from_buffer(buffer, count, offset, buf, len);
|
||||
WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->status_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int wcd_spi_ac_status_poll(struct file *file,
|
||||
poll_table *wait)
|
||||
{
|
||||
struct wcd_spi_ac_priv *ac;
|
||||
unsigned int ret = 0;
|
||||
|
||||
ac = (struct wcd_spi_ac_priv *) file->private_data;
|
||||
if (!ac) {
|
||||
pr_err("%s: Invalid private data for status\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(ac->dev, "%s: Poll wait, svc = %s\n",
|
||||
__func__, ac->svc_offline ? "offline" : "online");
|
||||
poll_wait(file, &ac->svc_poll_wait, wait);
|
||||
dev_dbg(ac->dev, "%s: Woken up Poll wait, svc = %s\n",
|
||||
__func__, ac->svc_offline ? "offline" : "online");
|
||||
|
||||
WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->status_lock);
|
||||
if (xchg(&ac->svc_offline_change, 0))
|
||||
ret = POLLIN | POLLPRI | POLLRDNORM;
|
||||
dev_dbg(ac->dev, "%s: ret (%d) from poll_wait\n",
|
||||
__func__, ret);
|
||||
WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->status_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations wcd_spi_ac_status_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = wcd_spi_ac_status_open,
|
||||
.read = wcd_spi_ac_status_read,
|
||||
.poll = wcd_spi_ac_status_poll,
|
||||
};
|
||||
|
||||
static int wcd_spi_ac_procfs_init(struct wcd_spi_ac_priv *ac)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ac->pfs_root = proc_mkdir(WCD_SPI_AC_PROCFS_DIR_NAME, NULL);
|
||||
if (!ac->pfs_root) {
|
||||
dev_err(ac->dev, "%s: proc_mkdir failed\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ac->pfs_status = proc_create_data(WCD_SPI_AC_PROCFS_STATE_NAME,
|
||||
0444, ac->pfs_root,
|
||||
&wcd_spi_ac_status_ops,
|
||||
ac);
|
||||
if (!ac->pfs_status) {
|
||||
dev_err(ac->dev, "%s: proc_create_data failed\n",
|
||||
__func__);
|
||||
ret = -EINVAL;
|
||||
goto rmdir_root;
|
||||
}
|
||||
|
||||
proc_set_size(ac->pfs_status, WCD_SPI_AC_PFS_ENTRY_MAX_LEN);
|
||||
|
||||
return 0;
|
||||
|
||||
rmdir_root:
|
||||
proc_remove(ac->pfs_root);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void wcd_spi_ac_procfs_deinit(struct wcd_spi_ac_priv *ac)
|
||||
{
|
||||
proc_remove(ac->pfs_status);
|
||||
proc_remove(ac->pfs_root);
|
||||
}
|
||||
|
||||
static int wcd_spi_ac_request_access(struct wcd_spi_ac_priv *ac,
|
||||
bool is_svc_locked)
|
||||
{
|
||||
struct wcd_spi_req_access_msg_v01 req;
|
||||
struct wcd_spi_req_access_resp_v01 rsp;
|
||||
struct msg_desc req_desc, rsp_desc;
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(ac->dev, "%s: is_svc_locked = %s\n",
|
||||
__func__, is_svc_locked ? "true" : "false");
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
memset(&rsp, 0, sizeof(rsp));
|
||||
|
||||
req.reason_valid = 1;
|
||||
req.reason = ac->state & 0x03;
|
||||
|
||||
req_desc.max_msg_len = WCD_SPI_REQ_ACCESS_MSG_V01_MAX_MSG_LEN;
|
||||
req_desc.msg_id = WCD_SPI_REQ_ACCESS_MSG_V01;
|
||||
req_desc.ei_array = wcd_spi_req_access_msg_v01_ei;
|
||||
|
||||
rsp_desc.max_msg_len = WCD_SPI_REQ_ACCESS_RESP_V01_MAX_MSG_LEN;
|
||||
rsp_desc.msg_id = WCD_SPI_REQ_ACCESS_RESP_V01;
|
||||
rsp_desc.ei_array = wcd_spi_req_access_resp_v01_ei;
|
||||
|
||||
if (!is_svc_locked)
|
||||
WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->svc_lock);
|
||||
|
||||
ret = qmi_send_req_wait(ac->qmi_hdl,
|
||||
&req_desc, &req, sizeof(req),
|
||||
&rsp_desc, &rsp, sizeof(rsp),
|
||||
WCD_SPI_AC_QMI_TIMEOUT_MS);
|
||||
if (ret) {
|
||||
dev_err(ac->dev, "%s: msg send failed %d\n",
|
||||
__func__, ret);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (rsp.resp.result != QMI_RESULT_SUCCESS_V01) {
|
||||
ret = -EIO;
|
||||
dev_err(ac->dev, "%s: qmi resp error %d\n",
|
||||
__func__, rsp.resp.result);
|
||||
}
|
||||
done:
|
||||
if (!is_svc_locked)
|
||||
WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->svc_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wcd_spi_ac_release_access(struct wcd_spi_ac_priv *ac,
|
||||
bool is_svc_locked)
|
||||
{
|
||||
struct wcd_spi_rel_access_msg_v01 req;
|
||||
struct wcd_spi_rel_access_resp_v01 rsp;
|
||||
struct msg_desc req_desc, rsp_desc;
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(ac->dev, "%s: is_svc_locked = %s\n",
|
||||
__func__, is_svc_locked ? "true" : "false");
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
memset(&rsp, 0, sizeof(rsp));
|
||||
|
||||
req_desc.max_msg_len = WCD_SPI_REL_ACCESS_MSG_V01_MAX_MSG_LEN;
|
||||
req_desc.msg_id = WCD_SPI_REL_ACCESS_MSG_V01;
|
||||
req_desc.ei_array = wcd_spi_rel_access_msg_v01_ei;
|
||||
|
||||
rsp_desc.max_msg_len = WCD_SPI_REL_ACCESS_RESP_V01_MAX_MSG_LEN;
|
||||
rsp_desc.msg_id = WCD_SPI_REL_ACCESS_RESP_V01;
|
||||
rsp_desc.ei_array = wcd_spi_rel_access_resp_v01_ei;
|
||||
|
||||
if (!is_svc_locked)
|
||||
WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->svc_lock);
|
||||
|
||||
ret = qmi_send_req_wait(ac->qmi_hdl,
|
||||
&req_desc, &req, sizeof(req),
|
||||
&rsp_desc, &rsp, sizeof(rsp),
|
||||
WCD_SPI_AC_QMI_TIMEOUT_MS);
|
||||
if (ret) {
|
||||
dev_err(ac->dev, "%s: msg send failed %d\n",
|
||||
__func__, ret);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (rsp.resp.result != QMI_RESULT_SUCCESS_V01) {
|
||||
ret = -EIO;
|
||||
dev_err(ac->dev, "%s: qmi resp error %d\n",
|
||||
__func__, rsp.resp.result);
|
||||
}
|
||||
done:
|
||||
if (!is_svc_locked)
|
||||
WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->svc_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wcd_spi_ac_buf_msg(
|
||||
struct wcd_spi_ac_priv *ac,
|
||||
u8 *data, int data_sz)
|
||||
{
|
||||
struct wcd_spi_ac_buf_data *buf_data;
|
||||
struct wcd_spi_buff_msg_v01 req;
|
||||
struct wcd_spi_buff_resp_v01 rsp;
|
||||
struct msg_desc req_desc, rsp_desc;
|
||||
int ret = 0;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
memset(&rsp, 0, sizeof(rsp));
|
||||
|
||||
buf_data = (struct wcd_spi_ac_buf_data *) data;
|
||||
memcpy(req.buff_addr_1, buf_data,
|
||||
sizeof(*buf_data));
|
||||
|
||||
if (data_sz - sizeof(*buf_data) != 0) {
|
||||
req.buff_addr_2_valid = 1;
|
||||
buf_data++;
|
||||
memcpy(req.buff_addr_2, buf_data,
|
||||
sizeof(*buf_data));
|
||||
}
|
||||
|
||||
req_desc.max_msg_len = WCD_SPI_BUFF_MSG_V01_MAX_MSG_LEN;
|
||||
req_desc.msg_id = WCD_SPI_BUFF_MSG_V01;
|
||||
req_desc.ei_array = wcd_spi_buff_msg_v01_ei;
|
||||
|
||||
rsp_desc.max_msg_len = WCD_SPI_BUFF_RESP_V01_MAX_MSG_LEN;
|
||||
rsp_desc.msg_id = WCD_SPI_BUFF_RESP_V01;
|
||||
rsp_desc.ei_array = wcd_spi_buff_resp_v01_ei;
|
||||
|
||||
WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->svc_lock);
|
||||
ret = qmi_send_req_wait(ac->qmi_hdl,
|
||||
&req_desc, &req, sizeof(req),
|
||||
&rsp_desc, &rsp, sizeof(rsp),
|
||||
WCD_SPI_AC_QMI_TIMEOUT_MS);
|
||||
|
||||
if (ret) {
|
||||
dev_err(ac->dev, "%s: msg send failed %d\n",
|
||||
__func__, ret);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (rsp.resp.result != QMI_RESULT_SUCCESS_V01) {
|
||||
ret = -EIO;
|
||||
dev_err(ac->dev, "%s: qmi resp error %d\n",
|
||||
__func__, rsp.resp.result);
|
||||
}
|
||||
done:
|
||||
WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->svc_lock);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* wcd_spi_ac_set_sync: Sets the current status of the SPI
|
||||
* bus and requests access if not
|
||||
* already accesible.
|
||||
* @ac: pointer to the drivers private data
|
||||
* @value: value to be set in the status mask
|
||||
* @is_svc_locked: flag to indicate if svc_lock is acquired by caller
|
||||
*/
|
||||
static int wcd_spi_ac_set_sync(struct wcd_spi_ac_priv *ac,
|
||||
u32 value, bool is_svc_locked)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->state_lock);
|
||||
ac->state |= value;
|
||||
/* any non-zero state indicates us to request SPI access */
|
||||
wmb();
|
||||
dev_dbg(ac->dev, "%s: current state = 0x%x, current access 0x%x\n",
|
||||
__func__, ac->state, ac->current_access);
|
||||
if (ac->current_access == WCD_SPI_AC_REMOTE_ACCESS) {
|
||||
dev_dbg(ac->dev,
|
||||
"%s: requesting access, state = 0x%x\n",
|
||||
__func__, ac->state);
|
||||
ret = wcd_spi_ac_request_access(ac, is_svc_locked);
|
||||
if (!ret)
|
||||
ac->current_access = WCD_SPI_AC_LOCAL_ACCESS;
|
||||
}
|
||||
WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->state_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* wcd_spi_ac_clear_sync: Clears the current status of the SPI
|
||||
* bus and releases access if applicable
|
||||
* @ac: pointer to the drivers private data
|
||||
* @value: value to be cleared in the status mask
|
||||
* @is_svc_locked: flag to indicate if svc_lock is acquired by caller
|
||||
*/
|
||||
static int wcd_spi_ac_clear_sync(struct wcd_spi_ac_priv *ac,
|
||||
u32 value, bool is_svc_locked)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->state_lock);
|
||||
ac->state &= ~(value);
|
||||
/* make sure value is written before read */
|
||||
wmb();
|
||||
dev_dbg(ac->dev, "%s: current state = 0x%x, current access 0x%x\n",
|
||||
__func__, ac->state, ac->current_access);
|
||||
/* state should be zero to release SPI access */
|
||||
if (!ac->state &&
|
||||
ac->current_access == WCD_SPI_AC_LOCAL_ACCESS) {
|
||||
dev_dbg(ac->dev,
|
||||
"%s: releasing access, state = 0x%x\n",
|
||||
__func__, ac->state);
|
||||
ret = wcd_spi_ac_release_access(ac, is_svc_locked);
|
||||
if (!ret)
|
||||
ac->current_access = WCD_SPI_AC_REMOTE_ACCESS;
|
||||
}
|
||||
WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->state_lock);
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* wcd_spi_access_ctl: API to request/release the access
|
||||
* to wcd-spi bus.
|
||||
* @dev: handle to the wcd-spi-ac device
|
||||
* @request: enum to indicate access request or access release
|
||||
* @reason: reason for request/release. Must be one of the
|
||||
* valid reasons.
|
||||
* Returns success if the access handover was sucessful,
|
||||
* negative error code otherwise.
|
||||
*/
|
||||
int wcd_spi_access_ctl(struct device *dev,
|
||||
enum wcd_spi_acc_req request,
|
||||
u32 reason)
|
||||
{
|
||||
struct wcd_spi_ac_priv *ac;
|
||||
int ret = 0;
|
||||
|
||||
if (!dev) {
|
||||
pr_err("%s: invalid device\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* only data_transfer and remote_down are valid reasons */
|
||||
if (reason != WCD_SPI_AC_DATA_TRANSFER &&
|
||||
reason != WCD_SPI_AC_REMOTE_DOWN) {
|
||||
pr_err("%s: Invalid reason 0x%x\n",
|
||||
__func__, reason);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ac = (struct wcd_spi_ac_priv *) dev_get_drvdata(dev);
|
||||
if (!ac) {
|
||||
dev_err(dev, "%s: invalid driver data\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "%s: request = 0x%x, reason = 0x%x\n",
|
||||
__func__, request, reason);
|
||||
|
||||
switch (request) {
|
||||
case WCD_SPI_ACCESS_REQUEST:
|
||||
ret = wcd_spi_ac_set_sync(ac, reason, false);
|
||||
if (ret)
|
||||
dev_err(dev, "%s: set_sync(0x%x) failed %d\n",
|
||||
__func__, reason, ret);
|
||||
break;
|
||||
case WCD_SPI_ACCESS_RELEASE:
|
||||
ret = wcd_spi_ac_clear_sync(ac, reason, false);
|
||||
if (ret)
|
||||
dev_err(dev, "%s: clear_sync(0x%x) failed %d\n",
|
||||
__func__, reason, ret);
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "%s: invalid request 0x%x\n",
|
||||
__func__, request);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(wcd_spi_access_ctl);
|
||||
|
||||
static int wcd_spi_ac_cdev_open(struct inode *inode,
|
||||
struct file *file)
|
||||
{
|
||||
struct wcd_spi_ac_priv *ac;
|
||||
int ret = 0;
|
||||
|
||||
ac = container_of(inode->i_cdev, struct wcd_spi_ac_priv, cdev);
|
||||
if (!ac) {
|
||||
pr_err("%s: Invalid private data\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->status_lock);
|
||||
if (ac->svc_offline) {
|
||||
dev_err(ac->dev, "%s: SVC is not online, cannot open driver\n",
|
||||
__func__);
|
||||
ret = -ENODEV;
|
||||
goto done;
|
||||
}
|
||||
|
||||
file->private_data = ac;
|
||||
|
||||
done:
|
||||
WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->status_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t wcd_spi_ac_cdev_write(struct file *file,
|
||||
const char __user *buf,
|
||||
size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct wcd_spi_ac_priv *ac;
|
||||
struct wcd_spi_ac_write_cmd *cmd_buf;
|
||||
int ret = 0;
|
||||
int data_sz;
|
||||
|
||||
ac = (struct wcd_spi_ac_priv *) file->private_data;
|
||||
if (!ac) {
|
||||
pr_err("%s: Invalid private data\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (count < WCD_SPI_AC_WRITE_CMD_MIN_SIZE ||
|
||||
count > WCD_SPI_AC_WRITE_CMD_MAX_SIZE) {
|
||||
dev_err(ac->dev, "%s: Invalid write count %zd\n",
|
||||
__func__, count);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cmd_buf = kzalloc(count, GFP_KERNEL);
|
||||
if (!cmd_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
if (get_user(cmd_buf->cmd_type, buf)) {
|
||||
dev_err(ac->dev, "%s: get_user failed\n", __func__);
|
||||
ret = -EFAULT;
|
||||
goto free_cmd_buf;
|
||||
}
|
||||
|
||||
dev_dbg(ac->dev, "%s: write cmd type 0x%x\n",
|
||||
__func__, cmd_buf->cmd_type);
|
||||
|
||||
switch (cmd_buf->cmd_type) {
|
||||
|
||||
case WCD_SPI_AC_CMD_CONC_BEGIN:
|
||||
ret = wcd_spi_ac_set_sync(ac, WCD_SPI_AC_CONCURRENCY, false);
|
||||
if (ret) {
|
||||
dev_err(ac->dev, "%s: set_sync(CONC) fail %d\n",
|
||||
__func__, ret);
|
||||
goto free_cmd_buf;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case WCD_SPI_AC_CMD_CONC_END:
|
||||
ret = wcd_spi_ac_clear_sync(ac, WCD_SPI_AC_CONCURRENCY, false);
|
||||
if (ret) {
|
||||
dev_err(ac->dev, "%s: clear_sync(CONC) fail %d\n",
|
||||
__func__, ret);
|
||||
goto free_cmd_buf;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case WCD_SPI_AC_CMD_BUF_DATA:
|
||||
|
||||
/* Read the buffer details and send to service */
|
||||
data_sz = count - sizeof(cmd_buf->cmd_type);
|
||||
|
||||
if (!data_sz ||
|
||||
(data_sz % sizeof(struct wcd_spi_ac_buf_data))) {
|
||||
dev_err(ac->dev, "%s: size %d not multiple of %ld\n",
|
||||
__func__, data_sz,
|
||||
sizeof(struct wcd_spi_ac_buf_data));
|
||||
goto free_cmd_buf;
|
||||
}
|
||||
|
||||
if (data_sz / sizeof(struct wcd_spi_ac_buf_data) >
|
||||
WCD_SPI_AC_MAX_BUFFERS) {
|
||||
dev_err(ac->dev, "%s: invalid size %d\n",
|
||||
__func__, data_sz);
|
||||
goto free_cmd_buf;
|
||||
}
|
||||
|
||||
if (copy_from_user(cmd_buf->payload,
|
||||
buf + sizeof(cmd_buf->cmd_type),
|
||||
data_sz)) {
|
||||
dev_err(ac->dev, "%s: copy_from_user failed\n",
|
||||
__func__);
|
||||
ret = -EFAULT;
|
||||
goto free_cmd_buf;
|
||||
}
|
||||
|
||||
ret = wcd_spi_ac_buf_msg(ac, cmd_buf->payload, data_sz);
|
||||
if (ret) {
|
||||
dev_err(ac->dev, "%s: _buf_msg failed %d\n",
|
||||
__func__, ret);
|
||||
goto free_cmd_buf;
|
||||
}
|
||||
|
||||
ret = wcd_spi_ac_clear_sync(ac, WCD_SPI_AC_UNINITIALIZED,
|
||||
false);
|
||||
if (ret) {
|
||||
dev_err(ac->dev, "%s: clear_sync 0x%lx failed %d\n",
|
||||
__func__, WCD_SPI_AC_UNINITIALIZED, ret);
|
||||
goto free_cmd_buf;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dev_err(ac->dev, "%s: Invalid cmd_type 0x%x\n",
|
||||
__func__, cmd_buf->cmd_type);
|
||||
ret = -EINVAL;
|
||||
goto free_cmd_buf;
|
||||
}
|
||||
|
||||
free_cmd_buf:
|
||||
|
||||
kfree(cmd_buf);
|
||||
if (!ret)
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wcd_spi_ac_cdev_release(struct inode *inode,
|
||||
struct file *file)
|
||||
{
|
||||
struct wcd_spi_ac_priv *ac;
|
||||
int ret = 0;
|
||||
|
||||
ac = (struct wcd_spi_ac_priv *) file->private_data;
|
||||
if (!ac) {
|
||||
pr_err("%s: Invalid private data\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = wcd_spi_ac_set_sync(ac, WCD_SPI_AC_UNINITIALIZED, false);
|
||||
if (ret)
|
||||
dev_err(ac->dev, "%s: set_sync(UNINITIALIZED) failed %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations wcd_spi_ac_cdev_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = wcd_spi_ac_cdev_open,
|
||||
.write = wcd_spi_ac_cdev_write,
|
||||
.release = wcd_spi_ac_cdev_release,
|
||||
};
|
||||
|
||||
static int wcd_spi_ac_reg_chardev(struct wcd_spi_ac_priv *ac)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = alloc_chrdev_region(&ac->cdev_num, 0, 1,
|
||||
WCD_SPI_AC_CLIENT_CDEV_NAME);
|
||||
if (ret) {
|
||||
dev_err(ac->dev, "%s: alloc_chrdev_region failed %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ac->cls = class_create(THIS_MODULE, WCD_SPI_AC_CLIENT_CDEV_NAME);
|
||||
if (IS_ERR(ac->cls)) {
|
||||
ret = PTR_ERR(ac->cls);
|
||||
dev_err(ac->dev, "%s: class_create failed %d\n",
|
||||
__func__, ret);
|
||||
goto unregister_chrdev;
|
||||
}
|
||||
|
||||
ac->chardev = device_create(ac->cls, NULL, ac->cdev_num,
|
||||
NULL, WCD_SPI_AC_CLIENT_CDEV_NAME);
|
||||
if (IS_ERR(ac->chardev)) {
|
||||
ret = PTR_ERR(ac->chardev);
|
||||
dev_err(ac->dev, "%s: device_create failed %d\n",
|
||||
__func__, ret);
|
||||
goto destroy_class;
|
||||
}
|
||||
|
||||
cdev_init(&ac->cdev, &wcd_spi_ac_cdev_fops);
|
||||
ret = cdev_add(&ac->cdev, ac->cdev_num, 1);
|
||||
if (ret) {
|
||||
dev_err(ac->dev, "%s: cdev_add failed %d\n",
|
||||
__func__, ret);
|
||||
goto destroy_device;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
destroy_device:
|
||||
device_destroy(ac->cls, ac->cdev_num);
|
||||
|
||||
destroy_class:
|
||||
class_destroy(ac->cls);
|
||||
|
||||
unregister_chrdev:
|
||||
unregister_chrdev_region(0, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wcd_spi_ac_unreg_chardev(struct wcd_spi_ac_priv *ac)
|
||||
{
|
||||
cdev_del(&ac->cdev);
|
||||
device_destroy(ac->cls, ac->cdev_num);
|
||||
class_destroy(ac->cls);
|
||||
unregister_chrdev_region(0, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wcd_spi_ac_recv_msg(struct work_struct *work)
|
||||
{
|
||||
struct wcd_spi_ac_priv *ac;
|
||||
int rc = 0;
|
||||
|
||||
ac = container_of(work, struct wcd_spi_ac_priv,
|
||||
recv_msg_work);
|
||||
if (!ac) {
|
||||
pr_err("%s: Invalid private data\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
dev_dbg(ac->dev, "%s: msg received, rc = %d\n",
|
||||
__func__, rc);
|
||||
} while ((rc = qmi_recv_msg(ac->qmi_hdl)) == 0);
|
||||
|
||||
if (rc != -ENOMSG)
|
||||
dev_err(ac->dev, "%s: qmi_recv_msg failed %d\n",
|
||||
__func__, rc);
|
||||
}
|
||||
|
||||
static void wcd_spi_ac_clnt_notify(struct qmi_handle *hdl,
|
||||
enum qmi_event_type event, void *priv_data)
|
||||
{
|
||||
struct wcd_spi_ac_priv *ac;
|
||||
|
||||
if (!priv_data) {
|
||||
pr_err("%s: Invalid private data\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
ac = (struct wcd_spi_ac_priv *) priv_data;
|
||||
|
||||
switch (event) {
|
||||
case QMI_RECV_MSG:
|
||||
queue_work(ac->qmi_wq, &ac->recv_msg_work);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void wcd_spi_ac_svc_arrive(struct work_struct *work)
|
||||
{
|
||||
struct wcd_spi_ac_priv *ac;
|
||||
int ret;
|
||||
|
||||
ac = container_of(work, struct wcd_spi_ac_priv,
|
||||
svc_arr_work);
|
||||
if (!ac) {
|
||||
pr_err("%s: Invalid private data\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->svc_lock);
|
||||
ac->qmi_hdl = qmi_handle_create(wcd_spi_ac_clnt_notify,
|
||||
ac);
|
||||
if (!ac->qmi_hdl) {
|
||||
dev_err(ac->dev, "%s: qmi_handle_create failed\n",
|
||||
__func__);
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = qmi_connect_to_service(ac->qmi_hdl,
|
||||
WCD_SPI_CTL_SERVICE_ID_V01,
|
||||
WCD_SPI_CTL_SERVICE_VERS_V01,
|
||||
WCD_SPI_CTL_INS_ID);
|
||||
if (ret) {
|
||||
dev_err(ac->dev, "%s, cant connect to service, error %d\n",
|
||||
__func__, ret);
|
||||
qmi_handle_destroy(ac->qmi_hdl);
|
||||
ac->qmi_hdl = NULL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Mark service as online */
|
||||
wcd_spi_ac_status_change(ac, 1);
|
||||
|
||||
/*
|
||||
* update the state and clear the WCD_SPI_AC_SVC_OFFLINE
|
||||
* bit to indicate that the service is now online.
|
||||
*/
|
||||
ret = wcd_spi_ac_clear_sync(ac, WCD_SPI_AC_SVC_OFFLINE, true);
|
||||
if (ret)
|
||||
dev_err(ac->dev, "%s: clear_sync(SVC_OFFLINE) failed %d\n",
|
||||
__func__, ret);
|
||||
done:
|
||||
WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->svc_lock);
|
||||
|
||||
}
|
||||
|
||||
static void wcd_spi_ac_svc_exit(struct work_struct *work)
|
||||
{
|
||||
struct wcd_spi_ac_priv *ac;
|
||||
int ret = 0;
|
||||
|
||||
ac = container_of(work, struct wcd_spi_ac_priv,
|
||||
svc_exit_work);
|
||||
if (!ac) {
|
||||
pr_err("%s: Invalid private data\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->svc_lock);
|
||||
ret = wcd_spi_ac_set_sync(ac, WCD_SPI_AC_SVC_OFFLINE, true);
|
||||
if (ret)
|
||||
dev_err(ac->dev, "%s: set_sync(SVC_OFFLINE) failed %d\n",
|
||||
__func__, ret);
|
||||
qmi_handle_destroy(ac->qmi_hdl);
|
||||
ac->qmi_hdl = NULL;
|
||||
wcd_spi_ac_status_change(ac, 0);
|
||||
WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->svc_lock);
|
||||
}
|
||||
|
||||
static int wcd_spi_ac_svc_event(struct notifier_block *this,
|
||||
unsigned long event,
|
||||
void *data)
|
||||
{
|
||||
struct wcd_spi_ac_priv *ac;
|
||||
|
||||
ac = container_of(this, struct wcd_spi_ac_priv, nb);
|
||||
if (!ac) {
|
||||
pr_err("%s: Invalid private data\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(ac->dev, "%s: event = 0x%lx", __func__, event);
|
||||
|
||||
switch (event) {
|
||||
case QMI_SERVER_ARRIVE:
|
||||
schedule_work(&ac->svc_arr_work);
|
||||
break;
|
||||
case QMI_SERVER_EXIT:
|
||||
schedule_work(&ac->svc_exit_work);
|
||||
break;
|
||||
default:
|
||||
dev_err(ac->dev, "%s unhandled event %ld\n",
|
||||
__func__, event);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wcd_spi_ac_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct wcd_spi_ac_priv *ac;
|
||||
struct device *parent = pdev->dev.parent;
|
||||
int ret = 0;
|
||||
|
||||
ac = devm_kzalloc(&pdev->dev, sizeof(*ac),
|
||||
GFP_KERNEL);
|
||||
if (!ac)
|
||||
return -ENOMEM;
|
||||
|
||||
ac->dev = &pdev->dev;
|
||||
ac->parent = parent;
|
||||
|
||||
ret = wcd_spi_ac_reg_chardev(ac);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = wcd_spi_ac_procfs_init(ac);
|
||||
if (ret)
|
||||
goto unreg_chardev;
|
||||
|
||||
mutex_init(&ac->status_lock);
|
||||
mutex_init(&ac->state_lock);
|
||||
mutex_init(&ac->svc_lock);
|
||||
init_waitqueue_head(&ac->svc_poll_wait);
|
||||
ac->svc_offline = 1;
|
||||
ac->state = (WCD_SPI_AC_SVC_OFFLINE |
|
||||
WCD_SPI_AC_UNINITIALIZED);
|
||||
ac->current_access = WCD_SPI_AC_LOCAL_ACCESS;
|
||||
|
||||
ac->nb.notifier_call = wcd_spi_ac_svc_event;
|
||||
INIT_WORK(&ac->svc_arr_work, wcd_spi_ac_svc_arrive);
|
||||
INIT_WORK(&ac->svc_exit_work, wcd_spi_ac_svc_exit);
|
||||
INIT_WORK(&ac->recv_msg_work, wcd_spi_ac_recv_msg);
|
||||
|
||||
ac->qmi_wq = create_singlethread_workqueue("qmi_wq");
|
||||
if (!ac->qmi_wq) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: create_singlethread_workqueue failed\n",
|
||||
__func__);
|
||||
goto deinit_procfs;
|
||||
}
|
||||
|
||||
dev_set_drvdata(&pdev->dev, ac);
|
||||
|
||||
ret = qmi_svc_event_notifier_register(
|
||||
WCD_SPI_CTL_SERVICE_ID_V01,
|
||||
WCD_SPI_CTL_SERVICE_VERS_V01,
|
||||
WCD_SPI_CTL_INS_ID,
|
||||
&ac->nb);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: qmi_svc_event_notifier_register failed %d\n",
|
||||
__func__, ret);
|
||||
goto destroy_wq;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
destroy_wq:
|
||||
destroy_workqueue(ac->qmi_wq);
|
||||
dev_set_drvdata(&pdev->dev, NULL);
|
||||
deinit_procfs:
|
||||
wcd_spi_ac_procfs_deinit(ac);
|
||||
mutex_destroy(&ac->status_lock);
|
||||
mutex_destroy(&ac->state_lock);
|
||||
mutex_destroy(&ac->svc_lock);
|
||||
unreg_chardev:
|
||||
wcd_spi_ac_unreg_chardev(ac);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wcd_spi_ac_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct wcd_spi_ac_priv *ac;
|
||||
|
||||
ac = dev_get_drvdata(&pdev->dev);
|
||||
qmi_svc_event_notifier_unregister(
|
||||
WCD_SPI_CTL_SERVICE_ID_V01,
|
||||
WCD_SPI_CTL_SERVICE_VERS_V01,
|
||||
WCD_SPI_CTL_INS_ID,
|
||||
&ac->nb);
|
||||
if (ac->qmi_wq)
|
||||
destroy_workqueue(ac->qmi_wq);
|
||||
wcd_spi_ac_unreg_chardev(ac);
|
||||
wcd_spi_ac_procfs_deinit(ac);
|
||||
mutex_destroy(&ac->status_lock);
|
||||
mutex_destroy(&ac->state_lock);
|
||||
mutex_destroy(&ac->svc_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id wcd_spi_ac_of_match[] = {
|
||||
{ .compatible = "qcom,wcd-spi-ac" },
|
||||
{ },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, wcd_spi_ac_of_match);
|
||||
|
||||
static struct platform_driver wcd_spi_ac_driver = {
|
||||
.driver = {
|
||||
.name = "qcom,wcd-spi-ac",
|
||||
.of_match_table = wcd_spi_ac_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = wcd_spi_ac_probe,
|
||||
.remove = wcd_spi_ac_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(wcd_spi_ac_driver);
|
||||
|
||||
MODULE_DESCRIPTION("WCD SPI access control driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
129
qcom/opensource/audio-kernel/soc/wcd_spi_ctl_v01.c
Normal file
129
qcom/opensource/audio-kernel/soc/wcd_spi_ctl_v01.c
Normal file
@@ -0,0 +1,129 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
#include <linux/qmi_encdec.h>
|
||||
#include <soc/qcom/msm_qmi_interface.h>
|
||||
#include "wcd_spi_ctl_v01.h"
|
||||
|
||||
struct elem_info wcd_spi_req_access_msg_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_OPT_FLAG,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(u8),
|
||||
.is_array = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(struct wcd_spi_req_access_msg_v01,
|
||||
reason_valid),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_8_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(u64),
|
||||
.is_array = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(struct wcd_spi_req_access_msg_v01,
|
||||
reason),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.is_array = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct elem_info wcd_spi_req_access_resp_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(struct qmi_response_type_v01),
|
||||
.is_array = NO_ARRAY,
|
||||
.tlv_type = 0x02,
|
||||
.offset = offsetof(struct wcd_spi_req_access_resp_v01,
|
||||
resp),
|
||||
.ei_array = get_qmi_response_type_v01_ei(),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.is_array = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct elem_info wcd_spi_rel_access_msg_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.is_array = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct elem_info wcd_spi_rel_access_resp_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(struct qmi_response_type_v01),
|
||||
.is_array = NO_ARRAY,
|
||||
.tlv_type = 0x02,
|
||||
.offset = offsetof(struct wcd_spi_rel_access_resp_v01,
|
||||
resp),
|
||||
.ei_array = get_qmi_response_type_v01_ei(),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.is_array = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct elem_info wcd_spi_buff_msg_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_4_BYTE,
|
||||
.elem_len = WCD_SPI_BUFF_CHANNELS_MAX_V01,
|
||||
.elem_size = sizeof(u32),
|
||||
.is_array = STATIC_ARRAY,
|
||||
.tlv_type = 0x01,
|
||||
.offset = offsetof(struct wcd_spi_buff_msg_v01,
|
||||
buff_addr_1),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_OPT_FLAG,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(u8),
|
||||
.is_array = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(struct wcd_spi_buff_msg_v01,
|
||||
buff_addr_2_valid),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_4_BYTE,
|
||||
.elem_len = WCD_SPI_BUFF_CHANNELS_MAX_V01,
|
||||
.elem_size = sizeof(u32),
|
||||
.is_array = STATIC_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(struct wcd_spi_buff_msg_v01,
|
||||
buff_addr_2),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.is_array = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct elem_info wcd_spi_buff_resp_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(struct qmi_response_type_v01),
|
||||
.is_array = NO_ARRAY,
|
||||
.tlv_type = 0x02,
|
||||
.offset = offsetof(struct wcd_spi_buff_resp_v01,
|
||||
resp),
|
||||
.ei_array = get_qmi_response_type_v01_ei(),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.is_array = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
62
qcom/opensource/audio-kernel/soc/wcd_spi_ctl_v01.h
Normal file
62
qcom/opensource/audio-kernel/soc/wcd_spi_ctl_v01.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
#ifndef WCD_SPI_CTL_V01_H
|
||||
#define WCD_SPI_CTL_V01_H
|
||||
|
||||
#define WCD_SPI_CTL_SERVICE_ID_V01 0x421
|
||||
#define WCD_SPI_CTL_SERVICE_VERS_V01 0x01
|
||||
|
||||
#define WCD_SPI_BUFF_RESP_V01 0x0022
|
||||
#define WCD_SPI_REL_ACCESS_RESP_V01 0x0021
|
||||
#define WCD_SPI_REQ_ACCESS_MSG_V01 0x0020
|
||||
#define WCD_SPI_BUFF_MSG_V01 0x0022
|
||||
#define WCD_SPI_REL_ACCESS_MSG_V01 0x0021
|
||||
#define WCD_SPI_REQ_ACCESS_RESP_V01 0x0020
|
||||
|
||||
#define WCD_SPI_BUFF_CHANNELS_MAX_V01 0x08
|
||||
|
||||
#define WCD_SPI_REQ_DATA_TRANSFER_V01 ((u64)0x01ULL)
|
||||
#define WCD_SPI_REQ_CONCURRENCY_V01 ((u64)0x02ULL)
|
||||
#define WCD_SPI_REQ_REMOTE_DOWN_V01 ((u64)0x04ULL)
|
||||
|
||||
struct wcd_spi_req_access_msg_v01 {
|
||||
u8 reason_valid;
|
||||
u64 reason;
|
||||
};
|
||||
#define WCD_SPI_REQ_ACCESS_MSG_V01_MAX_MSG_LEN 11
|
||||
extern struct elem_info wcd_spi_req_access_msg_v01_ei[];
|
||||
|
||||
struct wcd_spi_req_access_resp_v01 {
|
||||
struct qmi_response_type_v01 resp;
|
||||
};
|
||||
#define WCD_SPI_REQ_ACCESS_RESP_V01_MAX_MSG_LEN 7
|
||||
extern struct elem_info wcd_spi_req_access_resp_v01_ei[];
|
||||
|
||||
struct wcd_spi_rel_access_msg_v01 {
|
||||
char placeholder;
|
||||
};
|
||||
#define WCD_SPI_REL_ACCESS_MSG_V01_MAX_MSG_LEN 0
|
||||
extern struct elem_info wcd_spi_rel_access_msg_v01_ei[];
|
||||
|
||||
struct wcd_spi_rel_access_resp_v01 {
|
||||
struct qmi_response_type_v01 resp;
|
||||
};
|
||||
#define WCD_SPI_REL_ACCESS_RESP_V01_MAX_MSG_LEN 7
|
||||
extern struct elem_info wcd_spi_rel_access_resp_v01_ei[];
|
||||
|
||||
struct wcd_spi_buff_msg_v01 {
|
||||
u32 buff_addr_1[WCD_SPI_BUFF_CHANNELS_MAX_V01];
|
||||
u8 buff_addr_2_valid;
|
||||
u32 buff_addr_2[WCD_SPI_BUFF_CHANNELS_MAX_V01];
|
||||
};
|
||||
#define WCD_SPI_BUFF_MSG_V01_MAX_MSG_LEN 70
|
||||
extern struct elem_info wcd_spi_buff_msg_v01_ei[];
|
||||
|
||||
struct wcd_spi_buff_resp_v01 {
|
||||
struct qmi_response_type_v01 resp;
|
||||
};
|
||||
#define WCD_SPI_BUFF_RESP_V01_MAX_MSG_LEN 7
|
||||
extern struct elem_info wcd_spi_buff_resp_v01_ei[];
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user