sm8750: init kernel modules repo

This commit is contained in:
2025-08-11 12:21:01 +02:00
parent 2681143b87
commit facad83b01
8851 changed files with 6894561 additions and 0 deletions

View 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')\"

View 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

View 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_])

File diff suppressed because it is too large Load Diff

View 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__ */

View 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");

View 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, &reg_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, &reg_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, &regmap_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, &regmap_swr, &swr->dev, config,
lock_key, lock_name);
}
EXPORT_SYMBOL(__devm_regmap_init_swr);
MODULE_LICENSE("GPL v2");

View 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");

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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 */

View 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 */

View 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 */

File diff suppressed because it is too large Load Diff

View 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 */

View 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 */

View 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");

View 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,
},
};

View 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