Add 'qcom/opensource/bt-kernel/' from commit 'abeb53d57fb210fc51839143b6ce9292c595c424'
git-subtree-dir: qcom/opensource/bt-kernel git-subtree-mainline:91a8910061
git-subtree-split:abeb53d57f
Change-Id: repo: https://git.codelinaro.org/clo/la/platform/vendor/qcom-opensource/bt-kernel tag: LA.VENDOR.14.3.0.r1-17300-lanai.QSSI15.0
This commit is contained in:
commit
7870029999
103
qcom/opensource/bt-kernel/Android.mk
Normal file
103
qcom/opensource/bt-kernel/Android.mk
Normal file
@ -0,0 +1,103 @@
|
||||
# Android makefile for BT kernel modules
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
# Build/Package only in case of supported target
|
||||
ifeq ($(call is-board-platform-in-list,taro kalama pineapple blair pitti volcano niobe anorak61), true)
|
||||
|
||||
BT_SELECT := CONFIG_MSM_BT_POWER=m
|
||||
#ifdef CONFIG_SLIMBUS
|
||||
BT_SELECT += CONFIG_BTFM_SLIM=m
|
||||
#endif
|
||||
BT_SELECT += CONFIG_I2C_RTC6226_QCA=m
|
||||
|
||||
ifeq ($(TARGET_KERNEL_DLKM_SECURE_MSM_OVERRIDE), true)
|
||||
ifeq ($(ENABLE_PERIPHERAL_STATE_UTILS), true)
|
||||
BT_SELECT += CONFIG_BT_HW_SECURE_DISABLE=y
|
||||
endif
|
||||
endif
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
LOCAL_MODULE_DDK_BUILD := true
|
||||
LOCAL_MODULE_KO_DIRS := pwr/btpower.ko
|
||||
LOCAL_MODULE_KO_DIRS += slimbus/bt_fm_slim.ko
|
||||
LOCAL_MODULE_KO_DIRS += rtc6226/radio-i2c-rtc6226-qca.ko
|
||||
|
||||
|
||||
# This makefile is only for DLKM
|
||||
ifneq ($(findstring vendor,$(LOCAL_PATH)),)
|
||||
|
||||
ifneq ($(findstring opensource,$(LOCAL_PATH)),)
|
||||
BT_BLD_DIR := $(abspath .)/vendor/qcom/opensource/bt-kernel
|
||||
endif # opensource
|
||||
|
||||
DLKM_DIR := $(TOP)/device/qcom/common/dlkm
|
||||
|
||||
|
||||
###########################################################
|
||||
# This is set once per LOCAL_PATH, not per (kernel) module
|
||||
KBUILD_OPTIONS := BT_KERNEL_ROOT=$(BT_BLD_DIR)
|
||||
KBUILD_OPTIONS += $(foreach bt_select, \
|
||||
$(BT_SELECT), \
|
||||
$(bt_select))
|
||||
BT_SRC_FILES := \
|
||||
$(wildcard $(LOCAL_PATH)/*) \
|
||||
$(wildcard $(LOCAL_PATH)/*/*) \
|
||||
|
||||
ifeq ($(TARGET_KERNEL_DLKM_SECURE_MSM_OVERRIDE), true)
|
||||
ifeq ($(ENABLE_PERIPHERAL_STATE_UTILS), true)
|
||||
KBUILD_REQUIRED_KOS := smcinvoke_dlkm.ko
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
# Module.symvers needs to be generated as a intermediate module so that
|
||||
# other modules which depend on BT platform modules can set local
|
||||
# dependencies to it.
|
||||
|
||||
########################### Module.symvers ############################
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := $(BT_SRC_FILES)
|
||||
LOCAL_MODULE := bt-kernel-module-symvers
|
||||
LOCAL_MODULE_STEM := Module.symvers
|
||||
LOCAL_MODULE_KBUILD_NAME := Module.symvers
|
||||
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
|
||||
include $(DLKM_DIR)/Build_external_kernelmodule.mk
|
||||
|
||||
# Below are for Android build system to recognize each module name, so
|
||||
# they can be installed properly. Since Kbuild is used to compile these
|
||||
# modules, invoking any of them will cause other modules to be compiled
|
||||
# as well if corresponding flags are added in KBUILD_OPTIONS from upper
|
||||
# level Makefiles.
|
||||
|
||||
################################ pwr ################################
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := $(BT_SRC_FILES)
|
||||
LOCAL_MODULE := btpower.ko
|
||||
LOCAL_MODULE_KBUILD_NAME := pwr/btpower.ko
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_DEBUG_ENABLE := true
|
||||
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
|
||||
include $(DLKM_DIR)/Build_external_kernelmodule.mk
|
||||
################################ slimbus ################################
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := $(BT_SRC_FILES)
|
||||
LOCAL_MODULE := bt_fm_slim.ko
|
||||
LOCAL_MODULE_KBUILD_NAME := slimbus/bt_fm_slim.ko
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_DEBUG_ENABLE := true
|
||||
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
|
||||
include $(DLKM_DIR)/Build_external_kernelmodule.mk
|
||||
################################ rtc6226 ################################
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := $(BT_SRC_FILES)
|
||||
LOCAL_MODULE := radio-i2c-rtc6226-qca.ko
|
||||
LOCAL_MODULE_KBUILD_NAME := rtc6226/radio-i2c-rtc6226-qca.ko
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_DEBUG_ENABLE := true
|
||||
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
|
||||
include $(DLKM_DIR)/Build_external_kernelmodule.mk
|
||||
###########################################################
|
||||
|
||||
endif # DLKM check
|
||||
endif # supported target check
|
17
qcom/opensource/bt-kernel/BUILD.bazel
Normal file
17
qcom/opensource/bt-kernel/BUILD.bazel
Normal file
@ -0,0 +1,17 @@
|
||||
load("//build/kernel/kleaf:kernel.bzl", "ddk_headers")
|
||||
|
||||
ddk_headers(
|
||||
name = "btfmcodec_headers",
|
||||
hdrs = glob([
|
||||
"btfmcodec/include/*.h"
|
||||
]),
|
||||
includes = ["btfmcodec/include"]
|
||||
)
|
||||
|
||||
load(":target.bzl", "define_pineapple")
|
||||
|
||||
define_pineapple()
|
||||
|
||||
load(":target.bzl", "define_anorak61")
|
||||
|
||||
define_anorak61()
|
25
qcom/opensource/bt-kernel/Kbuild
Normal file
25
qcom/opensource/bt-kernel/Kbuild
Normal file
@ -0,0 +1,25 @@
|
||||
ifeq ($(CONFIG_MSM_BT_POWER),m)
|
||||
KBUILD_CPPFLAGS += -DCONFIG_MSM_BT_POWER
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_BTFM_SLIM),m)
|
||||
KBUILD_CPPFLAGS += -DCONFIG_BTFM_SLIM
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_I2C_RTC6226_QCA),m)
|
||||
KBUILD_CPPFLAGS += -DCONFIG_I2C_RTC6226_QCA
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_SLIM_BTFM_CODEC), m)
|
||||
KBUILD_CPPFLAGS += -DCONFIG_SLIM_BTFM_CODEC
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_BT_HW_SECURE_DISABLE), y)
|
||||
KBUILD_CPPFLAGS += -DCONFIG_BT_HW_SECURE_DISABLE
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_MSM_BT_POWER) += pwr/
|
||||
obj-$(CONFIG_BTFM_SLIM) += slimbus/
|
||||
obj-$(CONFIG_I2C_RTC6226_QCA) += rtc6226/
|
||||
obj-$(CONFIG_BTFM_CODEC) += btfmcodec/
|
||||
obj-$(CONFIG_SLIM_BTFM_CODEC) += slimbus/
|
16
qcom/opensource/bt-kernel/Makefile
Normal file
16
qcom/opensource/bt-kernel/Makefile
Normal file
@ -0,0 +1,16 @@
|
||||
KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build
|
||||
M ?= $(shell pwd)
|
||||
|
||||
M=$(PWD)
|
||||
BT_ROOT=$(KERNEL_SRC)/$(M)
|
||||
|
||||
KBUILD_OPTIONS+= BT_ROOT=$(BT_ROOT)
|
||||
|
||||
all:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(M) modules $(KBUILD_OPTIONS)
|
||||
|
||||
modules_install:
|
||||
$(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(M) modules_install
|
||||
|
||||
clean:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(M) clean
|
95
qcom/opensource/bt-kernel/bt_kernel.bzl
Normal file
95
qcom/opensource/bt-kernel/bt_kernel.bzl
Normal file
@ -0,0 +1,95 @@
|
||||
load("//msm-kernel:target_variants.bzl", "get_all_variants")
|
||||
load("//build/kernel/kleaf:kernel.bzl", "ddk_module")
|
||||
load("//build/bazel_common_rules/dist:dist.bzl", "copy_to_dist_dir")
|
||||
load(":bt_modules.bzl", "bt_modules")
|
||||
|
||||
def _get_config_choices(config_srcs, options):
|
||||
choices = []
|
||||
|
||||
for option in config_srcs:
|
||||
choices.extend(config_srcs[option].get(option in options, []))
|
||||
|
||||
return choices
|
||||
|
||||
def _get_module_srcs(module, options):
|
||||
"""
|
||||
Gets all the module sources, formats them with the path for that module
|
||||
and then groups them together
|
||||
It also includes all the headers within the `include` directory
|
||||
`native.glob()` returns a new list with every file need for the current package
|
||||
"""
|
||||
srcs = module.srcs + _get_config_choices(module.config_srcs, options)
|
||||
return native.glob(
|
||||
["{}/{}".format(module.path, src) for src in srcs] + ["include/*.h"]
|
||||
)
|
||||
|
||||
def _get_module_deps(module, options, formatter):
|
||||
"""
|
||||
Formats the dependent targets with the necessary prefix
|
||||
Args:
|
||||
module: kernel module
|
||||
options: dependencies that rely on a config option
|
||||
formatter: function that will replace the format string within `deps`
|
||||
Example:
|
||||
kernel build = "pineapple_gki"
|
||||
dep = "%b_btpower"
|
||||
The formatted string will look as follow
|
||||
formatted_dep = formatter(dep) = "pineapple_gki_btpower"
|
||||
"""
|
||||
deps = module.deps + _get_config_choices(module.config_deps, options)
|
||||
return [formatter(dep) for dep in deps]
|
||||
|
||||
def _get_build_options(modules, config_options):
|
||||
all_options = {option: True for option in config_options}
|
||||
all_options = all_options | {module.config_opt: True for module in modules if module.config_opt}
|
||||
|
||||
return all_options
|
||||
|
||||
def define_target_variant_modules(target, variant, modules, config_options = []):
|
||||
"""
|
||||
Generates the ddk_module for each of our kernel modules
|
||||
Args:
|
||||
target: either `pineapple` or `kalama`
|
||||
variant: either `gki` or `consolidate`
|
||||
modules: bt_modules dictionary defined in `bt_modules.bzl`
|
||||
config_options: decides which kernel modules to build
|
||||
"""
|
||||
kernel_build = "{}_{}".format(target, variant)
|
||||
kernel_build_label = "//msm-kernel:{}".format(kernel_build)
|
||||
modules = [bt_modules.get(module_name) for module_name in modules]
|
||||
options = _get_build_options(modules, config_options)
|
||||
formatter = lambda s : s.replace("%b", kernel_build)
|
||||
|
||||
all_modules = []
|
||||
for module in modules:
|
||||
rule_name = "{}_{}".format(kernel_build, module.name)
|
||||
module_srcs = _get_module_srcs(module, options)
|
||||
|
||||
ddk_module(
|
||||
name = rule_name,
|
||||
kernel_build = kernel_build_label,
|
||||
srcs = module_srcs,
|
||||
out = "{}.ko".format(module.name),
|
||||
deps = ["//msm-kernel:all_headers"] + _get_module_deps(module, options, formatter),
|
||||
includes = ["include"],
|
||||
local_defines = options.keys(),
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
all_modules.append(rule_name)
|
||||
|
||||
copy_to_dist_dir(
|
||||
name = "{}_bt-kernel_dist".format(kernel_build),
|
||||
data = all_modules,
|
||||
dist_dir = "out/target/product/{}/dlkm/lib/modules".format(target),
|
||||
flat = True,
|
||||
wipe_dist_dir = False,
|
||||
allow_duplicate_filenames = False,
|
||||
mode_overrides = {"**/*": "644"},
|
||||
log = "info",
|
||||
)
|
||||
|
||||
def define_bt_modules(target, modules, config_options = []):
|
||||
for (t, v) in get_all_variants():
|
||||
if t == target:
|
||||
define_target_variant_modules(t, v, modules, config_options)
|
4
qcom/opensource/bt-kernel/bt_kernel_product_board.mk
Normal file
4
qcom/opensource/bt-kernel/bt_kernel_product_board.mk
Normal file
@ -0,0 +1,4 @@
|
||||
# Build BT kernel drivers
|
||||
PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/btpower.ko\
|
||||
$(KERNEL_MODULES_OUT)/bt_fm_slim.ko \
|
||||
$(KERNEL_MODULES_OUT)/radio-i2c-rtc6226-qca.ko
|
20
qcom/opensource/bt-kernel/bt_kernel_vendor_board.mk
Normal file
20
qcom/opensource/bt-kernel/bt_kernel_vendor_board.mk
Normal file
@ -0,0 +1,20 @@
|
||||
# Build audio kernel driver
|
||||
ifneq ($(TARGET_BOARD_AUTO),true)
|
||||
ifeq ($(TARGET_USES_QMAA),true)
|
||||
ifeq ($(TARGET_USES_QMAA_OVERRIDE_BLUETOOTH), true)
|
||||
ifeq ($(call is-board-platform-in-list,$(TARGET_BOARD_PLATFORM)),true)
|
||||
BT_KERNEL_DRIVER := $(KERNEL_MODULES_OUT)/btpower.ko\
|
||||
$(KERNEL_MODULES_OUT)/bt_fm_slim.ko \
|
||||
$(KERNEL_MODULES_OUT)/radio-i2c-rtc6226-qca.ko
|
||||
BOARD_VENDOR_KERNEL_MODULES += $(BT_KERNEL_DRIVER)
|
||||
endif
|
||||
endif
|
||||
else
|
||||
ifeq ($(call is-board-platform-in-list,$(TARGET_BOARD_PLATFORM)),true)
|
||||
BT_KERNEL_DRIVER := $(KERNEL_MODULES_OUT)/btpower.ko\
|
||||
$(KERNEL_MODULES_OUT)/bt_fm_slim.ko \
|
||||
$(KERNEL_MODULES_OUT)/radio-i2c-rtc6226-qca.ko
|
||||
BOARD_VENDOR_KERNEL_MODULES += $(BT_KERNEL_DRIVER)
|
||||
endif
|
||||
endif
|
||||
endif
|
116
qcom/opensource/bt-kernel/bt_modules.bzl
Normal file
116
qcom/opensource/bt-kernel/bt_modules.bzl
Normal file
@ -0,0 +1,116 @@
|
||||
PWR_PATH = "pwr"
|
||||
SLIMBUS_PATH = "slimbus"
|
||||
FMRTC_PATH = "rtc6226"
|
||||
BTFMCODEC_PATH = "btfmcodec"
|
||||
|
||||
# This dictionary holds all the BT modules included in the bt-kernel
|
||||
bt_modules = {}
|
||||
|
||||
def register_bt_modules(name, path = None, config_opt = None, srcs = [], config_srcs = {}, deps = [], config_deps = {}):
|
||||
"""
|
||||
Register modules
|
||||
Args:
|
||||
name: Name of the module (which will be used to generate the name of the .ko file)
|
||||
path: Path in which the source files can be found
|
||||
config_opt: Config name used in Kconfig (not needed currently)
|
||||
srcs: source files and local headers
|
||||
config_srcs: source files and local headers that depend on a config define being enabled.
|
||||
deps: a list of dependent targets
|
||||
config_deps: a list of dependent targets that depend on a config define being enabled.
|
||||
"""
|
||||
processed_config_srcs = {}
|
||||
processed_config_deps = {}
|
||||
|
||||
for config_src_name in config_srcs:
|
||||
config_src = config_srcs[config_src_name]
|
||||
|
||||
if type(config_src) == "list":
|
||||
processed_config_srcs[config_src_name] = {True: config_src}
|
||||
else:
|
||||
processed_config_srcs[config_src_name] = config_src
|
||||
|
||||
for config_deps_name in config_deps:
|
||||
config_dep = config_deps[config_deps_name]
|
||||
|
||||
if type(config_dep) == "list":
|
||||
processed_config_deps[config_deps_name] = {True: config_dep}
|
||||
else:
|
||||
processed_config_deps[config_deps_name] = config_dep
|
||||
|
||||
module = struct(
|
||||
name = name,
|
||||
path = path,
|
||||
srcs = srcs,
|
||||
config_srcs = processed_config_srcs,
|
||||
config_opt = config_opt,
|
||||
deps = deps,
|
||||
config_deps = processed_config_deps,
|
||||
)
|
||||
bt_modules[name] = module
|
||||
|
||||
# --- BT Modules ---
|
||||
|
||||
register_bt_modules(
|
||||
name = "btpower",
|
||||
path = PWR_PATH,
|
||||
config_opt = "CONFIG_MSM_BT_POWER",
|
||||
srcs = ["btpower.c"],
|
||||
config_deps = {
|
||||
"CONFIG_BT_HW_SECURE_DISABLE": [
|
||||
"//vendor/qcom/opensource/securemsm-kernel:%b_smcinvoke_dlkm",
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
register_bt_modules(
|
||||
name = "bt_fm_slim",
|
||||
path = SLIMBUS_PATH,
|
||||
# config_opt = "CONFIG_BTFM_SLIM",
|
||||
srcs = [
|
||||
"btfm_slim.c",
|
||||
"btfm_slim.h",
|
||||
"btfm_slim_slave.c",
|
||||
"btfm_slim_slave.h",
|
||||
"btfm_slim_codec.c",
|
||||
],
|
||||
deps = [":%b_btpower"],
|
||||
)
|
||||
|
||||
register_bt_modules(
|
||||
name = "btfm_slim_codec",
|
||||
path = SLIMBUS_PATH,
|
||||
config_opt = "CONFIG_SLIM_BTFM_CODEC",
|
||||
srcs = [
|
||||
"btfm_slim.c",
|
||||
"btfm_slim.h",
|
||||
"btfm_slim_slave.c",
|
||||
"btfm_slim_slave.h",
|
||||
"btfm_slim_hw_interface.c",
|
||||
"btfm_slim_hw_interface.h",
|
||||
],
|
||||
deps = [":%b_btpower", ":%b_btfmcodec", ":btfmcodec_headers"],
|
||||
)
|
||||
|
||||
register_bt_modules(
|
||||
name = "btfmcodec",
|
||||
path = BTFMCODEC_PATH,
|
||||
config_opt = "CONFIG_BTFM_CODEC",
|
||||
srcs = [
|
||||
"btfm_codec.c",
|
||||
"btfm_codec_btadv_interface.c",
|
||||
"btfm_codec_hw_interface.c",
|
||||
"btfm_codec_interface.c",
|
||||
],
|
||||
deps = [":btfmcodec_headers"],
|
||||
)
|
||||
|
||||
register_bt_modules(
|
||||
name = "radio-i2c-rtc6226-qca",
|
||||
path = FMRTC_PATH,
|
||||
config_opt = "CONFIG_I2C_RTC6226_QCA",
|
||||
srcs = [
|
||||
"radio-rtc6226-common.c",
|
||||
"radio-rtc6226-i2c.c",
|
||||
"radio-rtc6226.h",
|
||||
],
|
||||
)
|
10
qcom/opensource/bt-kernel/btfmcodec/Kconfig
Normal file
10
qcom/opensource/bt-kernel/btfmcodec/Kconfig
Normal file
@ -0,0 +1,10 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config BTFM_CODEC
|
||||
tristate "MSM Bluetooth/FM CODEC Driver"
|
||||
help
|
||||
This will enables BT/FM Codec driver. Hardware endpoint
|
||||
drivers will register to this driver to communicates with ALSA codec
|
||||
driver.
|
||||
|
||||
Say Y here to compile support for BT/FM Codec driver
|
||||
into the kernel or say M to compile as a module.
|
4
qcom/opensource/bt-kernel/btfmcodec/Makefile
Normal file
4
qcom/opensource/bt-kernel/btfmcodec/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
ccflags-y += -I$(BT_ROOT)/include
|
||||
ccflags-y += -I$(BT_ROOT)/btfmcodec/include
|
||||
btfmcodec-objs := btfm_codec.o btfm_codec_hw_interface.o btfm_codec_interface.o btfm_codec_btadv_interface.o
|
||||
obj-$(CONFIG_BTFM_CODEC) += btfmcodec.o
|
731
qcom/opensource/bt-kernel/btfmcodec/btfm_codec.c
Normal file
731
qcom/opensource/bt-kernel/btfmcodec/btfm_codec.c
Normal file
@ -0,0 +1,731 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kdev_t.h>
|
||||
#include <linux/refcount.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/module.h>
|
||||
#include "btfm_codec.h"
|
||||
#include "btfm_codec_pkt.h"
|
||||
|
||||
#define dev_to_btfmcodec(_dev) container_of(_dev, struct btfmcodec_data, dev)
|
||||
|
||||
static DEFINE_IDR(dev_minor);
|
||||
static struct class *dev_class;
|
||||
static dev_t dev_major;
|
||||
struct btfmcodec_data *btfmcodec;
|
||||
struct device_driver driver = {.name = "btfmcodec-driver", .owner = THIS_MODULE};
|
||||
struct btfmcodec_char_device *btfmcodec_dev;
|
||||
bool is_cp_supported = true;
|
||||
|
||||
#define cdev_to_btfmchardev(_cdev) container_of(_cdev, struct btfmcodec_char_device, cdev)
|
||||
#define MIN_PKT_LEN 0x9
|
||||
|
||||
char *coverttostring(enum btfmcodec_states state) {
|
||||
switch (state) {
|
||||
case IDLE:
|
||||
return "IDLE";
|
||||
break;
|
||||
case BT_Connected:
|
||||
return "BT_CONNECTED";
|
||||
break;
|
||||
case BT_Connecting:
|
||||
return "BT_CONNECTING";
|
||||
break;
|
||||
case BTADV_AUDIO_Connected:
|
||||
return "BTADV_AUDIO_CONNECTED";
|
||||
break;
|
||||
case BTADV_AUDIO_Connecting:
|
||||
return "BTADV_AUDIO_CONNECTING";
|
||||
break;
|
||||
default:
|
||||
return "INVALID_STATE";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* btfmcodec_dev_open() - open() syscall for the btfmcodec dev node
|
||||
* inode: Pointer to the inode structure.
|
||||
* file: Pointer to the file structure.
|
||||
*
|
||||
* This function is used to open the btfmcodec char device when
|
||||
* userspace client do a open() system call. All input arguments are
|
||||
* validated by the virtual file system before calling this function.
|
||||
* Note: btfmcodec dev node works on nonblocking mode.
|
||||
*/
|
||||
static int btfmcodec_dev_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct btfmcodec_char_device *btfmcodec_dev = cdev_to_btfmchardev(inode->i_cdev);
|
||||
struct btfmcodec_data *btfmcodec = (struct btfmcodec_data *)btfmcodec_dev->btfmcodec;
|
||||
unsigned int active_clients = refcount_read(&btfmcodec_dev->active_clients);
|
||||
|
||||
btfmcodec->states.current_state = IDLE; /* Just a temp*/
|
||||
btfmcodec->states.next_state = IDLE;
|
||||
BTFMCODEC_INFO("for %s by %s:%d active_clients[%d]\n",
|
||||
btfmcodec_dev->dev_name, current->comm,
|
||||
task_pid_nr(current), refcount_read(&btfmcodec_dev->active_clients));
|
||||
/* Don't allow a new client if already one is active. */
|
||||
if (active_clients > 1) {
|
||||
BTFMCODEC_WARN("%s: Not honoring open as other client is active", __func__);
|
||||
return EACCES;
|
||||
}
|
||||
/* for now have btfmcodec and later we can think of having it btfmcodec_dev */
|
||||
file->private_data = btfmcodec;
|
||||
refcount_inc(&btfmcodec_dev->active_clients);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* btfmcodec_pkt_release() - release operation on btfmcodec device
|
||||
* inode: Pointer to the inode structure.
|
||||
* file: Pointer to the file structure.
|
||||
*
|
||||
* This function is used to release the btfmcodec dev node when
|
||||
* userspace client do a close() system call. All input arguments are
|
||||
* validated by the virtual file system before calling this function.
|
||||
*/
|
||||
static int btfmcodec_dev_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct btfmcodec_char_device *btfmcodec_dev = cdev_to_btfmchardev(inode->i_cdev);
|
||||
unsigned long flags;
|
||||
int idx;
|
||||
|
||||
BTFMCODEC_INFO("for %s by %s:%d active_clients[%u]\n",
|
||||
btfmcodec_dev->dev_name, current->comm,
|
||||
task_pid_nr(current), refcount_read(&btfmcodec_dev->active_clients));
|
||||
|
||||
refcount_dec(&btfmcodec_dev->active_clients);
|
||||
if (refcount_read(&btfmcodec_dev->active_clients) == 1) {
|
||||
spin_lock_irqsave(&btfmcodec_dev->tx_queue_lock, flags);
|
||||
skb_queue_purge(&btfmcodec_dev->txq);
|
||||
/* Wakeup the device if waiting for the data */
|
||||
wake_up_interruptible(&btfmcodec_dev->readq);
|
||||
spin_unlock_irqrestore(&btfmcodec_dev->tx_queue_lock, flags);
|
||||
/* we need to have separte rx lock for below buff */
|
||||
skb_queue_purge(&btfmcodec_dev->rxq);
|
||||
}
|
||||
|
||||
/* Notify waiting clients that client is closed or killed */
|
||||
for (idx = 0; idx < BTM_PKT_TYPE_MAX; idx++) {
|
||||
btfmcodec_dev->status[idx] = BTM_RSP_NOT_RECV_CLIENT_KILLED;
|
||||
wake_up_interruptible(&btfmcodec_dev->rsp_wait_q[idx]);
|
||||
}
|
||||
|
||||
if (btfmcodec_dev->wq_hwep_shutdown.func)
|
||||
cancel_work_sync(&btfmcodec_dev->wq_hwep_shutdown);
|
||||
if (btfmcodec_dev->wq_hwep_configure.func)
|
||||
cancel_work_sync(&btfmcodec_dev->wq_hwep_configure);
|
||||
if (btfmcodec_dev->wq_prepare_bearer.func)
|
||||
cancel_work_sync(&btfmcodec_dev->wq_prepare_bearer);
|
||||
|
||||
btfmcodec->states.current_state = IDLE;
|
||||
btfmcodec->states.next_state = IDLE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
btm_opcode STREAM_TO_UINT32 (struct sk_buff *skb)
|
||||
{
|
||||
return (skb->data[0] | (skb->data[1] << 8) |
|
||||
(skb->data[2] << 16) | (skb->data[3] << 24));
|
||||
}
|
||||
|
||||
static void btfmcodec_dev_rxwork(struct work_struct *work)
|
||||
{
|
||||
struct btfmcodec_char_device *btfmcodec_dev = container_of(work, struct btfmcodec_char_device, rx_work);
|
||||
struct sk_buff *skb;
|
||||
uint32_t len;
|
||||
uint8_t status;
|
||||
int idx;
|
||||
uint8_t *bearer_switch_ind;
|
||||
|
||||
BTFMCODEC_DBG("start");
|
||||
while ((skb = skb_dequeue(&btfmcodec_dev->rxq))) {
|
||||
btm_opcode opcode = STREAM_TO_UINT32(skb);
|
||||
skb_pull(skb, sizeof(btm_opcode));
|
||||
len = STREAM_TO_UINT32(skb);
|
||||
skb_pull(skb, sizeof(len));
|
||||
switch (opcode) {
|
||||
case BTM_BTFMCODEC_PREPARE_AUDIO_BEARER_SWITCH_REQ:
|
||||
idx = BTM_PKT_TYPE_PREPARE_REQ;
|
||||
BTFMCODEC_DBG("BTM_BTFMCODEC_PREPARE_AUDIO_BEARER_SWITCH_REQ");
|
||||
if (len == BTM_PREPARE_AUDIO_BEARER_SWITCH_REQ_LEN) {
|
||||
/* there are chances where bearer indication is not recevied,
|
||||
* So inform waiting thread to unblock itself and move to
|
||||
* previous state.
|
||||
*/
|
||||
if (btfmcodec_dev->status[BTM_PKT_TYPE_BEARER_SWITCH_IND] == BTM_WAITING_RSP) {
|
||||
BTFMCODEC_DBG("Notifying waiting beare indications");
|
||||
btfmcodec_dev->status[BTM_PKT_TYPE_BEARER_SWITCH_IND] = BTM_FAIL_RESP_RECV;
|
||||
wake_up_interruptible(&btfmcodec_dev->rsp_wait_q[BTM_PKT_TYPE_BEARER_SWITCH_IND]);
|
||||
}
|
||||
btfmcodec_dev->status[idx] = skb->data[0];
|
||||
/* Reset bearer switch ind flag */
|
||||
bearer_switch_ind =
|
||||
&btfmcodec_dev->status[BTM_PKT_TYPE_BEARER_SWITCH_IND];
|
||||
*bearer_switch_ind = BTM_WAITING_RSP;
|
||||
queue_work(btfmcodec_dev->workqueue, &btfmcodec_dev->wq_prepare_bearer);
|
||||
} else {
|
||||
BTFMCODEC_ERR("wrong packet format with len:%d", len);
|
||||
}
|
||||
break;
|
||||
case BTM_BTFMCODEC_MASTER_CONFIG_RSP:
|
||||
idx = BTM_PKT_TYPE_MASTER_CONFIG_RSP;
|
||||
if (len == BTM_MASTER_CONFIG_RSP_LEN) {
|
||||
status = skb->data[1];
|
||||
if (status == MSG_SUCCESS)
|
||||
btfmcodec_dev->status[idx] = BTM_RSP_RECV;
|
||||
else
|
||||
btfmcodec_dev->status[idx] = BTM_FAIL_RESP_RECV;
|
||||
} else {
|
||||
BTFMCODEC_ERR("wrong packet format with len:%d", len);
|
||||
btfmcodec_dev->status[idx] = BTM_FAIL_RESP_RECV;
|
||||
}
|
||||
BTFMCODEC_INFO("Rx BTM_BTFMCODEC_MASTER_CONFIG_RSP status:%d",
|
||||
status);
|
||||
wake_up_interruptible(&btfmcodec_dev->rsp_wait_q[idx]);
|
||||
break;
|
||||
case BTM_BTFMCODEC_CODEC_CONFIG_DMA_RSP:
|
||||
idx = BTM_PKT_TYPE_DMA_CONFIG_RSP;
|
||||
if (len == BTM_CODEC_CONFIG_DMA_RSP_LEN) {
|
||||
status = skb->data[1];
|
||||
if (status == MSG_SUCCESS)
|
||||
btfmcodec_dev->status[idx] = BTM_RSP_RECV;
|
||||
else
|
||||
btfmcodec_dev->status[idx] = BTM_FAIL_RESP_RECV;
|
||||
} else {
|
||||
BTFMCODEC_ERR("wrong packet format with len:%d", len);
|
||||
btfmcodec_dev->status[idx] = BTM_FAIL_RESP_RECV;
|
||||
}
|
||||
BTFMCODEC_INFO("Rx BTM_BTFMCODEC_CODEC_CONFIG_DMA_RSP status:%d",
|
||||
status);
|
||||
wake_up_interruptible(&btfmcodec_dev->rsp_wait_q[idx]);
|
||||
break;
|
||||
case BTM_BTFMCODEC_CTRL_MASTER_SHUTDOWN_RSP:
|
||||
idx = BTM_PKT_TYPE_MASTER_SHUTDOWN_RSP;
|
||||
if (len == BTM_MASTER_CONFIG_RSP_LEN) {
|
||||
status = skb->data[1];
|
||||
if (status == MSG_SUCCESS)
|
||||
btfmcodec_dev->status[idx] = BTM_RSP_RECV;
|
||||
else
|
||||
btfmcodec_dev->status[idx] = BTM_FAIL_RESP_RECV;
|
||||
} else {
|
||||
BTFMCODEC_ERR("wrong packet format with len:%d", len);
|
||||
btfmcodec_dev->status[idx] = BTM_FAIL_RESP_RECV;
|
||||
}
|
||||
BTFMCODEC_INFO("Rx BTM_BTFMCODEC_CTRL_MASTER_SHUTDOWN_RSP status:%d",
|
||||
status);
|
||||
wake_up_interruptible(&btfmcodec_dev->rsp_wait_q[idx]);
|
||||
BTFMCODEC_INFO("%s: waiting to cancel prepare bearer wq", __func__);
|
||||
cancel_work_sync(&btfmcodec_dev->wq_prepare_bearer);
|
||||
BTFMCODEC_INFO("%s: prepare bearer wq canceled", __func__);
|
||||
break;
|
||||
case BTM_BTFMCODEC_BEARER_SWITCH_IND:
|
||||
idx = BTM_PKT_TYPE_BEARER_SWITCH_IND;
|
||||
if (len == BTM_BEARER_SWITCH_IND_LEN) {
|
||||
status = skb->data[0];
|
||||
if (status == MSG_SUCCESS)
|
||||
btfmcodec_dev->status[idx] = BTM_RSP_RECV;
|
||||
else
|
||||
btfmcodec_dev->status[idx] = BTM_FAIL_RESP_RECV;
|
||||
} else {
|
||||
BTFMCODEC_ERR("wrong packet format with len:%d", len);
|
||||
btfmcodec_dev->status[idx] = BTM_FAIL_RESP_RECV;
|
||||
}
|
||||
BTFMCODEC_INFO("Rx BTM_BTFMCODEC_BEARER_SWITCH_IND status:%d",
|
||||
status);
|
||||
wake_up_interruptible(&btfmcodec_dev->rsp_wait_q[idx]);
|
||||
break;
|
||||
case BTM_BTFMCODEC_CTRL_LOG_LVL_IND:
|
||||
if (len == BTM_LOG_LVL_IND_LEN) {
|
||||
log_lvl = skb->data[0];
|
||||
} else {
|
||||
BTFMCODEC_ERR("wrong packet format with len:%d", len);
|
||||
}
|
||||
BTFMCODEC_INFO("Rx BTM_BTFMCODEC_CTRL_LOG_LVL_IND status:%d",
|
||||
log_lvl);
|
||||
wake_up_interruptible(&btfmcodec_dev->rsp_wait_q[idx]);
|
||||
break;
|
||||
default:
|
||||
BTFMCODEC_ERR("wrong opcode:%08x", opcode);
|
||||
}
|
||||
kfree_skb(skb);
|
||||
}
|
||||
BTFMCODEC_DBG("end");
|
||||
}
|
||||
|
||||
/*
|
||||
* btfmcodec_pkt_write() - write() syscall for the btfmcodec_pkt device
|
||||
* file: Pointer to the file structure.
|
||||
* buf: Pointer to the userspace buffer.
|
||||
* count: Number bytes to read from the file.
|
||||
* ppos: Pointer to the position into the file.
|
||||
*
|
||||
* This function is used to write the data to btfmcodec dev node when
|
||||
* userspace client do a write() system call. All input arguments are
|
||||
* validated by the virtual file system before calling this function.
|
||||
*/
|
||||
static ssize_t btfmcodec_dev_write(struct file *file,
|
||||
const char __user *buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct btfmcodec_data *btfmcodec = file->private_data;
|
||||
struct btfmcodec_char_device *btfmcodec_dev= NULL;
|
||||
struct sk_buff *skb;
|
||||
void *kbuf;
|
||||
int ret = 0;
|
||||
|
||||
if (!btfmcodec || !btfmcodec->btfmcodec_dev || refcount_read(&btfmcodec->btfmcodec_dev->active_clients) == 1) {
|
||||
BTFMCODEC_INFO("%s: %d\n", current->comm, task_pid_nr(current));
|
||||
return -EINVAL;
|
||||
} else {
|
||||
btfmcodec_dev = btfmcodec->btfmcodec_dev;
|
||||
}
|
||||
|
||||
if (mutex_lock_interruptible(&btfmcodec_dev->lock)) {
|
||||
ret = -ERESTARTSYS;
|
||||
goto free_kbuf;
|
||||
}
|
||||
|
||||
/* Hack for Now */
|
||||
if (count < MIN_PKT_LEN) {
|
||||
BTFMCODEC_ERR("minimum packet len should be greater than 3 bytes");
|
||||
goto free_kbuf;
|
||||
}
|
||||
if (refcount_read(&btfmcodec->btfmcodec_dev->active_clients) == 0) {
|
||||
BTFMCODEC_WARN("Client disconnected");
|
||||
ret = -ENETRESET;
|
||||
goto free_kbuf;
|
||||
}
|
||||
|
||||
BTFMCODEC_DBG("begin to %s buffer_size %zu\n", btfmcodec_dev->dev_name, count);
|
||||
kbuf = memdup_user(buf, count);
|
||||
if (IS_ERR(kbuf)) {
|
||||
ret = PTR_ERR(kbuf);
|
||||
goto free_kbuf;
|
||||
}
|
||||
|
||||
/* Check whether we need a dedicated chunk of memory for this driver */
|
||||
skb = alloc_skb(count* sizeof(size_t), GFP_KERNEL);
|
||||
if (!skb) {
|
||||
BTFMCODEC_ERR("failed to allocate memory for recevied packet");
|
||||
ret = -ENOMEM;
|
||||
goto free_kbuf;
|
||||
}
|
||||
|
||||
skb_put_data(skb, (uint8_t *)kbuf, count);
|
||||
skb_queue_tail(&btfmcodec_dev->rxq, skb);
|
||||
schedule_work(&btfmcodec_dev->rx_work);
|
||||
|
||||
kfree(kbuf);
|
||||
|
||||
free_kbuf:
|
||||
mutex_unlock(&btfmcodec_dev->lock);
|
||||
BTFMCODEC_DBG("finish to %s ret %d\n", btfmcodec_dev->dev_name, ret);
|
||||
return ret < 0 ? ret : count;
|
||||
}
|
||||
|
||||
int btfmcodec_dev_enqueue_pkt(struct btfmcodec_char_device *btfmcodec_dev, void *buf, int len)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
unsigned long flags;
|
||||
uint8_t *cmd = buf;
|
||||
|
||||
BTFMCODEC_DBG("start");
|
||||
spin_lock_irqsave(&btfmcodec_dev->tx_queue_lock, flags);
|
||||
if (refcount_read(&btfmcodec_dev->active_clients) == 1) {
|
||||
BTFMCODEC_WARN("no active clients discarding the packet");
|
||||
spin_unlock_irqrestore(&btfmcodec_dev->tx_queue_lock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
skb = alloc_skb(len, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
BTFMCODEC_ERR("failed to allocate memory");
|
||||
spin_unlock_irqrestore(&btfmcodec_dev->tx_queue_lock, flags);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
skb_put_data(skb, cmd, len);
|
||||
skb_queue_tail(&btfmcodec_dev->txq, skb);
|
||||
wake_up_interruptible(&btfmcodec_dev->readq);
|
||||
spin_unlock_irqrestore(&btfmcodec_dev->tx_queue_lock, flags);
|
||||
BTFMCODEC_DBG("end");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* btfmcodec_pkt_poll() - poll() syscall for the btfmcodec device
|
||||
* file: Pointer to the file structure.
|
||||
* wait: pointer to Poll table.
|
||||
*
|
||||
* This function is used to poll on the btfmcodec dev node when
|
||||
* userspace client do a poll() system call. All input arguments are
|
||||
* validated by the virtual file system before calling this function.
|
||||
*/
|
||||
static __poll_t btfmcodec_dev_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct btfmcodec_data *btfmcodec = file->private_data;
|
||||
struct btfmcodec_char_device *btfmcodec_dev= NULL;
|
||||
__poll_t mask = 0;
|
||||
unsigned long flags;
|
||||
|
||||
BTFMCODEC_DBG("start");
|
||||
if (!btfmcodec || !btfmcodec->btfmcodec_dev || refcount_read(&btfmcodec->btfmcodec_dev->active_clients) == 1) {
|
||||
BTFMCODEC_INFO("%s: %d\n", current->comm, task_pid_nr(current));
|
||||
return -EINVAL;
|
||||
} else {
|
||||
btfmcodec_dev = btfmcodec->btfmcodec_dev;
|
||||
}
|
||||
|
||||
/* Wait here for timeout or for a wakeup signal on readq */
|
||||
poll_wait(file, &btfmcodec_dev->readq, wait);
|
||||
|
||||
mutex_lock(&btfmcodec_dev->lock);
|
||||
/* recheck if the client has released by the driver */
|
||||
if (refcount_read(&btfmcodec_dev->active_clients) == 1) {
|
||||
BTFMCODEC_WARN("port has been closed already");
|
||||
mutex_unlock(&btfmcodec_dev->lock);
|
||||
return POLLHUP;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&btfmcodec_dev->tx_queue_lock, flags);
|
||||
/* Set flags if data is avilable to read */
|
||||
if (!skb_queue_empty(&btfmcodec_dev->txq))
|
||||
mask |= POLLIN | POLLRDNORM;
|
||||
|
||||
spin_unlock_irqrestore(&btfmcodec_dev->tx_queue_lock, flags);
|
||||
mutex_unlock(&btfmcodec_dev->lock);
|
||||
|
||||
BTFMCODEC_DBG("end with reason %d", mask);
|
||||
return mask;
|
||||
}
|
||||
|
||||
/*
|
||||
* btfmcodec_dev_read() - read() syscall for the btfmcodec dev node
|
||||
* file: Pointer to the file structure.
|
||||
* buf: Pointer to the userspace buffer.
|
||||
* count: Number bytes to read from the file.
|
||||
* ppos: Pointer to the position into the file.
|
||||
*
|
||||
* This function is used to Read the data from btfmcodec pkt device when
|
||||
* userspace client do a read() system call. All input arguments are
|
||||
* validated by the virtual file system before calling this function.
|
||||
*/
|
||||
static ssize_t btfmcodec_dev_read(struct file *file,
|
||||
char __user *buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct btfmcodec_data *btfmcodec = file->private_data;
|
||||
struct btfmcodec_char_device *btfmcodec_dev= NULL;
|
||||
unsigned long flags;
|
||||
struct sk_buff *skb;
|
||||
int use;
|
||||
|
||||
BTFMCODEC_DBG("start");
|
||||
if (!btfmcodec || !btfmcodec->btfmcodec_dev || refcount_read(&btfmcodec->btfmcodec_dev->active_clients) == 1) {
|
||||
BTFMCODEC_INFO("%s: %d\n", current->comm, task_pid_nr(current));
|
||||
return -EINVAL;
|
||||
} else {
|
||||
btfmcodec_dev = btfmcodec->btfmcodec_dev;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&btfmcodec_dev->tx_queue_lock, flags);
|
||||
/* Wait for data in the queue */
|
||||
if (skb_queue_empty(&btfmcodec_dev->txq)) {
|
||||
spin_unlock_irqrestore(&btfmcodec_dev->tx_queue_lock, flags);
|
||||
|
||||
if (file->f_flags & O_NONBLOCK)
|
||||
return -EAGAIN;
|
||||
|
||||
/* Wait until we get data*/
|
||||
if (wait_event_interruptible(btfmcodec_dev->readq,
|
||||
!skb_queue_empty(&btfmcodec_dev->txq)))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
/* We lost the client while waiting */
|
||||
if (refcount_read(&btfmcodec->btfmcodec_dev->active_clients) == 1)
|
||||
return -ENETRESET;
|
||||
|
||||
spin_lock_irqsave(&btfmcodec_dev->tx_queue_lock, flags);
|
||||
}
|
||||
|
||||
skb = skb_dequeue(&btfmcodec_dev->txq);
|
||||
spin_unlock_irqrestore(&btfmcodec_dev->tx_queue_lock, flags);
|
||||
if (!skb)
|
||||
return -EFAULT;
|
||||
|
||||
use = min_t(size_t, count, skb->len);
|
||||
if (copy_to_user(buf, skb->data, use))
|
||||
use = -EFAULT;
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
BTFMCODEC_DBG("end for %s by %s:%d ret[%d]\n", btfmcodec_dev->dev_name,
|
||||
current->comm, task_pid_nr(current), use);
|
||||
|
||||
return use;
|
||||
}
|
||||
|
||||
bool isCpSupported(void)
|
||||
{
|
||||
return is_cp_supported;
|
||||
}
|
||||
|
||||
static long btfmcodec_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct btfmcodec_data *btfmcodec = file->private_data;
|
||||
struct hwep_data *hwep_info;
|
||||
|
||||
BTFMCODEC_INFO("%s: command %04x", __func__, cmd);
|
||||
|
||||
mutex_lock(&btfmcodec->hwep_drv_lock);
|
||||
hwep_info = btfmcodec->hwep_info;
|
||||
if (!hwep_info) {
|
||||
BTFMCODEC_WARN("%s: HWEP is not registered with btfmcodec", __func__);
|
||||
BTFMCODEC_WARN("%s: caching required info", __func__);
|
||||
is_cp_supported = ((int)arg == 1) ? true : false;
|
||||
mutex_unlock(&btfmcodec->hwep_drv_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
mutex_unlock(&btfmcodec->hwep_drv_lock);
|
||||
switch (cmd) {
|
||||
case BTM_CP_UPDATE: {
|
||||
if ((int)arg == 1) {
|
||||
if (!strcmp(hwep_info->driver_name, "btfmslim"))
|
||||
set_bit(BTADV_AUDIO_MASTER_CONFIG, &hwep_info->flags);
|
||||
else if (!strcmp(hwep_info->driver_name, "btfmswr_slave"))
|
||||
set_bit(BTADV_CONFIGURE_DMA, &hwep_info->flags);
|
||||
BTFMCODEC_INFO("%s: This target support CP hwep %s",
|
||||
__func__, hwep_info->driver_name);
|
||||
} else {
|
||||
clear_bit(BTADV_AUDIO_MASTER_CONFIG, &hwep_info->flags);
|
||||
clear_bit(BTADV_CONFIGURE_DMA, &hwep_info->flags);
|
||||
BTFMCODEC_INFO("%s: This target support doesn't CP", __func__);
|
||||
}
|
||||
|
||||
BTFMCODEC_INFO("%s: mastr %d dma codec %d", __func__,
|
||||
(int)test_bit(BTADV_AUDIO_MASTER_CONFIG, &hwep_info->flags),
|
||||
(int)test_bit(BTADV_CONFIGURE_DMA, &hwep_info->flags));
|
||||
break;
|
||||
} default: {
|
||||
BTFMCODEC_ERR("%s unhandled cmd %04x", __func__, cmd);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static const struct file_operations btfmcodec_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = btfmcodec_dev_open,
|
||||
.release = btfmcodec_dev_release,
|
||||
.write = btfmcodec_dev_write,
|
||||
.poll = btfmcodec_dev_poll,
|
||||
.read = btfmcodec_dev_read,
|
||||
/* For Now add no hookups for below callbacks */
|
||||
.unlocked_ioctl = btfmcodec_ioctl,
|
||||
.compat_ioctl = btfmcodec_ioctl,
|
||||
};
|
||||
|
||||
static ssize_t btfmcodec_attributes_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t n)
|
||||
{
|
||||
struct btfmcodec_data *btfmcodec = dev_to_btfmcodec(dev);
|
||||
struct btfmcodec_char_device *btfmcodec_dev = btfmcodec->btfmcodec_dev;
|
||||
long tmp;
|
||||
|
||||
mutex_lock(&btfmcodec_dev->lock);
|
||||
if (kstrtol(buf, 0, &tmp)) {
|
||||
mutex_unlock(&btfmcodec_dev->lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
mutex_unlock(&btfmcodec_dev->lock);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static ssize_t btfmcodec_attributes_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
// struct btfmcodec_get_current_transport *btfmcodec_dev = dev_to_btfmcodec(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct btfmcodec_data* btfm_get_btfmcodec(void)
|
||||
{
|
||||
return btfmcodec;
|
||||
}
|
||||
EXPORT_SYMBOL(btfm_get_btfmcodec);
|
||||
|
||||
static DEVICE_ATTR_RW(btfmcodec_attributes);
|
||||
|
||||
static int __init btfmcodec_init(void)
|
||||
{
|
||||
struct btfmcodec_state_machine *states;
|
||||
struct btfmcodec_char_device *btfmcodec_dev;
|
||||
struct device *dev;
|
||||
int ret, i;
|
||||
|
||||
BTFMCODEC_INFO("starting up the module");
|
||||
btfmcodec = kzalloc(sizeof(struct btfmcodec_data), GFP_KERNEL);
|
||||
if (!btfmcodec) {
|
||||
BTFMCODEC_ERR("failed to allocate memory");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mutex_init(&btfmcodec->hwep_drv_lock);
|
||||
states = &btfmcodec->states;
|
||||
states->current_state = IDLE;
|
||||
states->next_state = IDLE;
|
||||
|
||||
BTFMCODEC_INFO("creating device node");
|
||||
/* create device node for communication between userspace and kernel */
|
||||
btfmcodec_dev = kzalloc(sizeof(struct btfmcodec_char_device), GFP_KERNEL);
|
||||
if (!btfmcodec_dev) {
|
||||
BTFMCODEC_ERR("failed to allocate memory");
|
||||
ret = -ENOMEM;
|
||||
goto info_cleanup;
|
||||
}
|
||||
|
||||
BTFMCODEC_INFO("trying to get major number\n");
|
||||
ret = alloc_chrdev_region(&dev_major, 0, 0, "btfmcodec");
|
||||
if (ret < 0) {
|
||||
BTFMCODEC_ERR("failed to allocate character device region");
|
||||
goto dev_cleanup;
|
||||
}
|
||||
|
||||
BTFMCODEC_INFO("creating btfm codec class");
|
||||
dev_class = class_create(THIS_MODULE, "btfmcodec");
|
||||
if (IS_ERR(dev_class)) {
|
||||
ret = PTR_ERR(dev_class);
|
||||
BTFMCODEC_ERR("class_create failed ret:%d\n", ret);
|
||||
goto deinit_chrdev;
|
||||
}
|
||||
|
||||
btfmcodec_dev->reuse_minor = idr_alloc(&dev_minor, btfmcodec, 1, 0, GFP_KERNEL);
|
||||
if (ret < 0) {
|
||||
BTFMCODEC_ERR("failed to allocated minor number");
|
||||
goto deinit_class;
|
||||
}
|
||||
|
||||
dev = &btfmcodec->dev;
|
||||
dev->driver = &driver;
|
||||
|
||||
// ToDo Rethink of having btfmcodec alone instead of btfmcodec
|
||||
btfmcodec->btfmcodec_dev = btfmcodec_dev;
|
||||
refcount_set(&btfmcodec_dev->active_clients, 1);
|
||||
mutex_init(&btfmcodec_dev->lock);
|
||||
strlcpy(btfmcodec_dev->dev_name, "btfmcodec_dev", DEVICE_NAME_MAX_LEN);
|
||||
device_initialize(dev);
|
||||
dev->class = dev_class;
|
||||
dev->devt = MKDEV(MAJOR(dev_major), btfmcodec_dev->reuse_minor);
|
||||
dev_set_drvdata(dev, btfmcodec);
|
||||
|
||||
cdev_init(&btfmcodec_dev->cdev, &btfmcodec_fops);
|
||||
btfmcodec_dev->cdev.owner = THIS_MODULE;
|
||||
btfmcodec_dev->btfmcodec = (struct btfmcodec_data *)btfmcodec;
|
||||
dev_set_name(dev, btfmcodec_dev->dev_name, btfmcodec_dev->reuse_minor);
|
||||
ret = cdev_add(&btfmcodec_dev->cdev, dev->devt, 1);
|
||||
if (ret) {
|
||||
BTFMCODEC_ERR("cdev_add failed with error no %d", ret);
|
||||
goto idr_cleanup;
|
||||
}
|
||||
|
||||
// ToDo to handler HIDL abrupt kill
|
||||
dev->release = NULL;
|
||||
ret = device_add(dev);
|
||||
if (ret) {
|
||||
BTFMCODEC_ERR("Failed to add device error no %d", ret);
|
||||
goto free_device;
|
||||
}
|
||||
|
||||
BTFMCODEC_ERR("Creating a sysfs entry with name: %s", btfmcodec_dev->dev_name);
|
||||
ret = device_create_file(dev, &dev_attr_btfmcodec_attributes);
|
||||
if (ret) {
|
||||
BTFMCODEC_ERR("Failed to create a devicd node: %s", btfmcodec_dev->dev_name);
|
||||
goto free_device;
|
||||
}
|
||||
|
||||
BTFMCODEC_INFO("created a node at /dev/%s with %u:%u\n",
|
||||
btfmcodec_dev->dev_name, dev_major, btfmcodec_dev->reuse_minor);
|
||||
|
||||
skb_queue_head_init(&btfmcodec_dev->rxq);
|
||||
mutex_init(&btfmcodec_dev->lock);
|
||||
INIT_WORK(&btfmcodec_dev->rx_work, btfmcodec_dev_rxwork);
|
||||
init_waitqueue_head(&btfmcodec_dev->readq);
|
||||
spin_lock_init(&btfmcodec_dev->tx_queue_lock);
|
||||
skb_queue_head_init(&btfmcodec_dev->txq);
|
||||
INIT_LIST_HEAD(&btfmcodec->config_head);
|
||||
for (i = 0; i < BTM_PKT_TYPE_MAX; i++) {
|
||||
init_waitqueue_head(&btfmcodec_dev->rsp_wait_q[i]);
|
||||
}
|
||||
mutex_init(&states->state_machine_lock);
|
||||
btfmcodec_dev->workqueue = alloc_ordered_workqueue("btfmcodec_wq", 0);
|
||||
if (!btfmcodec_dev->workqueue) {
|
||||
BTFMCODEC_ERR("btfmcodec_dev Workqueue not initialized properly");
|
||||
ret = -ENOMEM;
|
||||
goto free_device;
|
||||
}
|
||||
return ret;
|
||||
|
||||
free_device:
|
||||
put_device(dev);
|
||||
idr_cleanup:
|
||||
idr_remove(&dev_minor, btfmcodec_dev->reuse_minor);
|
||||
deinit_class:
|
||||
class_destroy(dev_class);
|
||||
deinit_chrdev:
|
||||
unregister_chrdev_region(MAJOR(dev_major), 0);
|
||||
dev_cleanup:
|
||||
kfree(btfmcodec_dev);
|
||||
info_cleanup:
|
||||
kfree(btfmcodec);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit btfmcodec_deinit(void)
|
||||
{
|
||||
struct btfmcodec_char_device *btfmcodec_dev;
|
||||
struct device *dev;
|
||||
BTFMCODEC_INFO("%s: cleaning up btfm codec driver", __func__);
|
||||
if (!btfmcodec) {
|
||||
BTFMCODEC_ERR("%s: skiping driver cleanup", __func__);
|
||||
goto info_cleanup;
|
||||
}
|
||||
|
||||
dev = &btfmcodec->dev;
|
||||
|
||||
device_remove_file(dev, &dev_attr_btfmcodec_attributes);
|
||||
put_device(dev);
|
||||
|
||||
if (!btfmcodec->btfmcodec_dev) {
|
||||
BTFMCODEC_ERR("%s: skiping device node cleanup", __func__);
|
||||
goto info_cleanup;
|
||||
}
|
||||
|
||||
btfmcodec_dev = btfmcodec->btfmcodec_dev;
|
||||
skb_queue_purge(&btfmcodec_dev->rxq);
|
||||
idr_remove(&dev_minor, btfmcodec_dev->reuse_minor);
|
||||
class_destroy(dev_class);
|
||||
unregister_chrdev_region(MAJOR(dev_major), 0);
|
||||
kfree(btfmcodec_dev);
|
||||
info_cleanup:
|
||||
kfree(btfmcodec);
|
||||
BTFMCODEC_INFO("%s: btfm codec driver cleanup completed", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("MSM Bluetooth FM CODEC driver");
|
||||
|
||||
module_init(btfmcodec_init);
|
||||
module_exit(btfmcodec_deinit);
|
326
qcom/opensource/bt-kernel/btfmcodec/btfm_codec_btadv_interface.c
Normal file
326
qcom/opensource/bt-kernel/btfmcodec/btfm_codec_btadv_interface.c
Normal file
@ -0,0 +1,326 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "btfm_codec.h"
|
||||
#include "btfm_codec_pkt.h"
|
||||
#include "btfm_codec_btadv_interface.h"
|
||||
|
||||
void btfmcodec_initiate_hwep_shutdown(struct btfmcodec_char_device *btfmcodec_dev)
|
||||
{
|
||||
wait_queue_head_t *rsp_wait_q =
|
||||
&btfmcodec_dev->rsp_wait_q[BTM_PKT_TYPE_HWEP_SHUTDOWN];
|
||||
int ret;
|
||||
uint8_t *status = &btfmcodec_dev->status[BTM_PKT_TYPE_HWEP_SHUTDOWN];
|
||||
|
||||
*status = BTM_WAITING_RSP;
|
||||
BTFMCODEC_INFO("queuing work to shutdown");
|
||||
schedule_work(&btfmcodec_dev->wq_hwep_shutdown);
|
||||
BTFMCODEC_INFO("waiting here for shutdown");
|
||||
ret = wait_event_interruptible_timeout(*rsp_wait_q,
|
||||
*status != BTM_WAITING_RSP,
|
||||
msecs_to_jiffies(BTM_MASTER_CONFIG_RSP_TIMEOUT));
|
||||
|
||||
/* Rethink of having a new packet to notify transport switch error */
|
||||
if (ret == 0) {
|
||||
BTFMCODEC_ERR("failed to recevie to complete hwep_shutdown");
|
||||
flush_work(&btfmcodec_dev->wq_hwep_shutdown);
|
||||
} else {
|
||||
if (*status == BTM_RSP_RECV) {
|
||||
BTFMCODEC_ERR("sucessfully closed hwep");
|
||||
return;
|
||||
} else if (*status == BTM_FAIL_RESP_RECV ||
|
||||
*status == BTM_RSP_NOT_RECV_CLIENT_KILLED) {
|
||||
BTFMCODEC_ERR("Failed to close hwep");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void btfmcodec_move_to_next_state(struct btfmcodec_state_machine *state)
|
||||
{
|
||||
mutex_lock(&state->state_machine_lock);
|
||||
if (state->current_state == BT_Connecting ||
|
||||
state->current_state == BTADV_AUDIO_Connecting) {
|
||||
state->current_state += 1;
|
||||
BTFMCODEC_INFO("moving from %s to %s",
|
||||
coverttostring(state->current_state -1 ),
|
||||
coverttostring(state->current_state));
|
||||
} else {
|
||||
BTFMCODEC_ERR("State machine might have gone bad. reseting to default");
|
||||
state->current_state = IDLE;
|
||||
}
|
||||
|
||||
state->prev_state = IDLE;
|
||||
mutex_unlock(&state->state_machine_lock);
|
||||
}
|
||||
|
||||
void btfmcodec_revert_current_state(struct btfmcodec_state_machine *state)
|
||||
{
|
||||
mutex_lock(&state->state_machine_lock);
|
||||
BTFMCODEC_INFO("reverting from %s to %s", coverttostring(state->current_state),
|
||||
coverttostring(state->prev_state));
|
||||
state->current_state = state->prev_state;
|
||||
state->prev_state = IDLE;
|
||||
mutex_unlock(&state->state_machine_lock);
|
||||
}
|
||||
|
||||
void btfmcodec_set_current_state(struct btfmcodec_state_machine *state,
|
||||
btfmcodec_state current_state)
|
||||
{
|
||||
mutex_lock(&state->state_machine_lock);
|
||||
BTFMCODEC_INFO("moving from %s to %s", coverttostring(state->current_state),
|
||||
coverttostring(current_state));
|
||||
state->prev_state = state->current_state;
|
||||
state->current_state = current_state;
|
||||
mutex_unlock(&state->state_machine_lock);
|
||||
}
|
||||
|
||||
btfmcodec_state btfmcodec_get_current_transport(struct
|
||||
btfmcodec_state_machine *state)
|
||||
{
|
||||
btfmcodec_state current_state;
|
||||
|
||||
mutex_lock(&state->state_machine_lock);
|
||||
current_state = state->current_state;
|
||||
mutex_unlock(&state->state_machine_lock);
|
||||
return current_state;
|
||||
}
|
||||
|
||||
int btfmcodec_frame_transport_switch_ind_pkt(struct btfmcodec_char_device *btfmcodec_dev,
|
||||
uint8_t active_transport,
|
||||
uint8_t status)
|
||||
{
|
||||
struct btm_ctrl_pkt rsp;
|
||||
|
||||
rsp.opcode = BTM_BTFMCODEC_TRANSPORT_SWITCH_FAILED_IND;
|
||||
rsp.len = BTM_PREPARE_AUDIO_BEARER_SWITCH_RSP_LEN;
|
||||
rsp.active_transport = active_transport;
|
||||
rsp.status = status;
|
||||
return btfmcodec_dev_enqueue_pkt(btfmcodec_dev, &rsp, (rsp.len +
|
||||
BTM_HEADER_LEN));
|
||||
}
|
||||
|
||||
int btfmcodec_frame_prepare_bearer_rsp_pkt(struct btfmcodec_char_device *btfmcodec_dev,
|
||||
uint8_t active_transport,
|
||||
uint8_t status)
|
||||
{
|
||||
struct btm_ctrl_pkt rsp;
|
||||
|
||||
rsp.opcode = BTM_BTFMCODEC_PREPARE_AUDIO_BEARER_SWITCH_RSP;
|
||||
rsp.len =BTM_PREPARE_AUDIO_BEARER_SWITCH_RSP_LEN;
|
||||
rsp.active_transport = active_transport;
|
||||
rsp.status = status;
|
||||
return btfmcodec_dev_enqueue_pkt(btfmcodec_dev, &rsp, (rsp.len +
|
||||
BTM_HEADER_LEN));
|
||||
}
|
||||
|
||||
int btfmcodec_wait_for_bearer_ind(struct btfmcodec_char_device *btfmcodec_dev)
|
||||
{
|
||||
wait_queue_head_t *rsp_wait_q =
|
||||
&btfmcodec_dev->rsp_wait_q[BTM_PKT_TYPE_BEARER_SWITCH_IND];
|
||||
int ret;
|
||||
uint8_t *status = &btfmcodec_dev->status[BTM_PKT_TYPE_BEARER_SWITCH_IND];
|
||||
|
||||
ret = wait_event_interruptible_timeout(*rsp_wait_q,
|
||||
*status != BTM_WAITING_RSP,
|
||||
msecs_to_jiffies(BTM_MASTER_CONFIG_RSP_TIMEOUT));
|
||||
|
||||
if (ret == 0) {
|
||||
BTFMCODEC_ERR("failed to recevie BTM_BEARER_SWITCH_IND");
|
||||
ret = -MSG_INTERNAL_TIMEOUT;
|
||||
} else {
|
||||
if (*status == BTM_RSP_RECV) {
|
||||
ret = 0;
|
||||
} else if (*status == BTM_FAIL_RESP_RECV) {
|
||||
BTFMCODEC_ERR("Rx BTM_BEARER_SWITCH_IND with failure status");
|
||||
ret = -1;
|
||||
} else if (*status == BTM_RSP_NOT_RECV_CLIENT_KILLED) {
|
||||
BTFMCODEC_ERR("client killed so moving further");
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int btfmcodec_initiate_hwep_configuration(struct btfmcodec_char_device *btfmcodec_dev)
|
||||
{
|
||||
wait_queue_head_t *rsp_wait_q =
|
||||
&btfmcodec_dev->rsp_wait_q[BTM_PKT_TYPE_HWEP_CONFIG];
|
||||
uint8_t *status = &btfmcodec_dev->status[BTM_PKT_TYPE_HWEP_CONFIG];
|
||||
int ret;
|
||||
|
||||
schedule_work(&btfmcodec_dev->wq_hwep_configure);
|
||||
|
||||
*status = BTM_WAITING_RSP;
|
||||
ret = wait_event_interruptible_timeout(*rsp_wait_q,
|
||||
*status != BTM_WAITING_RSP,
|
||||
msecs_to_jiffies(BTM_MASTER_CONFIG_RSP_TIMEOUT));
|
||||
|
||||
if (ret == 0) {
|
||||
BTFMCODEC_ERR("failed to recevie complete hwep configure");
|
||||
flush_work(&btfmcodec_dev->wq_hwep_configure);
|
||||
ret = -1;
|
||||
} else {
|
||||
if (*status == BTM_RSP_RECV) {
|
||||
ret = 0;
|
||||
} else if (*status == BTM_FAIL_RESP_RECV ||
|
||||
*status == BTM_RSP_NOT_RECV_CLIENT_KILLED) {
|
||||
BTFMCODEC_ERR("Failed to close hwep moving back to previous state");
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void btfmcodec_configure_hwep(struct btfmcodec_char_device *btfmcodec_dev)
|
||||
{
|
||||
struct btfmcodec_data *btfmcodec = (struct btfmcodec_data *)btfmcodec_dev->btfmcodec;
|
||||
struct btfmcodec_state_machine *state = &btfmcodec->states;
|
||||
int ret;
|
||||
uint8_t status = MSG_SUCCESS;
|
||||
|
||||
ret = btfmcodec_initiate_hwep_configuration(btfmcodec_dev);
|
||||
if (ret < 0) {
|
||||
status = MSG_FAILED_TO_CONFIGURE_HWEP;
|
||||
/* Move back to BTADV_AUDIO_Connected from BT_Connecting */
|
||||
btfmcodec_revert_current_state(state);
|
||||
}
|
||||
|
||||
ret = btfmcodec_frame_prepare_bearer_rsp_pkt(btfmcodec_dev,
|
||||
btfmcodec_get_current_transport(state), status);
|
||||
|
||||
if (status != MSG_SUCCESS)
|
||||
return;
|
||||
|
||||
if (ret < 0) {
|
||||
ret = btfmcodec_wait_for_bearer_ind(btfmcodec_dev);
|
||||
if (ret < 0) {
|
||||
/* Move back to BTADV_AUDIO_Connected for failure cases*/
|
||||
BTFMCODEC_ERR("moving back to previous state");
|
||||
btfmcodec_revert_current_state(state);
|
||||
/* close HWEP */
|
||||
btfmcodec_initiate_hwep_shutdown(btfmcodec_dev);
|
||||
if (ret == -MSG_INTERNAL_TIMEOUT) {
|
||||
btfmcodec_frame_transport_switch_ind_pkt(btfmcodec_dev, BT,
|
||||
MSG_INTERNAL_TIMEOUT);
|
||||
}
|
||||
} else {
|
||||
/* move from BT_Connecting to BT_Connected */
|
||||
btfmcodec_move_to_next_state(state);
|
||||
}
|
||||
} else {
|
||||
/* add code to handle packet errors */
|
||||
}
|
||||
}
|
||||
|
||||
void btfmcodec_prepare_bearer(struct btfmcodec_char_device *btfmcodec_dev,
|
||||
enum transport_type new_transport)
|
||||
{
|
||||
struct btfmcodec_data *btfmcodec = (struct btfmcodec_data *)btfmcodec_dev->btfmcodec;
|
||||
struct btfmcodec_state_machine *state = &btfmcodec->states;
|
||||
btfmcodec_state current_state;
|
||||
int ret = -1;
|
||||
|
||||
if (new_transport > (ARRAY_SIZE(transport_type_text))) {
|
||||
btfmcodec_frame_prepare_bearer_rsp_pkt(btfmcodec_dev,
|
||||
(uint8_t) btfmcodec_get_current_transport(state),
|
||||
MSG_WRONG_TRANSPORT_TYPE);
|
||||
return;
|
||||
}
|
||||
|
||||
BTFMCODEC_INFO("Rx to switch from transport type %s to %s",
|
||||
coverttostring(btfmcodec_get_current_transport(state)),
|
||||
transport_type_text[new_transport - 1]);
|
||||
|
||||
current_state = btfmcodec_get_current_transport(state);
|
||||
if (new_transport == BT) {
|
||||
/* If BT is already active. send +ve ack to BTADV Audio Manager */
|
||||
if (current_state == BT_Connected ||
|
||||
current_state == BT_Connecting) {
|
||||
btfmcodec_frame_prepare_bearer_rsp_pkt(btfmcodec_dev,
|
||||
(uint8_t)current_state, MSG_SUCCESS);
|
||||
return;
|
||||
} else if (current_state == BTADV_AUDIO_Connected ||
|
||||
current_state == BTADV_AUDIO_Connecting||
|
||||
current_state == IDLE) {
|
||||
if (btfmcodec_is_valid_cache_avb(btfmcodec)) {
|
||||
BTFMCODEC_INFO("detected BTADV audio Gaming usecase to BT usecase");
|
||||
btfmcodec_set_current_state(state, BT_Connecting);
|
||||
btfmcodec_configure_hwep(btfmcodec_dev);
|
||||
} else {
|
||||
if (current_state != IDLE)
|
||||
BTFMCODEC_INFO("detected BTADV Audio lossless to IDLE");
|
||||
BTFMCODEC_INFO("moving to IDLE as no config available");
|
||||
btfmcodec_set_current_state(state, IDLE);
|
||||
btfmcodec_frame_prepare_bearer_rsp_pkt(btfmcodec_dev,
|
||||
btfmcodec_get_current_transport(state),
|
||||
MSG_SUCCESS);
|
||||
/* No need wait for bearer switch indications as BTFMCODEC
|
||||
* driver doesn't have configs to configure
|
||||
*/
|
||||
}
|
||||
}
|
||||
} else if(new_transport == BTADV) {
|
||||
/* If BTADV audio is already active. send +ve ack to BTADV audio Manager */
|
||||
if (current_state == BTADV_AUDIO_Connecting ||
|
||||
current_state == BTADV_AUDIO_Connected) {
|
||||
btfmcodec_frame_prepare_bearer_rsp_pkt(btfmcodec_dev,
|
||||
(uint8_t)current_state, MSG_SUCCESS);
|
||||
return;
|
||||
} else {
|
||||
btfmcodec_set_current_state(state, BTADV_AUDIO_Connecting);
|
||||
if (btfmcodec_is_valid_cache_avb(btfmcodec)) {
|
||||
BTFMCODEC_INFO("detected BT to BTADV audio Gaming usecase");
|
||||
} else {
|
||||
BTFMCODEC_INFO("detected IDLE to BTADV audio lossless usecase");
|
||||
}
|
||||
|
||||
ret = btfmcodec_frame_prepare_bearer_rsp_pkt(btfmcodec_dev,
|
||||
BTADV_AUDIO_Connecting, MSG_SUCCESS);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
/* Wait here to get Bearer switch indication */
|
||||
ret = btfmcodec_wait_for_bearer_ind(btfmcodec_dev);
|
||||
if (ret < 0) {
|
||||
BTFMCODEC_ERR("moving back to previous state");
|
||||
btfmcodec_revert_current_state(state);
|
||||
if (ret == -MSG_INTERNAL_TIMEOUT) {
|
||||
btfmcodec_frame_transport_switch_ind_pkt(
|
||||
btfmcodec_dev, BTADV,
|
||||
MSG_INTERNAL_TIMEOUT);
|
||||
}
|
||||
} else {
|
||||
btfmcodec_move_to_next_state(state);
|
||||
}
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
if (btfmcodec_is_valid_cache_avb(btfmcodec)) {
|
||||
BTFMCODEC_INFO("Initiating BT port close...");
|
||||
btfmcodec_initiate_hwep_shutdown(btfmcodec_dev);
|
||||
}
|
||||
}
|
||||
} else if (new_transport == NONE) {
|
||||
/* Let ALSA handles the transport close for BT */
|
||||
if (current_state != BT_Connecting && current_state != BT_Connected)
|
||||
btfmcodec_set_current_state(state, IDLE);
|
||||
btfmcodec_frame_prepare_bearer_rsp_pkt(btfmcodec_dev, (uint8_t)current_state,
|
||||
MSG_SUCCESS);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void btfmcodec_wq_prepare_bearer(struct work_struct *work)
|
||||
{
|
||||
struct btfmcodec_char_device *btfmcodec_dev = container_of(work,
|
||||
struct btfmcodec_char_device,
|
||||
wq_prepare_bearer);
|
||||
int idx = BTM_PKT_TYPE_PREPARE_REQ;
|
||||
BTFMCODEC_INFO("with new transport:%d", btfmcodec_dev->status[idx]);
|
||||
btfmcodec_prepare_bearer(btfmcodec_dev, btfmcodec_dev->status[idx]);
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
#include "btfm_codec.h"
|
||||
#include "btfm_codec_hw_interface.h"
|
||||
#include "btfm_codec_interface.h"
|
||||
|
||||
int btfmcodec_register_hw_ep (struct hwep_data *ep_info)
|
||||
{
|
||||
struct btfmcodec_data *btfmcodec;
|
||||
struct hwep_data *hwep_info;
|
||||
int ret = 0;
|
||||
|
||||
btfmcodec = btfm_get_btfmcodec();
|
||||
mutex_lock(&btfmcodec->hwep_drv_lock);
|
||||
if (!btfmcodec) {
|
||||
BTFMCODEC_ERR("btfm codec driver it not initialized");
|
||||
ret = -EPERM;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (ep_info->num_dai == 0) {
|
||||
BTFMCODEC_ERR("no active information provided by hw ep interface");
|
||||
ret = -EPERM;
|
||||
goto end;
|
||||
|
||||
}
|
||||
|
||||
hwep_info = btfmcodec->hwep_info;
|
||||
if (hwep_info) {
|
||||
BTFMCODEC_ERR("driver already holds hardware endpoint info");
|
||||
ret = -EPERM;
|
||||
goto end;
|
||||
}
|
||||
|
||||
hwep_info = kzalloc(sizeof(struct hwep_data), GFP_KERNEL);
|
||||
if (!hwep_info) {
|
||||
BTFMCODEC_ERR("%s: failed to allocate memory\n", __func__);
|
||||
ret = -ENOMEM;
|
||||
goto end;
|
||||
}
|
||||
|
||||
btfmcodec->hwep_info = hwep_info;
|
||||
memcpy(hwep_info, ep_info, sizeof(struct hwep_data));
|
||||
|
||||
BTFMCODEC_INFO("Below driver registered with btfm codec\n");
|
||||
BTFMCODEC_INFO("Driver name: %s\n", hwep_info->driver_name);
|
||||
BTFMCODEC_INFO("Num of dai: %d supported", hwep_info->num_dai);
|
||||
BTFMCODEC_INFO("Master config enabled: %u\n", test_bit(BTADV_AUDIO_MASTER_CONFIG,
|
||||
&hwep_info->flags));
|
||||
ret = btfm_register_codec(hwep_info);
|
||||
end:
|
||||
mutex_unlock(&btfmcodec->hwep_drv_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int btfmcodec_unregister_hw_ep (char *driver_name)
|
||||
{
|
||||
struct btfmcodec_data *btfmcodec;
|
||||
struct hwep_data *hwep_info;
|
||||
int ret;
|
||||
|
||||
btfmcodec = btfm_get_btfmcodec();
|
||||
mutex_lock(&btfmcodec->hwep_drv_lock);
|
||||
if (!btfmcodec) {
|
||||
BTFMCODEC_ERR("btfm codec driver it not initialized");
|
||||
ret = -EPERM;
|
||||
goto end;
|
||||
}
|
||||
|
||||
hwep_info = btfmcodec->hwep_info;
|
||||
if (!hwep_info) {
|
||||
BTFMCODEC_ERR("%s: no active hardware endpoint registered\n", __func__);
|
||||
ret = -EPERM;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if(!strncmp(hwep_info->driver_name, driver_name, DEVICE_NAME_MAX_LEN)) {
|
||||
btfm_unregister_codec();
|
||||
kfree(hwep_info);
|
||||
BTFMCODEC_INFO("%s: deleted %s hardware endpoint\n", __func__, driver_name);
|
||||
ret = -1;
|
||||
goto end;
|
||||
} else {
|
||||
BTFMCODEC_ERR("%s: No hardware endpoint registered with %s\n", __func__, driver_name);
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
end:
|
||||
mutex_unlock(&btfmcodec->hwep_drv_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(btfmcodec_register_hw_ep);
|
||||
EXPORT_SYMBOL(btfmcodec_unregister_hw_ep);
|
881
qcom/opensource/bt-kernel/btfmcodec/btfm_codec_interface.c
Normal file
881
qcom/opensource/bt-kernel/btfmcodec/btfm_codec_interface.c
Normal file
@ -0,0 +1,881 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/remoteproc.h>
|
||||
#include <linux/remoteproc/qcom_rproc.h>
|
||||
#include "btfm_codec.h"
|
||||
#include "btfm_codec_interface.h"
|
||||
#include "btfm_codec_pkt.h"
|
||||
#include "btfm_codec_btadv_interface.h"
|
||||
|
||||
static struct snd_soc_dai_driver *btfmcodec_dai_info;
|
||||
uint32_t bits_per_second;
|
||||
uint8_t num_channels;
|
||||
|
||||
static int btfm_codec_get_mixer_control(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *codec = kcontrol->private_data;
|
||||
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(codec);
|
||||
struct hwep_data *hwepinfo = btfmcodec->hwep_info;
|
||||
struct snd_kcontrol_new *mixer_ctrl = hwepinfo->mixer_ctrl;
|
||||
struct snd_ctl_elem_id id = kcontrol->id;
|
||||
int num_mixer_ctrl = hwepinfo->num_mixer_ctrl;
|
||||
int i = 0;
|
||||
|
||||
BTFMCODEC_DBG("");
|
||||
for (; i < num_mixer_ctrl ; i++) {
|
||||
BTFMCODEC_DBG("checking mixer_ctrl:%s and current mixer:%s",
|
||||
id.name, mixer_ctrl[i].name);
|
||||
if (!strncmp(id.name, mixer_ctrl[i].name, 64)) {
|
||||
BTFMCODEC_DBG("Matched");
|
||||
mixer_ctrl[i].get(kcontrol, ucontrol);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (num_mixer_ctrl == i)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int btfmcodec_put_mixer_control(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *codec = kcontrol->private_data;
|
||||
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(codec);
|
||||
struct hwep_data *hwepinfo = btfmcodec->hwep_info;
|
||||
struct snd_kcontrol_new *mixer_ctrl = hwepinfo->mixer_ctrl;
|
||||
struct snd_ctl_elem_id id = kcontrol->id;
|
||||
int num_mixer_ctrl = hwepinfo->num_mixer_ctrl;
|
||||
int i = 0;
|
||||
|
||||
BTFMCODEC_DBG("");
|
||||
for (; i < num_mixer_ctrl ; i++) {
|
||||
BTFMCODEC_DBG("checking mixer_ctrl:%s and current mixer:%s",
|
||||
id.name, mixer_ctrl[i].name);
|
||||
if (!strncmp(id.name, mixer_ctrl[i].name, 64)) {
|
||||
BTFMCODEC_DBG("Matched");
|
||||
mixer_ctrl[i].put(kcontrol, ucontrol);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (num_mixer_ctrl == i)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int btfmcodec_codec_probe(struct snd_soc_component *codec)
|
||||
{
|
||||
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(codec);
|
||||
struct btfmcodec_state_machine *state = &btfmcodec->states;
|
||||
struct hwep_data *hwep_info = btfmcodec->hwep_info;
|
||||
int num_mixer_ctrl = hwep_info->num_mixer_ctrl;
|
||||
BTFMCODEC_DBG("");
|
||||
|
||||
// ToDo: check Whether probe has to allowed when state if different
|
||||
if (btfmcodec_get_current_transport(state)!= IDLE) {
|
||||
BTFMCODEC_WARN("Received probe when state is :%s",
|
||||
coverttostring(btfmcodec_get_current_transport(state)));
|
||||
} else if (hwep_info->drv && hwep_info->drv->hwep_probe) {
|
||||
hwep_info->drv->hwep_probe(codec);
|
||||
/* Register mixer control */
|
||||
if (hwep_info->mixer_ctrl && num_mixer_ctrl >= 1) {
|
||||
struct snd_kcontrol_new *mixer_ctrl;
|
||||
int i = 0;
|
||||
mixer_ctrl = (struct snd_kcontrol_new *)
|
||||
kzalloc(num_mixer_ctrl *
|
||||
sizeof(struct snd_kcontrol_new), GFP_KERNEL);
|
||||
if (!mixer_ctrl) {
|
||||
BTFMCODEC_ERR("failed to register mixer controls");
|
||||
goto end;
|
||||
}
|
||||
|
||||
BTFMCODEC_INFO("Registering %d mixer controls", num_mixer_ctrl);
|
||||
memcpy(mixer_ctrl, hwep_info->mixer_ctrl, num_mixer_ctrl * sizeof(struct snd_kcontrol_new));
|
||||
for (; i< num_mixer_ctrl; i++) {
|
||||
BTFMCODEC_INFO("name of control:%s", mixer_ctrl[i].name);
|
||||
mixer_ctrl[i].get = btfm_codec_get_mixer_control;
|
||||
mixer_ctrl[i].put = btfmcodec_put_mixer_control;
|
||||
}
|
||||
snd_soc_add_component_controls(codec, mixer_ctrl, num_mixer_ctrl);
|
||||
BTFMCODEC_INFO("CODEC address while registering mixer ctrl:%p", codec);
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
// ToDo to add mixer control.
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void btfmcodec_codec_remove(struct snd_soc_component *codec)
|
||||
{
|
||||
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(codec);
|
||||
struct btfmcodec_state_machine *state = &btfmcodec->states;
|
||||
struct hwep_data *hwep_info = btfmcodec->hwep_info;
|
||||
BTFMCODEC_DBG("");
|
||||
|
||||
// ToDo: check whether remove has to allowed when state if different
|
||||
if (btfmcodec_get_current_transport(state)!= IDLE) {
|
||||
BTFMCODEC_WARN("Received probe when state is :%s",
|
||||
coverttostring(btfmcodec_get_current_transport(state)));
|
||||
} else if (hwep_info->drv && hwep_info->drv->hwep_remove) {
|
||||
hwep_info->drv->hwep_remove(codec);
|
||||
}
|
||||
}
|
||||
|
||||
static int btfmcodec_codec_write(struct snd_soc_component *codec,
|
||||
unsigned int reg, unsigned int value)
|
||||
{
|
||||
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(codec);
|
||||
struct btfmcodec_state_machine *state = &btfmcodec->states;
|
||||
struct hwep_data *hwep_info = btfmcodec->hwep_info;
|
||||
BTFMCODEC_DBG("");
|
||||
|
||||
// ToDo: check whether write has to allowed when state if different
|
||||
if (btfmcodec_get_current_transport(state)!= IDLE) {
|
||||
BTFMCODEC_WARN("Received probe when state is :%s",
|
||||
coverttostring(btfmcodec_get_current_transport(state)));
|
||||
} else if (hwep_info->drv && hwep_info->drv->hwep_remove) {
|
||||
return hwep_info->drv->hwep_write(codec, reg, value);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int btfmcodec_codec_read(struct snd_soc_component *codec,
|
||||
unsigned int reg)
|
||||
{
|
||||
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(codec);
|
||||
struct btfmcodec_state_machine *state = &btfmcodec->states;
|
||||
struct hwep_data *hwep_info = btfmcodec->hwep_info;
|
||||
BTFMCODEC_DBG("");
|
||||
|
||||
// ToDo: check whether read has to allowed when state if different
|
||||
if (btfmcodec_get_current_transport(state)!= IDLE) {
|
||||
BTFMCODEC_WARN("Received probe when state is :%s",
|
||||
coverttostring(btfmcodec_get_current_transport(state)));
|
||||
} else if (hwep_info->drv && hwep_info->drv->hwep_read) {
|
||||
return hwep_info->drv->hwep_read(codec, reg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_component_driver btfmcodec_codec_component = {
|
||||
.probe = btfmcodec_codec_probe,
|
||||
.remove = btfmcodec_codec_remove,
|
||||
.read = btfmcodec_codec_read,
|
||||
.write = btfmcodec_codec_write,
|
||||
};
|
||||
|
||||
|
||||
static inline void * btfmcodec_get_dai_drvdata(struct hwep_data *hwep_info)
|
||||
{
|
||||
if (!hwep_info || !hwep_info->dai_drv) return NULL;
|
||||
return hwep_info->dai_drv;
|
||||
}
|
||||
|
||||
int btfmcodec_hwep_startup(struct btfmcodec_data *btfmcodec)
|
||||
{
|
||||
struct hwep_data *hwep_info = btfmcodec->hwep_info;
|
||||
struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)
|
||||
btfmcodec_get_dai_drvdata(hwep_info);
|
||||
|
||||
if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_startup) {
|
||||
return dai_drv->dai_ops->hwep_startup((void *)btfmcodec->hwep_info);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int btfmcodec_dai_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component);
|
||||
struct btfmcodec_state_machine *state = &btfmcodec->states;
|
||||
|
||||
BTFMCODEC_DBG("substream = %s stream = %d dai->name = %s",
|
||||
substream->name, substream->stream, dai->name);
|
||||
|
||||
if (btfmcodec_get_current_transport(state) != IDLE &&
|
||||
btfmcodec_get_current_transport(state) != BT_Connected) {
|
||||
BTFMCODEC_DBG("Not allowing as state is:%s",
|
||||
coverttostring(btfmcodec_get_current_transport(state)));
|
||||
} else {
|
||||
return btfmcodec_hwep_startup(btfmcodec);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int btfmcodec_hwep_shutdown(struct btfmcodec_data *btfmcodec, int id,
|
||||
bool disable_master)
|
||||
{
|
||||
struct hwep_data *hwep_info = btfmcodec->hwep_info;
|
||||
struct btfmcodec_char_device *btfmcodec_dev = btfmcodec->btfmcodec_dev;
|
||||
struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)
|
||||
btfmcodec_get_dai_drvdata(hwep_info);
|
||||
struct btfmcodec_state_machine *state = &btfmcodec->states;
|
||||
struct btm_master_shutdown_req shutdown_req;
|
||||
wait_queue_head_t *rsp_wait_q =
|
||||
&btfmcodec_dev->rsp_wait_q[BTM_PKT_TYPE_MASTER_SHUTDOWN_RSP];
|
||||
uint8_t *status = &btfmcodec_dev->status[BTM_PKT_TYPE_MASTER_SHUTDOWN_RSP];
|
||||
int ret = 0;
|
||||
|
||||
/* for master configurations failure cases, we don't need to send
|
||||
* shutdown request
|
||||
*/
|
||||
if (btfmcodec_get_current_transport(state) == BT_Connected && disable_master) {
|
||||
BTFMCODEC_DBG("sending master shutdown request..");
|
||||
shutdown_req.opcode = BTM_BTFMCODEC_MASTER_SHUTDOWN_REQ;
|
||||
shutdown_req.len = BTM_MASTER_SHUTDOWN_REQ_LEN;
|
||||
shutdown_req.stream_id = id;
|
||||
/* See if we need to protect below with lock */
|
||||
*status = BTM_WAITING_RSP;
|
||||
btfmcodec_dev_enqueue_pkt(btfmcodec_dev, &shutdown_req, (shutdown_req.len +
|
||||
BTM_HEADER_LEN));
|
||||
|
||||
ret = wait_event_interruptible_timeout(*rsp_wait_q,
|
||||
(*status) != BTM_WAITING_RSP,
|
||||
msecs_to_jiffies(BTM_MASTER_CONFIG_RSP_TIMEOUT));
|
||||
if (ret == 0) {
|
||||
BTFMCODEC_ERR("failed to recevie response from BTADV audio Manager");
|
||||
} else {
|
||||
if (*status == BTM_RSP_RECV)
|
||||
ret = 0;
|
||||
else if (*status == BTM_FAIL_RESP_RECV ||
|
||||
*status == BTM_RSP_NOT_RECV_CLIENT_KILLED)
|
||||
ret = -1;
|
||||
}
|
||||
} else {
|
||||
if (!disable_master)
|
||||
BTFMCODEC_WARN("Not sending master shutdown request as graph might have closed");
|
||||
else
|
||||
BTFMCODEC_WARN("Not sending master shutdown request as state is:%s",
|
||||
coverttostring(btfmcodec_get_current_transport(state)));
|
||||
}
|
||||
|
||||
if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_shutdown) {
|
||||
dai_drv->dai_ops->hwep_shutdown((void *)btfmcodec->hwep_info, id);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void btfmcodec_wq_hwep_shutdown(struct work_struct *work)
|
||||
{
|
||||
struct btfmcodec_char_device *btfmcodec_dev = container_of(work,
|
||||
struct btfmcodec_char_device,
|
||||
wq_hwep_shutdown);
|
||||
struct btfmcodec_data *btfmcodec = (struct btfmcodec_data *)btfmcodec_dev->btfmcodec;
|
||||
struct list_head *head = &btfmcodec->config_head;
|
||||
struct hwep_configurations *hwep_configs = NULL, *tmp;
|
||||
int ret = -1;
|
||||
int idx = BTM_PKT_TYPE_HWEP_SHUTDOWN;
|
||||
|
||||
BTFMCODEC_INFO(" starting shutdown");
|
||||
/* Just check if first Rx has to be closed first or
|
||||
* any order should be ok.
|
||||
*/
|
||||
list_for_each_entry_safe(hwep_configs, tmp, head, dai_list) {
|
||||
BTFMCODEC_INFO("shuting down dai id:%d", hwep_configs->stream_id);
|
||||
ret = btfmcodec_hwep_shutdown(btfmcodec, hwep_configs->stream_id, true);
|
||||
if (ret < 0) {
|
||||
BTFMCODEC_ERR("failed to shutdown master with id %d", hwep_configs->stream_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
btfmcodec_dev->status[idx] = BTM_FAIL_RESP_RECV;
|
||||
else
|
||||
btfmcodec_dev->status[idx] = BTM_RSP_RECV;
|
||||
|
||||
wake_up_interruptible(&btfmcodec_dev->rsp_wait_q[idx]);
|
||||
}
|
||||
|
||||
static int btfmcodec_delete_configs(struct btfmcodec_data *btfmcodec, uint8_t id)
|
||||
{
|
||||
struct list_head *head = &btfmcodec->config_head;
|
||||
struct hwep_configurations *hwep_configs, *tmp;
|
||||
int ret = -1;
|
||||
|
||||
list_for_each_entry_safe(hwep_configs, tmp, head, dai_list) {
|
||||
if (hwep_configs->stream_id == id) {
|
||||
BTFMCODEC_INFO("deleting configs with id %d", id);
|
||||
list_del(&hwep_configs->dai_list);
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void btfmcodec_dai_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component);
|
||||
struct btfmcodec_state_machine *state = &btfmcodec->states;
|
||||
|
||||
BTFMCODEC_DBG("dai->name: %s, dai->id: %d, dai->rate: %d", dai->name,
|
||||
dai->id, dai->rate);
|
||||
|
||||
if (btfmcodec_get_current_transport(state) != IDLE &&
|
||||
btfmcodec_get_current_transport(state) != BT_Connected) {
|
||||
BTFMCODEC_WARN("not allowing shutdown as state is:%s",
|
||||
coverttostring(btfmcodec_get_current_transport(state)));
|
||||
/* Delete stored configs */
|
||||
btfmcodec_delete_configs(btfmcodec, dai->id);
|
||||
} else {
|
||||
/* first master shutdown has to done */
|
||||
btfmcodec_hwep_shutdown(btfmcodec, dai->id, false);
|
||||
btfmcodec_delete_configs(btfmcodec, dai->id);
|
||||
if (!btfmcodec_is_valid_cache_avb(btfmcodec))
|
||||
btfmcodec_set_current_state(state, IDLE);
|
||||
else {
|
||||
BTFMCODEC_WARN("valid stream id is available not updating state\n");
|
||||
btfmcodec_set_current_state(state, BT_Connected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int btfmcodec_hwep_hw_params (struct btfmcodec_data *btfmcodec, uint32_t bps,
|
||||
uint32_t direction, uint8_t num_channels)
|
||||
{
|
||||
struct hwep_data *hwep_info = btfmcodec->hwep_info;
|
||||
struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)
|
||||
btfmcodec_get_dai_drvdata(hwep_info);
|
||||
|
||||
if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_hw_params) {
|
||||
return dai_drv->dai_ops->hwep_hw_params((void *)btfmcodec->hwep_info,
|
||||
bps, direction,
|
||||
num_channels);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int btfmcodec_dai_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component);
|
||||
struct btfmcodec_state_machine *state = &btfmcodec->states;
|
||||
uint32_t direction = substream->stream;
|
||||
|
||||
BTFMCODEC_DBG("dai->name = %s DAI-ID %x rate %d bps %d num_ch %d",
|
||||
dai->name, dai->id, params_rate(params), params_width(params),
|
||||
params_channels(params));
|
||||
|
||||
bits_per_second = params_width(params);
|
||||
num_channels = params_channels(params);
|
||||
if (btfmcodec_get_current_transport(state) != IDLE &&
|
||||
btfmcodec_get_current_transport(state) != BT_Connected) {
|
||||
BTFMCODEC_WARN("caching bps and num_channels as state is :%s",
|
||||
coverttostring(btfmcodec_get_current_transport(state)));
|
||||
} else {
|
||||
return btfmcodec_hwep_hw_params(btfmcodec, bits_per_second,
|
||||
direction, num_channels);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool btfmcodec_is_valid_cache_avb(struct btfmcodec_data *btfmcodec)
|
||||
{
|
||||
struct list_head *head = &btfmcodec->config_head;
|
||||
struct hwep_configurations *hwep_configs, *tmp;
|
||||
bool cache_avb = false;
|
||||
|
||||
list_for_each_entry_safe(hwep_configs, tmp, head, dai_list) {
|
||||
cache_avb = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return cache_avb;
|
||||
}
|
||||
|
||||
static int btfmcodec_check_and_cache_configs(struct btfmcodec_data *btfmcodec,
|
||||
uint32_t sampling_rate, uint32_t direction,
|
||||
int id, uint8_t codectype)
|
||||
{
|
||||
struct list_head *head = &btfmcodec->config_head;
|
||||
struct hwep_configurations *hwep_configs, *tmp;
|
||||
|
||||
list_for_each_entry_safe(hwep_configs, tmp, head, dai_list) {
|
||||
if (hwep_configs->stream_id == id) {
|
||||
BTFMCODEC_WARN("previous entry for %d is already available",
|
||||
id);
|
||||
list_del(&hwep_configs->dai_list);
|
||||
}
|
||||
}
|
||||
|
||||
hwep_configs = kzalloc(sizeof(struct hwep_configurations),
|
||||
GFP_KERNEL);
|
||||
if (!hwep_configs) {
|
||||
BTFMCODEC_ERR("failed to allocate memory");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
hwep_configs->stream_id = id; /* Stream identifier */
|
||||
hwep_configs->sample_rate = sampling_rate;
|
||||
hwep_configs->bit_width = bits_per_second;
|
||||
hwep_configs->codectype = codectype;
|
||||
hwep_configs->direction = direction;
|
||||
hwep_configs->num_channels = num_channels;
|
||||
|
||||
list_add(&hwep_configs->dai_list, head);
|
||||
BTFMCODEC_INFO("added dai id:%d to list with sampling_rate :%u, direction:%u", id, sampling_rate, direction);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int btfmcodec_configure_master(struct btfmcodec_data *btfmcodec, uint8_t id)
|
||||
{
|
||||
struct btfmcodec_char_device *btfmcodec_dev = btfmcodec->btfmcodec_dev;
|
||||
struct hwep_data *hwep_info = btfmcodec->hwep_info;
|
||||
struct master_hwep_configurations hwep_configs;
|
||||
struct btm_master_config_req config_req;
|
||||
struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)
|
||||
btfmcodec_get_dai_drvdata(hwep_info);
|
||||
wait_queue_head_t *rsp_wait_q =
|
||||
&btfmcodec_dev->rsp_wait_q[BTM_PKT_TYPE_MASTER_CONFIG_RSP];
|
||||
uint8_t *status = &btfmcodec_dev->status[BTM_PKT_TYPE_MASTER_CONFIG_RSP];
|
||||
int ret = 0;
|
||||
|
||||
if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_get_configs) {
|
||||
dai_drv->dai_ops->hwep_get_configs((void *)btfmcodec->hwep_info,
|
||||
&hwep_configs, id);
|
||||
} else {
|
||||
BTFMCODEC_ERR("No hwep_get_configs is set by hw ep driver");
|
||||
return -1;
|
||||
}
|
||||
|
||||
BTFMCODEC_INFO("framing packet for %d", id);
|
||||
config_req.opcode = BTM_BTFMCODEC_MASTER_CONFIG_REQ;
|
||||
config_req.len = BTM_MASTER_CONFIG_REQ_LEN;
|
||||
config_req.stream_id = hwep_configs.stream_id;
|
||||
config_req.device_id = hwep_configs.device_id;
|
||||
config_req.sample_rate = hwep_configs.sample_rate;
|
||||
config_req.bit_width = hwep_configs.bit_width;
|
||||
config_req.num_channels = hwep_configs.num_channels;
|
||||
config_req.channel_num = hwep_configs.chan_num;
|
||||
config_req.codec_id = hwep_configs.codectype;
|
||||
BTFMCODEC_DBG("================================================\n");
|
||||
BTFMCODEC_DBG("dma_config_req.len :%d", config_req.len);
|
||||
BTFMCODEC_DBG("dma_config_req.stream_id :%d", config_req.stream_id);
|
||||
BTFMCODEC_DBG("dma_config_req.device_id :%d", config_req.device_id);
|
||||
BTFMCODEC_DBG("dma_config_req.sample_rate :%d", config_req.sample_rate);
|
||||
BTFMCODEC_DBG("dma_config_req.bit_width :%d", config_req.bit_width);
|
||||
BTFMCODEC_DBG("dma_config_req.num_channels :%d", config_req.num_channels);
|
||||
BTFMCODEC_DBG("dma_config_req.channel_num :%d", config_req.channel_num);
|
||||
BTFMCODEC_DBG("dma_config_req.codec_id :%d", config_req.codec_id);
|
||||
BTFMCODEC_DBG("================================================\n");
|
||||
/* See if we need to protect below with lock */
|
||||
*status = BTM_WAITING_RSP;
|
||||
btfmcodec_dev_enqueue_pkt(btfmcodec_dev, &config_req, (config_req.len +
|
||||
BTM_HEADER_LEN));
|
||||
ret = wait_event_interruptible_timeout(*rsp_wait_q,
|
||||
(*status) != BTM_WAITING_RSP,
|
||||
msecs_to_jiffies(BTM_MASTER_CONFIG_RSP_TIMEOUT));
|
||||
if (ret == 0) {
|
||||
BTFMCODEC_ERR("failed to recevie response from BTADV audio Manager");
|
||||
ret = -ETIMEDOUT;
|
||||
} else {
|
||||
if (*status == BTM_RSP_RECV)
|
||||
return 0;
|
||||
else if (*status == BTM_FAIL_RESP_RECV ||
|
||||
*status == BTM_RSP_NOT_RECV_CLIENT_KILLED)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int btfmcodec_configure_dma(struct btfmcodec_data *btfmcodec, uint8_t id)
|
||||
{
|
||||
struct btfmcodec_char_device *btfmcodec_dev = btfmcodec->btfmcodec_dev;
|
||||
struct hwep_data *hwep_info = btfmcodec->hwep_info;
|
||||
struct hwep_dma_configurations dma_config;
|
||||
struct btm_dma_config_req dma_config_req;
|
||||
struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)
|
||||
btfmcodec_get_dai_drvdata(hwep_info);
|
||||
wait_queue_head_t *rsp_wait_q =
|
||||
&btfmcodec_dev->rsp_wait_q[BTM_PKT_TYPE_DMA_CONFIG_RSP];
|
||||
uint8_t *status = &btfmcodec_dev->status[BTM_PKT_TYPE_DMA_CONFIG_RSP];
|
||||
int ret = 0;
|
||||
|
||||
if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_get_configs) {
|
||||
dai_drv->dai_ops->hwep_get_configs((void *)btfmcodec->hwep_info,
|
||||
&dma_config, id);
|
||||
} else {
|
||||
BTFMCODEC_ERR("No hwep_get_configs is set by hw ep driver");
|
||||
return -1;
|
||||
}
|
||||
|
||||
BTFMCODEC_INFO("framing packet for %d", id);
|
||||
dma_config_req.opcode = BTM_BTFMCODEC_CODEC_CONFIG_DMA_REQ;
|
||||
dma_config_req.len = BTM_CODEC_CONFIG_DMA_REQ_LEN;
|
||||
dma_config_req.stream_id = dma_config.stream_id;
|
||||
dma_config_req.sample_rate = dma_config.sample_rate;
|
||||
dma_config_req.bit_width = dma_config.bit_width;
|
||||
dma_config_req.num_channels = dma_config.num_channels;
|
||||
dma_config_req.codec_id = dma_config.codectype;
|
||||
dma_config_req.lpaif = dma_config.lpaif;
|
||||
dma_config_req.inf_index = dma_config.inf_index;
|
||||
dma_config_req.active_channel_mask = dma_config.active_channel_mask;
|
||||
|
||||
BTFMCODEC_DBG("================================================\n");
|
||||
BTFMCODEC_DBG("dma_config_req.len :%d", dma_config_req.len);
|
||||
BTFMCODEC_DBG("dma_config_req.stream_id :%d", dma_config_req.stream_id);
|
||||
BTFMCODEC_DBG("dma_config_req.sample_rate :%d", dma_config_req.sample_rate);
|
||||
BTFMCODEC_DBG("dma_config_req.bit_width :%d", dma_config_req.bit_width);
|
||||
BTFMCODEC_DBG("dma_config_req.num_channels :%d", dma_config_req.num_channels);
|
||||
BTFMCODEC_DBG("dma_config_req.codec_id :%d", dma_config_req.codec_id);
|
||||
BTFMCODEC_DBG("dma_config_req.lpaif :%d", dma_config_req.lpaif);
|
||||
BTFMCODEC_DBG("dma_config_req.inf_index :%d", dma_config_req.inf_index);
|
||||
BTFMCODEC_DBG("dma_config_req.active_channel_mask :%d", dma_config_req.active_channel_mask);
|
||||
BTFMCODEC_DBG("================================================\n");
|
||||
|
||||
*status = BTM_WAITING_RSP;
|
||||
btfmcodec_dev_enqueue_pkt(btfmcodec_dev, &dma_config_req, (dma_config_req.len +
|
||||
BTM_HEADER_LEN));
|
||||
|
||||
ret = wait_event_interruptible_timeout(*rsp_wait_q,
|
||||
(*status) != BTM_WAITING_RSP,
|
||||
msecs_to_jiffies(BTM_MASTER_DMA_CONFIG_RSP_TIMEOUT));
|
||||
|
||||
if (ret == 0) {
|
||||
BTFMCODEC_ERR("failed to recevie response from BTADV audio Manager");
|
||||
ret = -ETIMEDOUT;
|
||||
} else {
|
||||
if (*status == BTM_RSP_RECV)
|
||||
return 0;
|
||||
else if (*status == BTM_FAIL_RESP_RECV ||
|
||||
*status == BTM_RSP_NOT_RECV_CLIENT_KILLED)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int btfmcodec_hwep_prepare(struct btfmcodec_data *btfmcodec, uint32_t sampling_rate,
|
||||
uint32_t direction, int id)
|
||||
{
|
||||
struct hwep_data *hwep_info = btfmcodec->hwep_info;
|
||||
struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)
|
||||
btfmcodec_get_dai_drvdata(hwep_info);
|
||||
struct btfmcodec_state_machine *state = &btfmcodec->states;
|
||||
|
||||
int ret;
|
||||
if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_prepare) {
|
||||
ret = dai_drv->dai_ops->hwep_prepare((void *)hwep_info, sampling_rate,
|
||||
direction, id);
|
||||
BTFMCODEC_ERR("%s: hwep info %d", __func__, hwep_info->flags);
|
||||
if (ret == 0 && test_bit(BTADV_AUDIO_MASTER_CONFIG, &hwep_info->flags)) {
|
||||
ret = btfmcodec_configure_master(btfmcodec, (uint8_t)id);
|
||||
if (ret < 0) {
|
||||
BTFMCODEC_ERR("failed to configure master error %d", ret);
|
||||
btfmcodec_set_current_state(state, IDLE);
|
||||
} else {
|
||||
btfmcodec_set_current_state(state, BT_Connected);
|
||||
}
|
||||
} else if (ret == 0 && test_bit(BTADV_CONFIGURE_DMA, &hwep_info->flags)) {
|
||||
ret = btfmcodec_configure_dma(btfmcodec, (uint8_t)id);
|
||||
if (ret < 0) {
|
||||
BTFMCODEC_ERR("failed to configure Codec DMA %d", ret);
|
||||
btfmcodec_set_current_state(state, IDLE);
|
||||
} else {
|
||||
btfmcodec_set_current_state(state, BT_Connected);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int btfmcodec_notify_usecase_start(struct btfmcodec_data *btfmcodec,
|
||||
uint8_t transport)
|
||||
{
|
||||
struct btfmcodec_char_device *btfmcodec_dev = btfmcodec->btfmcodec_dev;
|
||||
struct btm_usecase_start_ind ind;
|
||||
|
||||
ind.opcode = BTM_BTFMCODEC_USECASE_START_IND;
|
||||
ind.len = BTM_USECASE_START_IND_LEN;
|
||||
ind.transport = transport;
|
||||
return btfmcodec_dev_enqueue_pkt(btfmcodec_dev, &ind, (ind.len +
|
||||
BTM_HEADER_LEN));
|
||||
}
|
||||
|
||||
static int btfmcodec_dai_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component);
|
||||
struct btfmcodec_state_machine *state = &btfmcodec->states;
|
||||
struct hwep_data *hwep_info = btfmcodec->hwep_info;
|
||||
struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)
|
||||
btfmcodec_get_dai_drvdata(hwep_info);
|
||||
uint8_t *codectype = dai_drv->dai_ops->hwep_codectype;
|
||||
uint32_t sampling_rate = dai->rate;
|
||||
uint32_t direction = substream->stream;
|
||||
int id = dai->id;
|
||||
int ret ;
|
||||
|
||||
BTFMCODEC_INFO("dai->name: %s, dai->id: %d, dai->rate: %d direction: %d",
|
||||
dai->name, id, sampling_rate, direction);
|
||||
|
||||
ret = btfmcodec_check_and_cache_configs(btfmcodec, sampling_rate,
|
||||
direction, id, *codectype);
|
||||
if (btfmcodec_get_current_transport(state) != IDLE &&
|
||||
btfmcodec_get_current_transport(state) != BT_Connected) {
|
||||
BTFMCODEC_WARN("cached required info as state is:%s",
|
||||
coverttostring(btfmcodec_get_current_transport(state)));
|
||||
btfmcodec_notify_usecase_start(btfmcodec, BTADV);
|
||||
} else {
|
||||
ret = btfmcodec_hwep_prepare(btfmcodec, sampling_rate, direction, id);
|
||||
/* if (ret >= 0) {
|
||||
btfmcodec_check_and_cache_configs(btfmcodec, sampling_rate, direction,
|
||||
id, *codectype);
|
||||
}
|
||||
*/ }
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int btfmcodec_hwep_set_channel_map(void *hwep_info, unsigned int tx_num,
|
||||
unsigned int *tx_slot, unsigned int rx_num,
|
||||
unsigned int *rx_slot)
|
||||
{
|
||||
struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)
|
||||
btfmcodec_get_dai_drvdata(hwep_info);
|
||||
|
||||
if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_set_channel_map) {
|
||||
return dai_drv->dai_ops->hwep_set_channel_map(hwep_info, tx_num,
|
||||
tx_slot, rx_num,
|
||||
rx_slot);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int btfmcodec_dai_set_channel_map(struct snd_soc_dai *dai,
|
||||
unsigned int tx_num, unsigned int *tx_slot,
|
||||
unsigned int rx_num, unsigned int *rx_slot)
|
||||
{
|
||||
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component);
|
||||
struct btfmcodec_state_machine states = btfmcodec->states;
|
||||
|
||||
BTFMCODEC_DBG("");
|
||||
// ToDo: check whether hw_params has to allowed when state if different
|
||||
if (states.current_state != IDLE) {
|
||||
BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state));
|
||||
} else {
|
||||
return btfmcodec_hwep_set_channel_map((void *)btfmcodec->hwep_info, tx_num,
|
||||
tx_slot, rx_num, rx_slot);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int btfmcodec_hwep_get_channel_map(void *hwep_info, unsigned int *tx_num,
|
||||
unsigned int *tx_slot, unsigned int *rx_num,
|
||||
unsigned int *rx_slot, int id)
|
||||
{
|
||||
struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)
|
||||
btfmcodec_get_dai_drvdata(hwep_info);
|
||||
|
||||
if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_get_channel_map) {
|
||||
return dai_drv->dai_ops->hwep_get_channel_map(hwep_info, tx_num,
|
||||
tx_slot, rx_num,
|
||||
rx_slot, id);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int btfmcodec_dai_get_channel_map(struct snd_soc_dai *dai,
|
||||
unsigned int *tx_num, unsigned int *tx_slot,
|
||||
unsigned int *rx_num, unsigned int *rx_slot)
|
||||
{
|
||||
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component);
|
||||
// struct btfmcodec_state_machine states = btfmcodec->states;
|
||||
|
||||
BTFMCODEC_DBG("");
|
||||
// ToDo: get_channel_map is not needed for new driver
|
||||
/* if (states.current_state != IDLE) {
|
||||
BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state));
|
||||
} else {
|
||||
*/ return btfmcodec_hwep_get_channel_map((void *)btfmcodec->hwep_info,
|
||||
tx_num, tx_slot, rx_num,
|
||||
rx_slot, dai->id);
|
||||
// }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void btfmcodec_wq_hwep_configure(struct work_struct *work)
|
||||
{
|
||||
struct btfmcodec_char_device *btfmcodec_dev = container_of(work,
|
||||
struct btfmcodec_char_device,
|
||||
wq_hwep_configure);
|
||||
struct btfmcodec_data *btfmcodec = (struct btfmcodec_data *)btfmcodec_dev->btfmcodec;
|
||||
struct list_head *head = &btfmcodec->config_head;
|
||||
struct hwep_configurations *hwep_configs = NULL, *tmp;
|
||||
int ret;
|
||||
int idx = BTM_PKT_TYPE_HWEP_CONFIG;
|
||||
uint32_t sample_rate, direction;
|
||||
uint8_t id, bit_width, codectype, num_channels;
|
||||
|
||||
list_for_each_entry_safe(hwep_configs, tmp, head, dai_list) {
|
||||
id = hwep_configs->stream_id;
|
||||
sample_rate = hwep_configs->sample_rate;
|
||||
bit_width = hwep_configs->bit_width;
|
||||
codectype = hwep_configs->codectype;
|
||||
direction = hwep_configs->direction;
|
||||
num_channels = hwep_configs->num_channels;
|
||||
|
||||
BTFMCODEC_INFO("configuring dai id:%d with sampling rate:%d bit_width:%d", id, sample_rate, bit_width);
|
||||
ret = btfmcodec_hwep_startup(btfmcodec);
|
||||
if (ret >= 0)
|
||||
ret = btfmcodec_hwep_hw_params(btfmcodec, bit_width, direction, num_channels);
|
||||
if (ret >= 0)
|
||||
ret = btfmcodec_hwep_prepare(btfmcodec, sample_rate, direction, id);
|
||||
if (ret < 0) {
|
||||
BTFMCODEC_ERR("failed to configure hwep %d", hwep_configs->stream_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
btfmcodec_dev->status[idx] = BTM_FAIL_RESP_RECV;
|
||||
else
|
||||
btfmcodec_dev->status[idx] = BTM_RSP_RECV;
|
||||
|
||||
wake_up_interruptible(&btfmcodec_dev->rsp_wait_q[idx]);
|
||||
}
|
||||
static struct snd_soc_dai_ops btfmcodec_dai_ops = {
|
||||
.startup = btfmcodec_dai_startup,
|
||||
.shutdown = btfmcodec_dai_shutdown,
|
||||
.hw_params = btfmcodec_dai_hw_params,
|
||||
.prepare = btfmcodec_dai_prepare,
|
||||
.set_channel_map = btfmcodec_dai_set_channel_map,
|
||||
.get_channel_map = btfmcodec_dai_get_channel_map,
|
||||
};
|
||||
|
||||
static int btfmcodec_adsp_ssr_notify(struct notifier_block *nb,
|
||||
unsigned long action, void *data)
|
||||
{
|
||||
struct btfmcodec_data *btfmcodec = container_of(nb,
|
||||
struct btfmcodec_data, notifier.nb);
|
||||
struct btfmcodec_char_device *btfmcodec_dev = btfmcodec->btfmcodec_dev;
|
||||
struct btm_adsp_state_ind state_ind;
|
||||
|
||||
switch (action) {
|
||||
case QCOM_SSR_BEFORE_SHUTDOWN: {
|
||||
BTFMCODEC_WARN("LPASS SSR triggered");
|
||||
break;
|
||||
} case QCOM_SSR_AFTER_SHUTDOWN: {
|
||||
BTFMCODEC_WARN("LPASS SSR Completed");
|
||||
break;
|
||||
} case QCOM_SSR_BEFORE_POWERUP: {
|
||||
BTFMCODEC_WARN("LPASS booted up after SSR");
|
||||
break;
|
||||
} case QCOM_SSR_AFTER_POWERUP: {
|
||||
BTFMCODEC_WARN("LPASS booted up completely");
|
||||
state_ind.opcode = BTM_BTFMCODEC_ADSP_STATE_IND;
|
||||
state_ind.len = BTM_ADSP_STATE_IND_LEN;
|
||||
state_ind.action = (uint32_t)action;
|
||||
btfmcodec_dev_enqueue_pkt(btfmcodec_dev, &state_ind,
|
||||
(state_ind.len +
|
||||
BTM_HEADER_LEN));
|
||||
break;
|
||||
} default:
|
||||
BTFMCODEC_WARN("unhandled action id %lu", action);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int btfm_register_codec(struct hwep_data *hwep_info)
|
||||
{
|
||||
struct btfmcodec_data *btfmcodec;
|
||||
struct btfmcodec_char_device *btfmcodec_dev;
|
||||
struct device *dev;
|
||||
struct hwep_dai_driver *dai_drv;
|
||||
int i, ret;
|
||||
|
||||
btfmcodec = btfm_get_btfmcodec();
|
||||
btfmcodec_dev = btfmcodec->btfmcodec_dev;
|
||||
dev = &btfmcodec->dev;
|
||||
|
||||
btfmcodec->notifier.nb.notifier_call = btfmcodec_adsp_ssr_notify;
|
||||
btfmcodec->notifier.notifier = qcom_register_ssr_notifier("lpass",
|
||||
&btfmcodec->notifier.nb);
|
||||
if (IS_ERR(btfmcodec->notifier.notifier)) {
|
||||
ret = PTR_ERR(btfmcodec->notifier.notifier);
|
||||
BTFMCODEC_ERR("Failed to register SSR notification: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
btfmcodec_dai_info = kzalloc((sizeof(struct snd_soc_dai_driver) * hwep_info->num_dai), GFP_KERNEL);
|
||||
if (!btfmcodec_dai_info) {
|
||||
BTFMCODEC_ERR("failed to allocate memory");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < hwep_info->num_dai; i++) {
|
||||
dai_drv = &hwep_info->dai_drv[i];
|
||||
btfmcodec_dai_info[i].name = dai_drv->dai_name;
|
||||
btfmcodec_dai_info[i].id = dai_drv->id;
|
||||
btfmcodec_dai_info[i].capture = dai_drv->capture;
|
||||
btfmcodec_dai_info[i].playback = dai_drv->playback;
|
||||
btfmcodec_dai_info[i].ops = &btfmcodec_dai_ops;
|
||||
}
|
||||
|
||||
BTFMCODEC_INFO("Adding %d dai support to codec", hwep_info->num_dai);
|
||||
BTFMCODEC_INFO("slim bus driver name:%s", dev->driver->name);
|
||||
ret = snd_soc_register_component(dev, &btfmcodec_codec_component,
|
||||
btfmcodec_dai_info, hwep_info->num_dai);
|
||||
BTFMCODEC_INFO("Dev node address: %p", dev);
|
||||
BTFMCODEC_INFO("btfmcodec address :%p", btfmcodec);
|
||||
BTFMCODEC_INFO("HWEPINFO address:%p", hwep_info);
|
||||
BTFMCODEC_INFO("btfmcodec_dev INFO address:%p", btfmcodec->btfmcodec_dev);
|
||||
BTFMCODEC_INFO("before wq_hwep_shutdown:%p", btfmcodec_dev->wq_hwep_shutdown);
|
||||
BTFMCODEC_INFO("before wq_prepare_bearer:%p", btfmcodec_dev->wq_prepare_bearer);
|
||||
INIT_WORK(&btfmcodec_dev->wq_hwep_shutdown, btfmcodec_wq_hwep_shutdown);
|
||||
INIT_WORK(&btfmcodec_dev->wq_prepare_bearer, btfmcodec_wq_prepare_bearer);
|
||||
INIT_WORK(&btfmcodec_dev->wq_hwep_configure, btfmcodec_wq_hwep_configure);
|
||||
BTFMCODEC_INFO("after wq_hwep_shutdown:%p", btfmcodec_dev->wq_hwep_shutdown);
|
||||
BTFMCODEC_INFO("after wq_prepare_bearer:%p", btfmcodec_dev->wq_prepare_bearer);
|
||||
BTFMCODEC_INFO("btfmcodec_wq_prepare_bearer:%p", btfmcodec_wq_prepare_bearer);
|
||||
BTFMCODEC_INFO("btfmcodec_wq_hwep_shutdown:%p", btfmcodec_wq_hwep_shutdown);
|
||||
|
||||
if (isCpSupported()) {
|
||||
if (!strcmp(hwep_info->driver_name, "btfmslim"))
|
||||
set_bit(BTADV_AUDIO_MASTER_CONFIG, &hwep_info->flags);
|
||||
else if (!strcmp(hwep_info->driver_name, "btfmswr_slave"))
|
||||
set_bit(BTADV_CONFIGURE_DMA, &hwep_info->flags);
|
||||
|
||||
BTFMCODEC_INFO("%s: master %d dma codec %d", __func__,
|
||||
(int)test_bit(BTADV_AUDIO_MASTER_CONFIG, &hwep_info->flags),
|
||||
(int)test_bit(BTADV_CONFIGURE_DMA, &hwep_info->flags));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void btfm_unregister_codec(void)
|
||||
{
|
||||
struct btfmcodec_data *btfmcodec;
|
||||
|
||||
btfmcodec = btfm_get_btfmcodec();
|
||||
snd_soc_unregister_component(&btfmcodec->dev);
|
||||
}
|
108
qcom/opensource/bt-kernel/btfmcodec/include/btfm_codec.h
Normal file
108
qcom/opensource/bt-kernel/btfmcodec/include/btfm_codec.h
Normal file
@ -0,0 +1,108 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_BTFM_CODEC_H
|
||||
#define __LINUX_BTFM_CODEC_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include "btfm_codec_hw_interface.h"
|
||||
|
||||
#define BTM_BTFMCODEC_DEFAULT_LOG_LVL 0x03
|
||||
#define BTM_BTFMCODEC_DEBUG_LOG_LVL 0x04
|
||||
#define BTM_BTFMCODEC_INFO_LOG_LVL 0x08
|
||||
|
||||
static uint8_t log_lvl = BTM_BTFMCODEC_DEFAULT_LOG_LVL;
|
||||
|
||||
#define BTFMCODEC_ERR(fmt, arg...) pr_err("%s: " fmt "\n", __func__, ## arg)
|
||||
#define BTFMCODEC_WARN(fmt, arg...) pr_warn("%s: " fmt "\n", __func__, ## arg)
|
||||
#define BTFMCODEC_DBG(fmt, arg...) { if(log_lvl >= BTM_BTFMCODEC_DEBUG_LOG_LVL) \
|
||||
pr_err("%s: " fmt "\n", __func__, ## arg); \
|
||||
else \
|
||||
pr_debug("%s: " fmt "\n", __func__, ## arg); \
|
||||
}
|
||||
#define BTFMCODEC_INFO(fmt, arg...) { if(log_lvl >= BTM_BTFMCODEC_INFO_LOG_LVL) \
|
||||
pr_err("%s: " fmt "\n", __func__, ## arg);\
|
||||
else \
|
||||
pr_info("%s: " fmt "\n", __func__, ## arg);\
|
||||
}
|
||||
|
||||
#define DEVICE_NAME_MAX_LEN 64
|
||||
#define BTM_CP_UPDATE 0xbfaf
|
||||
|
||||
typedef enum btfmcodec_states {
|
||||
/*Default state of kernel proxy driver */
|
||||
IDLE = 0,
|
||||
/* Waiting for BT bearer indication after configuring HW ports */
|
||||
BT_Connecting = 1,
|
||||
/* When BT is active transport */
|
||||
BT_Connected = 2,
|
||||
/* Waiting for BTADV Audio bearer switch indications */
|
||||
BTADV_AUDIO_Connecting = 3,
|
||||
/* When BTADV audio is active transport */
|
||||
BTADV_AUDIO_Connected = 4
|
||||
} btfmcodec_state;
|
||||
|
||||
enum btfm_pkt_type {
|
||||
BTM_PKT_TYPE_PREPARE_REQ = 0,
|
||||
BTM_PKT_TYPE_MASTER_CONFIG_RSP,
|
||||
BTM_PKT_TYPE_MASTER_SHUTDOWN_RSP,
|
||||
BTM_PKT_TYPE_BEARER_SWITCH_IND,
|
||||
BTM_PKT_TYPE_HWEP_SHUTDOWN,
|
||||
BTM_PKT_TYPE_HWEP_CONFIG,
|
||||
BTM_PKT_TYPE_DMA_CONFIG_RSP,
|
||||
BTM_PKT_TYPE_MAX,
|
||||
};
|
||||
|
||||
|
||||
char *coverttostring(enum btfmcodec_states);
|
||||
struct btfmcodec_state_machine {
|
||||
struct mutex state_machine_lock;
|
||||
btfmcodec_state prev_state;
|
||||
btfmcodec_state current_state;
|
||||
btfmcodec_state next_state;
|
||||
};
|
||||
|
||||
struct btfmcodec_char_device {
|
||||
struct cdev cdev;
|
||||
refcount_t active_clients;
|
||||
struct mutex lock;
|
||||
int reuse_minor;
|
||||
char dev_name[DEVICE_NAME_MAX_LEN];
|
||||
struct workqueue_struct *workqueue;
|
||||
struct sk_buff_head rxq;
|
||||
struct work_struct rx_work;
|
||||
struct work_struct wq_hwep_shutdown;
|
||||
struct work_struct wq_prepare_bearer;
|
||||
struct work_struct wq_hwep_configure;
|
||||
wait_queue_head_t readq;
|
||||
spinlock_t tx_queue_lock;
|
||||
struct sk_buff_head txq;
|
||||
wait_queue_head_t rsp_wait_q[BTM_PKT_TYPE_MAX];
|
||||
uint8_t status[BTM_PKT_TYPE_MAX];
|
||||
void *btfmcodec;
|
||||
};
|
||||
|
||||
struct adsp_notifier {
|
||||
void *notifier;
|
||||
struct notifier_block nb;
|
||||
};
|
||||
|
||||
struct btfmcodec_data {
|
||||
struct device dev;
|
||||
struct btfmcodec_state_machine states;
|
||||
struct btfmcodec_char_device *btfmcodec_dev;
|
||||
struct hwep_data *hwep_info;
|
||||
struct list_head config_head;
|
||||
struct adsp_notifier notifier;
|
||||
struct mutex hwep_drv_lock;
|
||||
};
|
||||
|
||||
struct btfmcodec_data *btfm_get_btfmcodec(void);
|
||||
bool isCpSupported(void);
|
||||
#endif /*__LINUX_BTFM_CODEC_H */
|
@ -0,0 +1,22 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_BTFM_CODEC_BTADV_INTERFACE_H
|
||||
#define __LINUX_BTFM_CODEC_BTADV_INTERFACE_H
|
||||
|
||||
enum transport_type {
|
||||
BT = 1,
|
||||
BTADV,
|
||||
NONE,
|
||||
};
|
||||
|
||||
static char *transport_type_text[] = {"BT", "BTADV", "NONE"};
|
||||
|
||||
void btfmcodec_set_current_state(struct btfmcodec_state_machine *, btfmcodec_state);
|
||||
void btfmcodec_wq_prepare_bearer(struct work_struct *);
|
||||
void btfmcodec_wq_hwep_shutdown(struct work_struct *);
|
||||
void btfmcodec_initiate_hwep_shutdown(struct btfmcodec_char_device *btfmcodec_dev);
|
||||
btfmcodec_state btfmcodec_get_current_transport(struct btfmcodec_state_machine *state);
|
||||
#endif /* __LINUX_BTFM_CODEC_BTADV_INTERFACE_H */
|
@ -0,0 +1,116 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_BTFM_CODEC_HW_INTERFACE_H
|
||||
#define __LINUX_BTFM_CODEC_HW_INTERFACE_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/tlv.h>
|
||||
|
||||
/* This flag is set to indicate btfm codec driver is
|
||||
* responsible to configure master.
|
||||
*/
|
||||
#define BTADV_AUDIO_MASTER_CONFIG 0
|
||||
#define BTADV_CONFIGURE_DMA 1
|
||||
#define DEVICE_NAME_MAX_LEN 64
|
||||
|
||||
struct hwep_configurations {
|
||||
void *btfmcodec;
|
||||
uint8_t stream_id;
|
||||
uint32_t sample_rate;
|
||||
uint8_t bit_width;
|
||||
uint8_t codectype;
|
||||
uint32_t direction;
|
||||
uint8_t num_channels;
|
||||
struct list_head dai_list;
|
||||
};
|
||||
|
||||
struct master_hwep_configurations {
|
||||
uint8_t stream_id;
|
||||
uint32_t device_id;
|
||||
uint32_t sample_rate;
|
||||
uint8_t bit_width;
|
||||
uint8_t num_channels;
|
||||
uint8_t chan_num;
|
||||
uint8_t codectype;
|
||||
uint16_t direction;
|
||||
};
|
||||
|
||||
struct hwep_dma_configurations {
|
||||
uint8_t stream_id;
|
||||
uint32_t sample_rate;
|
||||
uint8_t bit_width;
|
||||
uint8_t num_channels;
|
||||
uint8_t codectype;
|
||||
uint8_t lpaif; // Low power audio interface
|
||||
uint8_t inf_index; // interface index
|
||||
uint8_t active_channel_mask;
|
||||
};
|
||||
|
||||
struct hwep_comp_drv {
|
||||
int (*hwep_probe) (struct snd_soc_component *);
|
||||
void (*hwep_remove) (struct snd_soc_component *);
|
||||
unsigned int (*hwep_read)(struct snd_soc_component *, unsigned int );
|
||||
int (*hwep_write)(struct snd_soc_component *, unsigned int,
|
||||
unsigned int);
|
||||
};
|
||||
|
||||
struct hwep_dai_ops {
|
||||
int (*hwep_startup)(void *);
|
||||
void (*hwep_shutdown)(void *, int);
|
||||
int (*hwep_hw_params)(void *, uint32_t, uint32_t, uint8_t);
|
||||
int (*hwep_prepare)(void *, uint32_t, uint32_t, int);
|
||||
int (*hwep_set_channel_map)(void *, unsigned int, unsigned int *,
|
||||
unsigned int, unsigned int *);
|
||||
int (*hwep_get_channel_map)(void *, unsigned int *, unsigned int *,
|
||||
unsigned int *, unsigned int *, int);
|
||||
int (*hwep_get_configs)(void *a, void *b, uint8_t c);
|
||||
uint8_t *hwep_codectype;
|
||||
};
|
||||
|
||||
struct hwep_dai_driver {
|
||||
const char *dai_name;
|
||||
unsigned int id;
|
||||
struct snd_soc_pcm_stream capture;
|
||||
struct snd_soc_pcm_stream playback;
|
||||
struct hwep_dai_ops *dai_ops;
|
||||
};
|
||||
|
||||
struct hwep_data {
|
||||
struct device *dev;
|
||||
char driver_name [DEVICE_NAME_MAX_LEN];
|
||||
struct hwep_comp_drv *drv;
|
||||
struct hwep_dai_driver *dai_drv;
|
||||
struct snd_kcontrol_new *mixer_ctrl;
|
||||
int num_dai;
|
||||
int num_mixer_ctrl;
|
||||
unsigned long flags;
|
||||
};
|
||||
|
||||
int btfmcodec_register_hw_ep(struct hwep_data *);
|
||||
int btfmcodec_unregister_hw_ep(char *);
|
||||
// ToDo below.
|
||||
/*
|
||||
#if IS_ENABLED(CONFIG_SLIM_BTFM_CODEC_DRV)
|
||||
int btfmcodec_register_hw_ep(struct hwep_data *);
|
||||
int btfmcodec_unregister_hw_ep(char *);
|
||||
#else
|
||||
static inline int btfmcodec_register_hw_ep(struct hwep_data *hwep_info)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int btfmcodec_unregister_hw_ep(char *dev_name)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif
|
||||
*/
|
||||
#endif /*__LINUX_BTFM_CODEC_HW_INTERFACE_H */
|
@ -0,0 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_BTFM_CODEC_INTERFACE
|
||||
#define __LINUX_BTFM_CODEC_INTERFACE
|
||||
|
||||
#include "btfm_codec_hw_interface.h"
|
||||
int btfm_register_codec(struct hwep_data *hwep_info);
|
||||
void btfm_unregister_codec(void);
|
||||
#endif /*__LINUX_BTFM_CODEC_INTERFACE */
|
136
qcom/opensource/bt-kernel/btfmcodec/include/btfm_codec_pkt.h
Normal file
136
qcom/opensource/bt-kernel/btfmcodec/include/btfm_codec_pkt.h
Normal file
@ -0,0 +1,136 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_BTFM_CODEC_PKT_H
|
||||
#define __LINUX_BTFM_CODEC_PKT_H
|
||||
|
||||
typedef uint32_t btm_opcode;
|
||||
|
||||
struct btm_req {
|
||||
btm_opcode opcode;
|
||||
uint32_t len;
|
||||
uint8_t *data;
|
||||
}__attribute__((packed));
|
||||
|
||||
struct btm_rsp {
|
||||
btm_opcode opcode;
|
||||
uint8_t status;
|
||||
}__attribute__((packed));
|
||||
|
||||
struct btm_ind {
|
||||
btm_opcode opcode;
|
||||
uint32_t len;
|
||||
uint8_t *data;
|
||||
}__attribute__((packed));
|
||||
|
||||
struct btm_ctrl_pkt {
|
||||
btm_opcode opcode;
|
||||
uint32_t len;
|
||||
uint8_t active_transport;
|
||||
uint8_t status;
|
||||
}__attribute__((packed));
|
||||
|
||||
#define BTM_BTFMCODEC_PREPARE_AUDIO_BEARER_SWITCH_REQ 0x50000000
|
||||
#define BTM_BTFMCODEC_PREPARE_AUDIO_BEARER_SWITCH_RSP 0x50000001
|
||||
#define BTM_BTFMCODEC_MASTER_CONFIG_REQ 0x50000002
|
||||
#define BTM_BTFMCODEC_MASTER_CONFIG_RSP 0x50000003
|
||||
#define BTM_BTFMCODEC_MASTER_SHUTDOWN_REQ 0x50000004
|
||||
#define BTM_BTFMCODEC_CTRL_MASTER_SHUTDOWN_RSP 0x50000005
|
||||
#define BTM_BTFMCODEC_CODEC_CONFIG_DMA_REQ 0x58000006
|
||||
#define BTM_BTFMCODEC_CODEC_CONFIG_DMA_RSP 0x58000007
|
||||
|
||||
#define BTM_BTFMCODEC_BEARER_SWITCH_IND 0x58000001
|
||||
#define BTM_BTFMCODEC_TRANSPORT_SWITCH_FAILED_IND 0x58000002
|
||||
#define BTM_BTFMCODEC_ADSP_STATE_IND 0x58000003
|
||||
#define BTM_BTFMCODEC_CTRL_LOG_LVL_IND 0x58000004
|
||||
|
||||
#define BTM_MASTER_CONFIG_REQ_LEN 13
|
||||
#define BTM_MASTER_CONFIG_RSP_TIMEOUT 5000
|
||||
#define BTM_MASTER_DMA_CONFIG_RSP_TIMEOUT 5000
|
||||
#define BTM_HEADER_LEN 8
|
||||
#define BTM_PREPARE_AUDIO_BEARER_SWITCH_RSP_LEN 2
|
||||
#define BTM_MASTER_CONFIG_RSP_LEN 2
|
||||
#define BTM_CODEC_CONFIG_DMA_RSP_LEN 2
|
||||
#define BTM_MASTER_SHUTDOWN_REQ_LEN 1
|
||||
#define BTM_PREPARE_AUDIO_BEARER_SWITCH_REQ_LEN 1
|
||||
#define BTM_BEARER_SWITCH_IND_LEN 1
|
||||
#define BTM_LOG_LVL_IND_LEN 1
|
||||
#define BTM_ADSP_STATE_IND_LEN 4
|
||||
#define BTM_CODEC_CONFIG_DMA_REQ_LEN 11
|
||||
|
||||
#define BTM_BTFMCODEC_USECASE_START_IND 0x58000008
|
||||
#define BTM_USECASE_START_IND_LEN 1
|
||||
|
||||
enum rx_status {
|
||||
/* Waiting for response */
|
||||
BTM_WAITING_RSP,
|
||||
/* Response recevied */
|
||||
BTM_RSP_RECV,
|
||||
/* Response recevied with failure status*/
|
||||
BTM_FAIL_RESP_RECV,
|
||||
/* Response not recevied, but client killed */
|
||||
BTM_RSP_NOT_RECV_CLIENT_KILLED,
|
||||
};
|
||||
|
||||
enum btfm_kp_status {
|
||||
/* KP processed message succesfully */
|
||||
MSG_SUCCESS = 0,
|
||||
/* Error while processing the message */
|
||||
MSG_FAILED,
|
||||
/* Wrong transport type selected by BTADV audio manager */
|
||||
MSG_WRONG_TRANSPORT_TYPE,
|
||||
/* Timeout triggered to receive bearer switch indications*/
|
||||
MSG_INTERNAL_TIMEOUT,
|
||||
MSG_FAILED_TO_CONFIGURE_HWEP,
|
||||
MSG_FAILED_TO_SHUTDOWN_HWEP,
|
||||
MSG_ERR_WHILE_SHUTING_DOWN_HWEP,
|
||||
};
|
||||
|
||||
struct btm_master_config_req {
|
||||
btm_opcode opcode;
|
||||
uint32_t len;
|
||||
uint8_t stream_id;
|
||||
uint32_t device_id;
|
||||
uint32_t sample_rate;
|
||||
uint8_t bit_width;
|
||||
uint8_t num_channels;
|
||||
uint8_t channel_num;
|
||||
uint8_t codec_id;
|
||||
}__attribute__((packed));
|
||||
|
||||
struct btm_dma_config_req {
|
||||
btm_opcode opcode;
|
||||
uint32_t len;
|
||||
uint8_t stream_id;
|
||||
uint32_t sample_rate;
|
||||
uint8_t bit_width;
|
||||
uint8_t num_channels;
|
||||
uint8_t codec_id;
|
||||
uint8_t lpaif; // Low power audio interface
|
||||
uint8_t inf_index; // interface index
|
||||
uint8_t active_channel_mask;
|
||||
} __packed;
|
||||
|
||||
struct btm_usecase_start_ind {
|
||||
btm_opcode opcode;
|
||||
uint32_t len;
|
||||
uint8_t transport;
|
||||
} __packed;
|
||||
|
||||
struct btm_master_shutdown_req {
|
||||
btm_opcode opcode;
|
||||
uint32_t len;
|
||||
uint8_t stream_id;
|
||||
}__attribute__((packed));
|
||||
|
||||
struct btm_adsp_state_ind {
|
||||
btm_opcode opcode;
|
||||
uint32_t len;
|
||||
uint32_t action;
|
||||
} __attribute__((packed));
|
||||
|
||||
int btfmcodec_dev_enqueue_pkt(struct btfmcodec_char_device *, void *, int);
|
||||
bool btfmcodec_is_valid_cache_avb(struct btfmcodec_data *);
|
||||
#endif /* __LINUX_BTFM_CODEC_PKT_H*/
|
721
qcom/opensource/bt-kernel/include/btpower.h
Normal file
721
qcom/opensource/bt-kernel/include/btpower.h
Normal file
@ -0,0 +1,721 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_BLUETOOTH_POWER_H
|
||||
#define __LINUX_BLUETOOTH_POWER_H
|
||||
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mailbox_client.h>
|
||||
#include <linux/mailbox/qmp.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
/*
|
||||
* voltage regulator information required for configuring the
|
||||
* bluetooth chipset
|
||||
*/
|
||||
|
||||
enum power_modes {
|
||||
POWER_DISABLE = 0,
|
||||
POWER_ENABLE,
|
||||
POWER_RETENTION,
|
||||
POWER_DISABLE_RETENTION,
|
||||
};
|
||||
|
||||
enum SubSystem {
|
||||
BLUETOOTH = 1,
|
||||
UWB,
|
||||
};
|
||||
|
||||
enum power_states {
|
||||
IDLE = 0,
|
||||
BT_ON,
|
||||
UWB_ON,
|
||||
ALL_CLIENTS_ON,
|
||||
};
|
||||
|
||||
enum retention_states {
|
||||
/* Default state */
|
||||
RETENTION_IDLE = 0,
|
||||
/* When BT is only client and it is in retention_state */
|
||||
BT_IN_RETENTION,
|
||||
/* BT is retention mode and UWB powered ON triggered */
|
||||
BT_OUT_OF_RETENTION,
|
||||
/* When UWB is only client and it is in retention_state */
|
||||
UWB_IN_RETENTION,
|
||||
/* UWB is retention mode and BT powered ON triggered */
|
||||
UWB_OUT_OF_RETENTION,
|
||||
/* Both clients are voted for retention */
|
||||
BOTH_CLIENTS_IN_RETENTION,
|
||||
};
|
||||
|
||||
enum grant_return_values {
|
||||
ACCESS_GRANTED = 0,
|
||||
ACCESS_DENIED = 1,
|
||||
ACCESS_RELEASED = 2,
|
||||
ACCESS_DISALLOWED = -1,
|
||||
};
|
||||
|
||||
enum grant_states {
|
||||
/* Default state */
|
||||
NO_GRANT_FOR_ANY_SS = 0,
|
||||
NO_OTHER_CLIENT_WAITING_FOR_GRANT,
|
||||
BT_HAS_GRANT,
|
||||
UWB_HAS_GRANT,
|
||||
BT_WAITING_FOR_GRANT,
|
||||
UWB_WAITING_FOR_GRANT,
|
||||
};
|
||||
|
||||
enum cores {
|
||||
BT_CORE = 0,
|
||||
UWB_CORE,
|
||||
PLATFORM_CORE
|
||||
};
|
||||
|
||||
enum ssr_states {
|
||||
SUB_STATE_IDLE = 0,
|
||||
SSR_ON_BT,
|
||||
BT_SSR_COMPLETED,
|
||||
SSR_ON_UWB,
|
||||
UWB_SSR_COMPLETED,
|
||||
REG_BT_PID,
|
||||
REG_UWB_PID,
|
||||
};
|
||||
|
||||
enum plt_pwr_state {
|
||||
POWER_ON_BT = 0,
|
||||
POWER_OFF_BT,
|
||||
POWER_ON_UWB,
|
||||
POWER_OFF_UWB,
|
||||
POWER_ON_BT_RETENION,
|
||||
POWER_ON_UWB_RETENION,
|
||||
BT_ACCESS_REQ,
|
||||
UWB_ACCESS_REQ,
|
||||
BT_RELEASE_ACCESS,
|
||||
UWB_RELEASE_ACCESS,
|
||||
BT_MAX_PWR_STATE,
|
||||
};
|
||||
|
||||
enum {
|
||||
PWR_WAITING_RSP = -2,
|
||||
PWR_RSP_RECV = 0,
|
||||
PWR_FAIL_RSP_RECV = -1,
|
||||
PWR_CLIENT_KILLED,
|
||||
};
|
||||
|
||||
static inline char *ConvertGrantRetToString(enum grant_return_values state) {
|
||||
|
||||
switch (state) {
|
||||
case ACCESS_GRANTED:
|
||||
return "ACCESS_GRANTED";
|
||||
case ACCESS_DENIED:
|
||||
return "ACCESS_DENIED";
|
||||
case ACCESS_RELEASED:
|
||||
return "ACCESS_RELEASED";
|
||||
case ACCESS_DISALLOWED:
|
||||
return "ACCESS_DISALLOWED";
|
||||
default:
|
||||
return "INVALID STATE";
|
||||
}
|
||||
}
|
||||
|
||||
static inline char *ConvertGrantToString(enum grant_states state) {
|
||||
|
||||
switch (state) {
|
||||
case NO_GRANT_FOR_ANY_SS:
|
||||
return "NO_GRANT_FOR_ANY_SS";
|
||||
case NO_OTHER_CLIENT_WAITING_FOR_GRANT:
|
||||
return "NO_OTHER_CLIENT_WAITING_FOR_GRANT";
|
||||
case BT_HAS_GRANT:
|
||||
return "BT_HAS_GRANT";
|
||||
case UWB_HAS_GRANT:
|
||||
return "UWB_HAS_GRANT";
|
||||
case BT_WAITING_FOR_GRANT:
|
||||
return "BT_WAITING_FOR_GRANT";
|
||||
case UWB_WAITING_FOR_GRANT:
|
||||
return "UWB_WAITING_FOR_GRANT";
|
||||
default:
|
||||
return "INVALID STATE";
|
||||
}
|
||||
}
|
||||
|
||||
static inline char *ConvertRetentionModeToString(int state) {
|
||||
|
||||
switch (state) {
|
||||
case IDLE:
|
||||
return "Both client not in Retention";
|
||||
case BT_IN_RETENTION:
|
||||
return "BT in Retention";
|
||||
case BT_OUT_OF_RETENTION:
|
||||
return "BT is out off Retention";
|
||||
case UWB_IN_RETENTION:
|
||||
return "UWB in Retention";
|
||||
case UWB_OUT_OF_RETENTION:
|
||||
return "UWB is out off Retention";
|
||||
case BOTH_CLIENTS_IN_RETENTION:
|
||||
return "Both client in Retention";
|
||||
default:
|
||||
return "Retention state = INVALID STATE";
|
||||
}
|
||||
}
|
||||
|
||||
static inline char *ConvertClientReqToString(int arg) {
|
||||
|
||||
switch (arg) {
|
||||
case POWER_DISABLE:
|
||||
return "Power OFF";
|
||||
case POWER_ENABLE:
|
||||
return "Power ON";
|
||||
case POWER_RETENTION:
|
||||
return "Power Retention";
|
||||
default:
|
||||
return "INVALID STATE";
|
||||
}
|
||||
}
|
||||
|
||||
static inline char *ConvertPowerStatusToString(int state) {
|
||||
|
||||
switch (state) {
|
||||
case IDLE:
|
||||
return "Current state is ALL Client OFF";
|
||||
case BT_ON:
|
||||
return "Current state is BT powered ON";
|
||||
case UWB_ON:
|
||||
return "Current state is UWB powered ON";
|
||||
case ALL_CLIENTS_ON:
|
||||
return "Current state is ALL Client ON";
|
||||
default:
|
||||
return "Current state is = INVALID STATE";
|
||||
}
|
||||
}
|
||||
|
||||
static inline char *ConvertSsrStatusToString(int state) {
|
||||
|
||||
switch (state) {
|
||||
case SUB_STATE_IDLE:
|
||||
return "and No SSR";
|
||||
case SSR_ON_BT:
|
||||
return "and SSR on BT";
|
||||
case BT_SSR_COMPLETED:
|
||||
return "and BT SSR completed";
|
||||
case SSR_ON_UWB:
|
||||
return "and SSR on UWB";
|
||||
case UWB_SSR_COMPLETED:
|
||||
return "and UWB SSR completed";
|
||||
default:
|
||||
return "SSR STATE = INVALID STATE";
|
||||
}
|
||||
}
|
||||
|
||||
static inline char *ConvertPowerReqToString(int arg) {
|
||||
|
||||
switch (arg) {
|
||||
case POWER_ON_BT:
|
||||
return "POWER_ON_BT";
|
||||
case POWER_OFF_BT:
|
||||
return "POWER_OFF_BT";
|
||||
case POWER_ON_UWB:
|
||||
return "POWER_ON_UWB";
|
||||
case POWER_OFF_UWB:
|
||||
return "POWER_OFF_UWB";
|
||||
case POWER_ON_BT_RETENION:
|
||||
return "POWER_ON_BT_RETENION";
|
||||
case POWER_ON_UWB_RETENION:
|
||||
return "POWER_ON_UWB_RETENION";
|
||||
case BT_ACCESS_REQ:
|
||||
return "BT_ACCESS_REQ";
|
||||
case UWB_ACCESS_REQ:
|
||||
return "UWB_ACCESS_REQ";
|
||||
case BT_RELEASE_ACCESS:
|
||||
return "BT_RELEASE_ACCESS";
|
||||
case UWB_RELEASE_ACCESS:
|
||||
return "UWB_RELEASE_ACCESS";
|
||||
case BT_MAX_PWR_STATE:
|
||||
return "BT_MAX_PWR_STATE";
|
||||
default:
|
||||
return "INVALID STATE";
|
||||
}
|
||||
};
|
||||
|
||||
static inline char *ConvertRegisterModeToString(int reg_mode) {
|
||||
|
||||
switch (reg_mode) {
|
||||
case POWER_DISABLE:
|
||||
return "vote off";
|
||||
case POWER_ENABLE:
|
||||
return "vote on";
|
||||
case POWER_RETENTION:
|
||||
return "vote for retention";
|
||||
case POWER_DISABLE_RETENTION:
|
||||
return "vote offretention";
|
||||
default:
|
||||
return "INVALID STATE";
|
||||
}
|
||||
}
|
||||
|
||||
enum UwbPrimaryReasonCode{
|
||||
UWB_HOST_REASON_DEFAULT_NONE = 0x00, //INVALID REASON
|
||||
UWB_HOST_REASON_PERI_SOC_CRASHED = 0x01, //PERI SOC WAS CRASHED
|
||||
UWB_HOST_REASON_PERI_SOC_CRASHED_DIAG_SSR = 0x02, //PERI SOC CRASHED DIAG INITIATED SSR
|
||||
UWB_HOST_REASON_INIT_FAILED = 0x03, //HOST INITIALIZATION FAILED
|
||||
UWB_HOST_REASON_CLOSE_RCVD_DURING_INIT = 0x04, //CLOSE RECEIVED FROM STACK DURING SOC INIT
|
||||
UWB_HOST_REASON_ERROR_READING_DATA_FROM_Q2SPI = 0x05, //ERROR READING DATA FROM Q2SPI
|
||||
UWB_HOST_REASON_WRITE_FAIL_SPCL_BUFF_CRASH_SOC = 0x06, //FAILED TO WRITE SPECIAL BYTES TO CRASH SOC
|
||||
UWB_HOST_REASON_RX_THREAD_STUCK = 0x07, //RX THREAD STUCK
|
||||
UWB_HOST_REASON_SSR_CMD_TIMEDOUT = 0x08, //SSR DUE TO CMD TIMED OUT
|
||||
UWB_HOST_REASON_SSR_INVALID_BYTES_RCVD = 0x0A, //INVALID HCI CMD TYPE RECEIVED
|
||||
UWB_HOST_REASON_SSR_RCVD_LARGE_PKT_FROM_SOC = 0x0B, //SSR DUE TO LARGE PKT RECVIVED FROM SOC
|
||||
UWB_HOST_REASON_SSR_UNABLE_TO_WAKEUP_SOC = 0x0C, //UNABLE TO WAKE UP SOC
|
||||
UWB_HOST_REASON_CMD_TIMEDOUT_SOC_WAIT_TIMEOUT = 0x0D, //COMMAND TIMEOUT AND SOC CRASH WAIT TIMEOUT
|
||||
UWB_HOST_REASON_INV_BYTES_SOC_WAIT_TIMEOUT = 0x0F, //INVALID BYTES AND SOC CRASH WAIT TIMEOUT
|
||||
UWB_HOST_REASON_SOC_WAKEUP_FAILED_SOC_WAIT_TIMEOUT = 0x10, //SOC WAKEUP FAILURE AND SOC CRASH WAIT TIMEOUT
|
||||
UWB_HOST_REASON_SOC_CRASHED_DIAG_SSR_SOC_WAIT_TIMEOUT = 0x11, //SOC CRASHED DIAG INITIATED SSR CRASH WAIT TIMEOUT
|
||||
UWB_HOST_REASON_NONE_SOC_WAIT_TIMEOUT = 0x12, //INVALID FAILURE AND SOC CRASH WAIT TIMEOUT
|
||||
UWB_HOST_REASON_SOC_DEINIT_STUCK = 0x13, //SOC DEINIT STUCK
|
||||
UWB_HOST_REASON_SSR_INTERNAL_CMD_TIMEDOUT = 0x14, //SSR DUE TO CMD INTERNAL TIMED OUT
|
||||
UWB_HOST_REASON_FAILED_TO_SEND_INTERNAL_CMD = 0x15, //FAILED TO SEND INTERNAL CMD
|
||||
UWB_HOST_REASON_SSR_SLEEP_IND_NOT_RCVD = 0x16, //SOC DID NOT RCVD SLEEP IND DURING CLOSE
|
||||
UWB_HOST_REASON_UWB_SOC_CRASHED = 0xC1, //UWB SOC WAS CRASHED
|
||||
UWB_HOST_REASON_UWB_SOC_CRASHED_DIAG_SSR = 0xC2, //UWB SOC CRASHED DIAG INITIATED SSR
|
||||
UWB_HOST_REASON_DIAG_LOG_API_STUCK = 0x39, //DIAG log API stuck.
|
||||
UWB_HOST_REASON_PERI_CRASH_ON_OTHER_SS = 0x3A, //Peripheral core crash detected in BT SS
|
||||
UWB_HOST_REASON_CRASH_EVT_INDUCED = 0x60, //Packet Type from SoC for inducing crash
|
||||
};
|
||||
|
||||
enum UwbSecondaryReasonCode{
|
||||
UWB_SOC_REASON_DEFAULT = 0x00,
|
||||
UWB_SOC_REASON_TX_RX_INVALID_PKT = 0x40,
|
||||
UWB_SOC_REASON_TX_RX_INVALID_PKT_LENE = 0x41,
|
||||
UWB_SOC_REASON_TX_RX_OVERFLOW_BUFF = 0x42,
|
||||
UWB_SOC_REASON_UNKNOWN = 0x81,
|
||||
UWB_SOC_REASON_SW_REQUESTED = 0x82,
|
||||
UWB_SOC_REASON_STACK_OVERFLOW = 0x83,
|
||||
UWB_SOC_REASON_EXCEPTION = 0x84,
|
||||
UWB_SOC_REASON_ASSERT = 0x85,
|
||||
UWB_SOC_REASON_TRAP = 0x86,
|
||||
UWB_SOC_REASON_OS_FATAL = 0x87,
|
||||
UWB_SOC_REASON_HCI_RESET = 0x88,
|
||||
UWB_SOC_REASON_PATCH_RESET = 0x89,
|
||||
UWB_SOC_REASON_ABT = 0x8A,
|
||||
UWB_SOC_REASON_RAMMASK = 0x8B,
|
||||
UWB_SOC_REASON_PREBARK = 0x8C,
|
||||
UWB_SOC_REASON_BUSERROR = 0x8D,
|
||||
UWB_SOC_REASON_IO_FATAL = 0x8E,
|
||||
UWB_SOC_REASON_SSR_CMD = 0x8F,
|
||||
UWB_SOC_REASON_POWERON = 0x90,
|
||||
UWB_SOC_REASON_WATCHDOG = 0x91,
|
||||
UWB_SOC_REASON_RAMMASK_RGN1 = 0x92,
|
||||
UWB_SOC_REASON_RAMMASK_RGN0 = 0x93,
|
||||
UWB_SOC_REASON_Q6_WATCHDOG = 0x94,
|
||||
UWB_SOC_REASON_ZEALIS_RAM_MASK_RGN0 = 0x95,
|
||||
UWB_SOC_REASON_ZEALIS_RAM_MASK_RGN1 = 0x96,
|
||||
UWB_SOC_REASON_APSS_RESET = 0x97,
|
||||
UWB_SOC_REASON_TIME_RESET = 0x98,
|
||||
UWB_SOC_REASON_AUDIOSS_RESET = 0x99,
|
||||
UWB_SOC_REASON_HOST_WARMRESET = 0x9A,
|
||||
UWB_SOC_REASON_HOST_NMI_INIT = 0x9B,
|
||||
UWB_SOC_REASON_PANIC_FAULT = 0x9C,
|
||||
UWB_SOC_REASON_EARLY_TRAP = 0x9D,
|
||||
UWB_SOC_REASON_INSTR_ADDR_MISALGIN = 0x9E,
|
||||
UWB_SOC_REASON_INSTR_ACCESS_FAULT = 0x9F,
|
||||
UWB_SOC_REASON_ILLEGAL_INSTR = 0xA0,
|
||||
UWB_SOC_REASON_BREAKPOINT_EXCEPTION = 0xA1,
|
||||
UWB_SOC_REASON_LOAD_ADDR_MISALIGN = 0xA2,
|
||||
UWB_SOC_REASON_LOAD_ACCESS_FAULT = 0xA3,
|
||||
UWB_SOC_REASON_STORE_ADDR_MISALGN = 0xA4,
|
||||
UWB_SOC_REASON_STORE_ACCESS_FAULT = 0xA5,
|
||||
UWB_SOC_REASON_ECALL_UMODE = 0xA6,
|
||||
UWB_SOC_REASON_ECALL_MMODE = 0xA7,
|
||||
UWB_SOC_REASON_STACK_UNDERFLOW = 0xA8,
|
||||
UWB_SOC_REASON_MACHINE_EXIT_INT = 0xA9,
|
||||
UWB_SOC_REASON_PERF_MONITOR_OVERFLOW = 0xAA,
|
||||
UWB_SOC_REASON_EXT_SUBSYS_RESET = 0xAB,
|
||||
UWB_SOC_REASON_IPC_STALL = 0xAC,
|
||||
UWB_SOC_REASON_PEER_CPU0_NMI = 0xAD,
|
||||
UWB_SOC_REASON_PEER_CPU1_NMI = 0xAE,
|
||||
UWB_SOC_REASON_PEER_CPU2_NMI = 0xAF,
|
||||
UWB_SOC_REASON_TX_RX_INVALID_PKT_FATAL = 0xC0,
|
||||
UWB_SOC_REASON_TX_RX_INVALID_LEN_FATAL = 0xC1,
|
||||
UWB_SOC_REASON_TX_RX_OVERFLOW_FATAL = 0xC2,
|
||||
UWB_SOC_REASON_INVALID_STACK = 0xF0,
|
||||
UWB_SOC_REASON_INVALID_MCI_MSG_RCVD = 0xF1,
|
||||
UWB_HOST_REASON_PERI_GETVER_SEND_STUCK = 0x18,
|
||||
UWB_HOST_REASON_PERI_GETVER_NO_RSP_RCVD = 0x19,
|
||||
UWB_HOST_REASON_PERI_PATCH_DNLD_STUCK = 0x1B,
|
||||
UWB_HOST_REASON_PERI_GETBOARDID_CMD_STUCK = 0x1C,
|
||||
UWB_HOST_REASON_PERI_NVM_DNLD_STUCK = 0x1D,
|
||||
UWB_HOST_REASON_PERI_RESET_STUCK = 0x1E,
|
||||
UWB_HOST_REASON_PERI_GETBLDINFO_CMD_STUCK = 0x1F,
|
||||
UWB_HOST_REASON_PERI_ENHLOG_CMD_STUCK = 0x21,
|
||||
UWB_HOST_REASON_DIAGINIT_STUCK = 0x22,
|
||||
UWB_HOST_REASON_DIAGDEINIT_STUCK = 0x23,
|
||||
UWB_HOST_REASON_SECURE_BRIDGE_CMD_STUCK = 0x26,
|
||||
UWB_HOST_REASON_FAILED_TO_SEND_CMD = 0x27,
|
||||
UWB_HOST_REASON_PERI_RESET_CC_NOT_RCVD = 0x28,
|
||||
UWB_HOST_REASON_HCI_PRE_SHUTDOWN_CC_NOT_RCVD = 0x29,
|
||||
UWB_HOST_REASON_FAILED_TO_RECEIVE_SLEEP_IND = 0x2B,
|
||||
UWB_HOST_REASON_POWER_ON_REGS_STUCK = 0x2C,
|
||||
UWB_HOST_REASON_RX_THREAD_START_STUCK = 0x2D,
|
||||
UWB_HOST_REASON_GET_LOCALADDR_STUCK = 0x2E,
|
||||
UWB_HOST_REASON_OTP_INFO_GET_CMD_STUCK = 0x2F,
|
||||
UWB_HOST_REASON_FILE_SYSTEM_CALL_STUCK = 0x30,
|
||||
UWB_HOST_REASON_PROPERTY_GET_STUCK = 0x31,
|
||||
UWB_HOST_REASON_PROPERTY_SET_STUCK = 0x32,
|
||||
UWB_HOST_REASON_PERI_RAM_PATCH_READ_STUCK = 0x33,
|
||||
UWB_HOST_REASON_PERI_NVM_PATCH_READ_STUCK = 0x34,
|
||||
UWB_HOST_REASON_POWER_IOCTL_STUCK = 0x36,
|
||||
UWB_HOST_REASON_PERI_PATCH_CONFIG_CMD_STUCK = 0x37,
|
||||
UWB_HOST_REASON_PERI_PATCH_CONFIG_FAILED = 0x38,
|
||||
UWB_HOST_REASON_UWB_GETVER_SEND_STUCK = 0x39,
|
||||
UWB_HOST_REASON_UWB_GETVER_NO_RSP_RCVD = 0x3A,
|
||||
UWB_HOST_REASON_SOC_NAME_UNKOWN = 0x3B,
|
||||
UWB_HOST_REASON_PERI_GETVER_CMD_FAILED = 0x3C,
|
||||
UWB_HOST_REASON_BAUDRATE_CHANGE_FAILED = 0x3D,
|
||||
UWB_HOST_REASON_PERI_TLV_DOWNLOAD_FAILED = 0x3E,
|
||||
UWB_HOST_REASON_PERI_GETBLDINFO_CMD_FAILED = 0x3F,
|
||||
UWB_HOST_REASON_PERI_RESET_CMD_FAILED = 0x40,
|
||||
UWB_HOST_REASON_MEMORY_ALLOCATION_FAILED = 0x42,
|
||||
UWB_HOST_REASON_READ_THREAD_START_FAILED = 0x43,
|
||||
UWB_HOST_REASON_HW_FLOW_ON_FAILED = 0x44,
|
||||
UWB_HOST_REASON_PERI_NVM_FILE_NOT_FOUND = 0x45,
|
||||
UWB_HOST_REASON_UWB_RAM_PATCH_READ_STUCK = 0x48,
|
||||
UWB_HOST_REASON_UWB_NVM_PATCH_READ_STUCK = 0x49,
|
||||
UWB_HOST_REASON_UWB_NVM_FILE_NOT_FOUND = 0x4A,
|
||||
UWB_HOST_REASON_UWB_GETBLDINFO_CMD_FAILED = 0x4B,
|
||||
UWB_HOST_REASON_UWB_PATCH_DNLD_STUCK = 0x4C,
|
||||
UWB_HOST_REASON_UWB_NVM_DNLD_STUCK = 0x4D,
|
||||
UWB_HOST_REASON_UWB_GETBLDINFO_CMD_STUCK = 0x4E,
|
||||
UWB_HOST_REASON_PERI_ACTIVATE_CMD_STUCK = 0x4F,
|
||||
UWB_HOST_REASON_PERI_ARBITRATION_CMD_STUCK = 0x50,
|
||||
UWB_HOST_REASON_PERI_ARBITRATION_NTF_STUCK = 0x51,
|
||||
UWB_HOST_REASON_INITIALIZATION_FAILED = 0x52,
|
||||
UWB_HOST_REASON_UWB_RESET_CC_NOT_RCVD = 0x53,
|
||||
UWB_HOST_REASON_UWB_ACTIVATE_CC_NOT_RCVD = 0x54,
|
||||
UWB_HOST_REASON_TME_ACTIVATE_CC_NOT_RCVD = 0x55,
|
||||
UWB_HOST_REASON_Q2SPI_INIT_STUCK = 0x56,
|
||||
UWB_HOST_REASON_Q2SPI_INIT_FAILED = 0x57,
|
||||
UWB_HOST_REASON_UWB_TLV_DOWNLOAD_FAILED = 0x58,
|
||||
UWB_HOST_REASON_UWB_ENHLOG_CMD_STUCK = 0x59,
|
||||
UWB_HOST_REASON_UWB_GETVER_CMD_FAILED = 0x5A,
|
||||
UWB_HOST_REASON_UWB_PATCH_CONFIG_CMD_STUCK = 0x5B,
|
||||
UWB_HOST_REASON_UWB_PATCH_CONFIG_CMD_FAILED = 0x5C,
|
||||
UWB_HOST_REASON_UWB_RESET_STUCK = 0x5D,
|
||||
UWB_HOST_REASON_PERI_ACTIVATE_NTF_STUCK = 0x5E,
|
||||
UWB_HOST_REASON_UWB_CORE_RESET_CMD_FAILED = 0x5F,
|
||||
UWB_HOST_REASON_TME_ARBITRATION_CMD_STUCK = 0x60,
|
||||
UWB_HOST_REASON_TME_ARBITRATION_NTF_STUCK = 0x61,
|
||||
UWB_HOST_REASON_TME_GETVER_SEND_STUCK = 0x62,
|
||||
UWB_HOST_REASON_TME_GETVER_NO_RSP_RCVD = 0x63,
|
||||
UWB_HOST_REASON_TME_GETVER_CMD_FAILED = 0x64,
|
||||
UWB_HOST_REASON_TME_PATCH_DNLD_STUCK = 0x65,
|
||||
UWB_HOST_REASON_TME_RESET_STUCK = 0x66,
|
||||
UWB_HOST_REASON_TME_GETBLDINFO_CMD_STUCK = 0x67,
|
||||
UWB_HOST_REASON_TME_GETBLDINFO_CMD_FAILED = 0x68,
|
||||
UWB_HOST_REASON_TME_RAM_PATCH_READ_STUCK = 0x69,
|
||||
Q2SPI_REASON_DEFAULT = 0xFF
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
enum UwbSecondaryReasonCode reason;
|
||||
char reasonstr[50];
|
||||
} UwbSecondaryReasonMap;
|
||||
|
||||
typedef struct {
|
||||
enum UwbPrimaryReasonCode reason;
|
||||
char reasonstr[100];
|
||||
} UwbPrimaryReasonMap;
|
||||
|
||||
static UwbPrimaryReasonMap uwbPriReasonMap[] = {
|
||||
{UWB_HOST_REASON_DEFAULT_NONE, "Invalid reason"},
|
||||
{UWB_HOST_REASON_PERI_SOC_CRASHED, "Peri SOC crashed"},
|
||||
{UWB_HOST_REASON_UWB_SOC_CRASHED, "UWB SOC crashed"},
|
||||
{UWB_HOST_REASON_PERI_SOC_CRASHED_DIAG_SSR, "Peri SOC crashed with diag initiated SSR"},
|
||||
{UWB_HOST_REASON_UWB_SOC_CRASHED_DIAG_SSR, "UWB SOC crashed with diag initiated SSR"},
|
||||
{UWB_HOST_REASON_INIT_FAILED, "Init failed"},
|
||||
{UWB_HOST_REASON_CLOSE_RCVD_DURING_INIT, "Close received from stack during SOC init"},
|
||||
{UWB_HOST_REASON_ERROR_READING_DATA_FROM_Q2SPI, "Error reading data from Q2SPI"},
|
||||
{UWB_HOST_REASON_WRITE_FAIL_SPCL_BUFF_CRASH_SOC, "Failed to write special bytes to crash SOC"},
|
||||
{UWB_HOST_REASON_RX_THREAD_STUCK, "Rx Thread Stuck"},
|
||||
{UWB_HOST_REASON_SSR_CMD_TIMEDOUT, "SSR due to command timed out"},
|
||||
{UWB_HOST_REASON_SSR_RCVD_LARGE_PKT_FROM_SOC, "Large packet received from SOC"},
|
||||
{UWB_HOST_REASON_SSR_UNABLE_TO_WAKEUP_SOC, "Unable to wake SOC"},
|
||||
{UWB_HOST_REASON_CMD_TIMEDOUT_SOC_WAIT_TIMEOUT, "Command timedout and SOC crash wait timeout"},
|
||||
{UWB_HOST_REASON_INV_BYTES_SOC_WAIT_TIMEOUT,
|
||||
"Invalid bytes received and SOC crash wait timeout"},
|
||||
{UWB_HOST_REASON_SOC_WAKEUP_FAILED_SOC_WAIT_TIMEOUT,
|
||||
"SOC Wakeup failed and SOC crash wait timeout"},
|
||||
{UWB_HOST_REASON_SOC_CRASHED_DIAG_SSR_SOC_WAIT_TIMEOUT,
|
||||
"SOC crashed with diag initiated SSR and SOC wait timeout"},
|
||||
{UWB_HOST_REASON_NONE_SOC_WAIT_TIMEOUT, "Invalid Reason and SOC crash wait timeout"},
|
||||
{UWB_HOST_REASON_SOC_DEINIT_STUCK, "SOC Deinit Stuck"},
|
||||
{UWB_HOST_REASON_SSR_INTERNAL_CMD_TIMEDOUT, "SSR due to internal Command timeout"},
|
||||
{UWB_HOST_REASON_FAILED_TO_SEND_INTERNAL_CMD, "Failed to send internal command"},
|
||||
{UWB_HOST_REASON_SSR_SLEEP_IND_NOT_RCVD, "Failed to receive SLEEP IND during close"},
|
||||
{UWB_HOST_REASON_PERI_CRASH_ON_OTHER_SS, "Peri SOC crashed detected on BT SS"},
|
||||
{UWB_HOST_REASON_DIAG_LOG_API_STUCK, "DIAG log API stuck"}
|
||||
};
|
||||
|
||||
static UwbSecondaryReasonMap uwbSecReasonMap[] = {
|
||||
{ UWB_SOC_REASON_DEFAULT, "Default"},
|
||||
{ UWB_SOC_REASON_TX_RX_INVALID_PKT, "Tx/Rx Inavlid Packet"},
|
||||
{ UWB_SOC_REASON_TX_RX_INVALID_PKT_LENE, "Tx/Rx Invalid Pkt Len"},
|
||||
{ UWB_SOC_REASON_TX_RX_OVERFLOW_BUFF, "Tx/Rx Overflow Buffer"},
|
||||
{ UWB_SOC_REASON_UNKNOWN, "Unknown"},
|
||||
{ UWB_SOC_REASON_TX_RX_INVALID_PKT_FATAL, "Tx/Rx invalid packet fatal error"},
|
||||
{ UWB_SOC_REASON_TX_RX_INVALID_LEN_FATAL, "Tx/Rx invalid length fatal error"},
|
||||
{ UWB_SOC_REASON_TX_RX_OVERFLOW_BUFF, "Tx/Rx Overflow Buffer"},
|
||||
{ UWB_SOC_REASON_SW_REQUESTED, "SW Requested"},
|
||||
{ UWB_SOC_REASON_STACK_OVERFLOW, "Stack Overflow"},
|
||||
{ UWB_SOC_REASON_EXCEPTION, "Exception"},
|
||||
{ UWB_SOC_REASON_ASSERT, "Assert"},
|
||||
{ UWB_SOC_REASON_TRAP, "Trap"},
|
||||
{ UWB_SOC_REASON_OS_FATAL, "OS Fatal"},
|
||||
{ UWB_SOC_REASON_HCI_RESET, "HCI Reset"},
|
||||
{ UWB_SOC_REASON_PATCH_RESET, "Patch Reset"},
|
||||
{ UWB_SOC_REASON_ABT, "SoC Abort"},
|
||||
{ UWB_SOC_REASON_RAMMASK, "RAM MASK"},
|
||||
{ UWB_SOC_REASON_PREBARK, "PREBARK"},
|
||||
{ UWB_SOC_REASON_BUSERROR, "Bus error"},
|
||||
{ UWB_SOC_REASON_IO_FATAL, "IO fatal eror"},
|
||||
{ UWB_SOC_REASON_SSR_CMD, "SSR CMD"},
|
||||
{ UWB_SOC_REASON_POWERON, "Power ON"},
|
||||
{ UWB_SOC_REASON_WATCHDOG, "Watchdog"},
|
||||
{ UWB_SOC_REASON_RAMMASK_RGN1, "RAMMASK RGN1"},
|
||||
{ UWB_SOC_REASON_RAMMASK_RGN0, "RAMMASK RGN0"},
|
||||
{ UWB_SOC_REASON_Q6_WATCHDOG, "Q6 Watchdog"},
|
||||
{ UWB_SOC_REASON_ZEALIS_RAM_MASK_RGN0, "ZEALIS RAM MASK RGN0"},
|
||||
{ UWB_SOC_REASON_ZEALIS_RAM_MASK_RGN1, "ZEALIS RAM MASK RGN1"},
|
||||
{ UWB_SOC_REASON_APSS_RESET, "APSS reset"},
|
||||
{ UWB_SOC_REASON_TIME_RESET, "Time reset"},
|
||||
{ UWB_SOC_REASON_AUDIOSS_RESET, "Audioss reset"},
|
||||
{ UWB_SOC_REASON_HOST_WARMRESET, "Host warm reset"},
|
||||
{ UWB_SOC_REASON_HOST_NMI_INIT, "Host NMI init"},
|
||||
{ UWB_SOC_REASON_PANIC_FAULT, "Panic Fault"},
|
||||
{ UWB_SOC_REASON_EARLY_TRAP, "Early Trap"},
|
||||
{ UWB_SOC_REASON_INSTR_ADDR_MISALGIN, "Instruction Address Misalign"},
|
||||
{ UWB_SOC_REASON_INSTR_ACCESS_FAULT, "Instruction Access Fault"},
|
||||
{ UWB_SOC_REASON_ILLEGAL_INSTR, "Illegal Instruction"},
|
||||
{ UWB_SOC_REASON_BREAKPOINT_EXCEPTION, "Breakpoint Exception"},
|
||||
{ UWB_SOC_REASON_LOAD_ADDR_MISALIGN, "Load Address Misalign"},
|
||||
{ UWB_SOC_REASON_LOAD_ACCESS_FAULT, "Load Access Fault"},
|
||||
{ UWB_SOC_REASON_STORE_ADDR_MISALGN, "Store Address Misalign"},
|
||||
{ UWB_SOC_REASON_STORE_ACCESS_FAULT, "Store Access Fault"},
|
||||
{ UWB_SOC_REASON_ECALL_UMODE, "Ecall Umode"},
|
||||
{ UWB_SOC_REASON_ECALL_MMODE, "Ecall Mmode"},
|
||||
{ UWB_SOC_REASON_STACK_UNDERFLOW, "Stack Underflow"},
|
||||
{ UWB_SOC_REASON_MACHINE_EXIT_INT, "Machine Exit Int"},
|
||||
{ UWB_SOC_REASON_PERF_MONITOR_OVERFLOW, "Perf Monitor Overflow"},
|
||||
{ UWB_SOC_REASON_EXT_SUBSYS_RESET, "Ext Subsystem Reset"},
|
||||
{ UWB_SOC_REASON_IPC_STALL, "IPC Stall"},
|
||||
{ UWB_SOC_REASON_PEER_CPU0_NMI, "Crash in Peri CPU"},
|
||||
{ UWB_SOC_REASON_PEER_CPU1_NMI, "Crash in BT CPU"},
|
||||
{ UWB_SOC_REASON_PEER_CPU2_NMI, "Crash in UWB CPU"},
|
||||
{ UWB_SOC_REASON_INVALID_STACK, "Invalid Stack"},
|
||||
{ UWB_SOC_REASON_INVALID_MCI_MSG_RCVD, "Invalid MCI message received"},
|
||||
{ UWB_HOST_REASON_PERI_GETVER_SEND_STUCK, "PeriGetVerSendStuck"},
|
||||
{ UWB_HOST_REASON_UWB_GETVER_SEND_STUCK, "UwbGetVerSendStuck"},
|
||||
{ UWB_HOST_REASON_TME_GETVER_SEND_STUCK, "TmeGetVerSendStuck"},
|
||||
{ UWB_HOST_REASON_PERI_GETVER_NO_RSP_RCVD, "PeriGetVerNoRspRcvd"},
|
||||
{ UWB_HOST_REASON_UWB_GETVER_NO_RSP_RCVD, "UwbGetVerNoRspRcvd"},
|
||||
{ UWB_HOST_REASON_TME_GETVER_NO_RSP_RCVD, "TmeGetVerNoRspRcvd"},
|
||||
{ UWB_HOST_REASON_PERI_PATCH_DNLD_STUCK, "PeriPatchDnldStuck"},
|
||||
{ UWB_HOST_REASON_UWB_PATCH_DNLD_STUCK, "UwbPatchDnldStuck"},
|
||||
{ UWB_HOST_REASON_TME_PATCH_DNLD_STUCK, "TmePatchDnldStuck"},
|
||||
{ UWB_HOST_REASON_PERI_GETBOARDID_CMD_STUCK, "PeriGetBoardIdStuck"},
|
||||
{ UWB_HOST_REASON_PERI_NVM_DNLD_STUCK, "PeriNvmDnldStuck"},
|
||||
{ UWB_HOST_REASON_UWB_NVM_DNLD_STUCK, "UwbNvmDnldStuck"},
|
||||
{ UWB_HOST_REASON_PERI_RESET_STUCK, "PeriResetStuck"},
|
||||
{ UWB_HOST_REASON_UWB_RESET_STUCK, "UwbResetStuck"},
|
||||
{ UWB_HOST_REASON_TME_RESET_STUCK, "TmeResetStuck"},
|
||||
{ UWB_HOST_REASON_PERI_GETBLDINFO_CMD_STUCK, "PeriGetBldInfoCmdStuck"},
|
||||
{ UWB_HOST_REASON_UWB_GETBLDINFO_CMD_STUCK, "UwbGetBldInfoCmdStuck"},
|
||||
{ UWB_HOST_REASON_TME_GETBLDINFO_CMD_STUCK, "TmeGetBldInfoCmdStuck"},
|
||||
{ UWB_HOST_REASON_PERI_ENHLOG_CMD_STUCK, "Peri EnhLogCmdStuck"},
|
||||
{ UWB_HOST_REASON_UWB_ENHLOG_CMD_STUCK, "Uwb EnhLogCmdStuck"},
|
||||
{ UWB_HOST_REASON_DIAGINIT_STUCK, "DiagInitStuck"},
|
||||
{ UWB_HOST_REASON_DIAGDEINIT_STUCK, "DiagDeinitStuck"},
|
||||
{ UWB_HOST_REASON_FAILED_TO_SEND_CMD, "Failed to send internal cmd"},
|
||||
{ UWB_HOST_REASON_PERI_RESET_CC_NOT_RCVD, "Peri Reset Cmd CC Not Rcvd"},
|
||||
{ UWB_HOST_REASON_UWB_RESET_CC_NOT_RCVD, "UWB Reset Cmd CC Not Rcvd"},
|
||||
{ UWB_HOST_REASON_UWB_ACTIVATE_CC_NOT_RCVD, "UWB Activate Cmd CC Not Rcvd"},
|
||||
{ UWB_HOST_REASON_TME_ACTIVATE_CC_NOT_RCVD, "TME DeActivate Cmd CC Not Rcvd"},
|
||||
{ UWB_HOST_REASON_POWER_ON_REGS_STUCK, "SoC Power ON Sequence stuck"},
|
||||
{ UWB_HOST_REASON_POWER_IOCTL_STUCK, "Power driver IOCTL stuck"},
|
||||
{ UWB_HOST_REASON_RX_THREAD_START_STUCK, "RX thread start stuck"},
|
||||
{ UWB_HOST_REASON_OTP_INFO_GET_CMD_STUCK, "Get OTP info. cmd stuck"},
|
||||
{ UWB_HOST_REASON_FILE_SYSTEM_CALL_STUCK, "FILE system call stuck"},
|
||||
{ UWB_HOST_REASON_PROPERTY_GET_STUCK, "Property get call stuck"},
|
||||
{ UWB_HOST_REASON_PROPERTY_SET_STUCK, "Property set call stuck"},
|
||||
{ UWB_HOST_REASON_PERI_RAM_PATCH_READ_STUCK, "Peri RAM patch open/read stuck"},
|
||||
{ UWB_HOST_REASON_UWB_RAM_PATCH_READ_STUCK, "UWB RAM patch open/read stuck"},
|
||||
{ UWB_HOST_REASON_PERI_NVM_PATCH_READ_STUCK, "Peri NVM file open/read stuck"},
|
||||
{ UWB_HOST_REASON_UWB_NVM_PATCH_READ_STUCK, "UWB NVM file open/read stuck"},
|
||||
{ UWB_HOST_REASON_PERI_PATCH_CONFIG_CMD_STUCK, "Peri Patch config cmd stuck"},
|
||||
{ UWB_HOST_REASON_PERI_PATCH_CONFIG_FAILED, "Peri Patch config cmd failed"},
|
||||
{ UWB_HOST_REASON_UWB_PATCH_CONFIG_CMD_STUCK, "Uwb Patch config cmd stuck"},
|
||||
{ UWB_HOST_REASON_UWB_PATCH_CONFIG_CMD_FAILED, "Uwb Patch config cmd stuck"},
|
||||
{ UWB_HOST_REASON_SOC_NAME_UNKOWN, "SoC name unkown"},
|
||||
{ UWB_HOST_REASON_PERI_TLV_DOWNLOAD_FAILED, "Peri TLV/NVM download failed"},
|
||||
{ UWB_HOST_REASON_PERI_GETBLDINFO_CMD_FAILED, "Peri FW build info. cmd failed"},
|
||||
{ UWB_HOST_REASON_UWB_GETBLDINFO_CMD_FAILED, "UWB build info. cmd failed"},
|
||||
{ UWB_HOST_REASON_PERI_RESET_CMD_FAILED, "HCI Peri RESET cmd failed"},
|
||||
{ UWB_HOST_REASON_UWB_CORE_RESET_CMD_FAILED, "UWB Core RESET cmd failed"},
|
||||
{ UWB_HOST_REASON_MEMORY_ALLOCATION_FAILED, "Memory allocation failed"},
|
||||
{ UWB_HOST_REASON_READ_THREAD_START_FAILED, "Read thread start failed"},
|
||||
{ UWB_HOST_REASON_HW_FLOW_ON_FAILED, "HW Flow ON failed"},
|
||||
{ UWB_HOST_REASON_PERI_ACTIVATE_CMD_STUCK, "Peri actvate cmd stuck"},
|
||||
{ UWB_HOST_REASON_PERI_ACTIVATE_NTF_STUCK, "Peri activate ntf stuck"},
|
||||
{ UWB_HOST_REASON_PERI_ARBITRATION_CMD_STUCK, "Peri arbitration cmd stuck"},
|
||||
{ UWB_HOST_REASON_PERI_ARBITRATION_NTF_STUCK, "Peri arbitration ntf stuck"},
|
||||
{ UWB_HOST_REASON_INITIALIZATION_FAILED, "Initialization Failed"},
|
||||
{ UWB_HOST_REASON_Q2SPI_INIT_STUCK, "Q2SPI Init stuck"},
|
||||
{ UWB_HOST_REASON_Q2SPI_INIT_FAILED, "Q2SPI Init Failed"},
|
||||
{ UWB_HOST_REASON_UWB_TLV_DOWNLOAD_FAILED, "Uwb TLV/NVM download failed"},
|
||||
{ Q2SPI_REASON_DEFAULT, "Q2SPI reason Default"},
|
||||
};
|
||||
|
||||
struct log_index {
|
||||
int init;
|
||||
int crash;
|
||||
};
|
||||
|
||||
struct vreg_data {
|
||||
struct regulator *reg; /* voltage regulator handle */
|
||||
const char *name; /* regulator name */
|
||||
u32 min_vol; /* min voltage level */
|
||||
u32 max_vol; /* max voltage level */
|
||||
u32 load_curr; /* current */
|
||||
bool is_enabled; /* is this regulator enabled? */
|
||||
bool is_retention_supp; /* does this regulator support retention mode */
|
||||
struct log_index indx; /* Index for reg. w.r.t init & crash */
|
||||
};
|
||||
|
||||
struct pwr_data {
|
||||
char compatible[32];
|
||||
struct vreg_data *bt_vregs;
|
||||
int bt_num_vregs;
|
||||
struct vreg_data *uwb_vregs;
|
||||
int uwb_num_vregs;
|
||||
struct vreg_data *platform_vregs;
|
||||
int platform_num_vregs;
|
||||
};
|
||||
|
||||
struct bt_power_clk_data {
|
||||
struct clk *clk; /* clock regulator handle */
|
||||
const char *name; /* clock name */
|
||||
bool is_enabled; /* is this clock enabled? */
|
||||
};
|
||||
|
||||
struct btpower_state_machine {
|
||||
struct mutex state_machine_lock;
|
||||
enum power_states power_state;
|
||||
enum retention_states retention_mode;
|
||||
enum grant_states grant_state;
|
||||
enum grant_states grant_pending;
|
||||
};
|
||||
|
||||
#define BTPWR_MAX_REQ BT_MAX_PWR_STATE
|
||||
|
||||
/*
|
||||
* Platform data for the bluetooth power driver.
|
||||
*/
|
||||
struct platform_pwr_data {
|
||||
struct platform_device *pdev;
|
||||
int bt_gpio_sys_rst; /* Bluetooth reset gpio */
|
||||
int wl_gpio_sys_rst; /* Wlan reset gpio */
|
||||
int bt_gpio_sw_ctrl; /* Bluetooth sw_ctrl gpio */
|
||||
int bt_gpio_debug; /* Bluetooth debug gpio */
|
||||
unsigned int wlan_sw_ctrl_gpio; /* Wlan switch control gpio*/
|
||||
#ifdef CONFIG_MSM_BT_OOBS
|
||||
int bt_gpio_dev_wake; /* Bluetooth bt_wake */
|
||||
int bt_gpio_host_wake; /* Bluetooth bt_host_wake */
|
||||
int irq; /* Bluetooth host_wake IRQ */
|
||||
#endif
|
||||
int sw_cntrl_gpio;
|
||||
int xo_gpio_clk; /* XO clock gpio*/
|
||||
struct device *slim_dev;
|
||||
struct vreg_data *bt_vregs;
|
||||
struct vreg_data *uwb_vregs;
|
||||
struct vreg_data *platform_vregs;
|
||||
struct bt_power_clk_data *bt_chip_clk; /* bluetooth reference clock */
|
||||
int (*power_setup)(int core, int id); /* Bluetooth power setup function */
|
||||
char compatible[32]; /*Bluetooth SoC name */
|
||||
int bt_num_vregs;
|
||||
int uwb_num_vregs;
|
||||
int platform_num_vregs;
|
||||
struct mbox_client mbox_client_data;
|
||||
struct mbox_chan *mbox_chan;
|
||||
const char *vreg_ipa;
|
||||
bool is_ganges_dt;
|
||||
int pdc_init_table_len;
|
||||
const char **pdc_init_table;
|
||||
int bt_device_type;
|
||||
bool sec_peri_feature_disable;
|
||||
int bt_sec_hw_disable;
|
||||
#ifdef CONFIG_MSM_BT_OOBS
|
||||
struct file *reffilp_obs;
|
||||
struct task_struct *reftask_obs;
|
||||
#endif
|
||||
struct task_struct *reftask;
|
||||
struct task_struct *reftask_bt;
|
||||
struct task_struct *reftask_uwb;
|
||||
struct btpower_state_machine btpower_state;
|
||||
enum ssr_states sub_state;
|
||||
enum ssr_states wrkq_signal_state;
|
||||
struct workqueue_struct *workq;
|
||||
struct device_node *bt_of_node;
|
||||
struct device_node *uwb_of_node;
|
||||
struct work_struct bt_wq;
|
||||
struct work_struct uwb_wq;
|
||||
wait_queue_head_t rsp_wait_q[BTPWR_MAX_REQ];
|
||||
int wait_status[BTPWR_MAX_REQ];
|
||||
struct work_struct wq_pwr_voting;
|
||||
struct sk_buff_head rxq;
|
||||
struct mutex pwr_mtx;
|
||||
};
|
||||
|
||||
int btpower_register_slimdev(struct device *dev);
|
||||
int btpower_get_chipset_version(void);
|
||||
int btpower_aop_mbox_init(struct platform_pwr_data *pdata);
|
||||
int bt_aop_pdc_reconfig(struct platform_pwr_data *pdata);
|
||||
|
||||
#define WLAN_SW_CTRL_GPIO "qcom,wlan-sw-ctrl-gpio"
|
||||
#define BT_CMD_SLIM_TEST 0xbfac
|
||||
#define BT_CMD_PWR_CTRL 0xbfad
|
||||
#define BT_CMD_CHIPSET_VERS 0xbfae
|
||||
#define BT_CMD_GET_CHIPSET_ID 0xbfaf
|
||||
#define BT_CMD_CHECK_SW_CTRL 0xbfb0
|
||||
#define BT_CMD_GETVAL_POWER_SRCS 0xbfb1
|
||||
#define BT_CMD_SET_IPA_TCS_INFO 0xbfc0
|
||||
#define BT_CMD_KERNEL_PANIC 0xbfc1
|
||||
#define UWB_CMD_KERNEL_PANIC 0xbfc2
|
||||
#define UWB_CMD_PWR_CTRL 0xbfe1
|
||||
#define BT_CMD_REGISTRATION 0xbfe2
|
||||
#define UWB_CMD_REGISTRATION 0xbfe3
|
||||
#define BT_CMD_ACCESS_CTRL 0xbfe4
|
||||
#define UWB_CMD_ACCESS_CTRL 0xbfe5
|
||||
|
||||
#ifdef CONFIG_MSM_BT_OOBS
|
||||
#define BT_CMD_OBS_VOTE_CLOCK 0xbfd1
|
||||
|
||||
|
||||
/**
|
||||
* enum btpower_obs_param: OOBS low power param
|
||||
* @BTPOWER_OBS_CLK_OFF: Transport bus is no longer acquired
|
||||
* @BTPOWER_OBS_CLK_ON: Acquire transport bus for either transmitting or receiving
|
||||
* @BTPOWER_OBS_DEV_OFF: Bluetooth is released because of no more transmission
|
||||
* @BTPOWER_OBS_DEV_ON: Wake up the Bluetooth controller for transmission
|
||||
*/
|
||||
enum btpower_obs_param {
|
||||
BTPOWER_OBS_CLK_OFF = 0,
|
||||
BTPOWER_OBS_CLK_ON,
|
||||
BTPOWER_OBS_DEV_OFF,
|
||||
BTPOWER_OBS_DEV_ON,
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* __LINUX_BLUETOOTH_POWER_H */
|
13
qcom/opensource/bt-kernel/pwr/Kconfig
Normal file
13
qcom/opensource/bt-kernel/pwr/Kconfig
Normal file
@ -0,0 +1,13 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config MSM_BT_POWER
|
||||
tristate "MSM Bluetooth Power Control"
|
||||
depends on ARCH_QCOM
|
||||
help
|
||||
MSM Bluetooth Power control driver.
|
||||
This provides a parameter to switch on/off power from PMIC
|
||||
to Bluetooth device. This will control LDOs/Clock/GPIOs to
|
||||
control Bluetooth Chipset based on power on/off sequence.
|
||||
|
||||
Say Y here to compile support for Bluetooth Power driver
|
||||
into the kernel or say M to compile as a module.
|
3
qcom/opensource/bt-kernel/pwr/Makefile
Normal file
3
qcom/opensource/bt-kernel/pwr/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
ccflags-y += -I$(BT_ROOT)/include
|
||||
|
||||
obj-$(CONFIG_MSM_BT_POWER) += btpower.o
|
2779
qcom/opensource/bt-kernel/pwr/btpower.c
Normal file
2779
qcom/opensource/bt-kernel/pwr/btpower.c
Normal file
File diff suppressed because it is too large
Load Diff
14
qcom/opensource/bt-kernel/rtc6226/Kconfig
Normal file
14
qcom/opensource/bt-kernel/rtc6226/Kconfig
Normal file
@ -0,0 +1,14 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config I2C_RTC6226_QCA
|
||||
tristate "Richwave RTC6226 FM Radio Receiver support with I2C for QCA"
|
||||
depends on I2C && VIDEO_V4L2
|
||||
help
|
||||
This is a driver for I2C devices with the Richwave RTC6226
|
||||
chip.
|
||||
|
||||
Say Y here if you want to connect this type of radio to your
|
||||
computer's I2C port.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called radio-i2c-RTC6226_QCA.
|
3
qcom/opensource/bt-kernel/rtc6226/Makefile
Normal file
3
qcom/opensource/bt-kernel/rtc6226/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
ccflags-y += -I$(BT_ROOT)/include
|
||||
radio-i2c-rtc6226-qca-objs := radio-rtc6226-i2c.o radio-rtc6226-common.o
|
||||
obj-$(CONFIG_I2C_RTC6226_QCA) += radio-i2c-rtc6226-qca.o
|
2375
qcom/opensource/bt-kernel/rtc6226/radio-rtc6226-common.c
Normal file
2375
qcom/opensource/bt-kernel/rtc6226/radio-rtc6226-common.c
Normal file
File diff suppressed because it is too large
Load Diff
1016
qcom/opensource/bt-kernel/rtc6226/radio-rtc6226-i2c.c
Normal file
1016
qcom/opensource/bt-kernel/rtc6226/radio-rtc6226-i2c.c
Normal file
File diff suppressed because it is too large
Load Diff
700
qcom/opensource/bt-kernel/rtc6226/radio-rtc6226.h
Normal file
700
qcom/opensource/bt-kernel/rtc6226/radio-rtc6226.h
Normal file
@ -0,0 +1,700 @@
|
||||
/* drivers/media/radio/rtc6226/radio-rtc6226.h
|
||||
*
|
||||
* Driver for Richwave RTC6226 FM Tuner
|
||||
*
|
||||
* Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net>
|
||||
* Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com>
|
||||
* Copyright (c) 2018 LG Electronics, Inc.
|
||||
* Copyright (c) 2018 Richwave Technology Co.Ltd
|
||||
* Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* driver definitions */
|
||||
/* #define _RDSDEBUG */
|
||||
#define DRIVER_NAME "rtc6226-fmtuner"
|
||||
|
||||
/* kernel includes */
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/wait.h>
|
||||
#include <media/v4l2-common.h>
|
||||
#include <media/v4l2-ioctl.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-event.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/v4l2-dev.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#define RW_Kernel_ENG
|
||||
|
||||
#define DEBUG
|
||||
#undef FMDBG
|
||||
#define FMDBG(fmt, args...) pr_debug("rtc6226: " fmt, ##args)
|
||||
|
||||
#undef FMDERR
|
||||
#define FMDERR(fmt, args...) pr_err("rtc6226: " fmt, ##args)
|
||||
|
||||
/* driver definitions */
|
||||
#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 1)
|
||||
#define DRIVER_CARD "Richwave rtc6226 FM Tuner"
|
||||
#define DRIVER_DESC "I2C radio driver for rtc6226 FM Tuner"
|
||||
#define DRIVER_VERSION "0.1.0"
|
||||
|
||||
/**************************************************************************
|
||||
* Register Definitions
|
||||
**************************************************************************/
|
||||
#define RADIO_REGISTER_SIZE 2 /* 16 register bit width */
|
||||
#define RADIO_REGISTER_NUM 32 /* DEVICEID */
|
||||
#define RDS_REGISTER_NUM 6 /* STATUSRSSI */
|
||||
|
||||
#define DEVICEID 0 /* Device ID Code */
|
||||
#define DEVICE_ID 0xffff /* [15:00] Device ID */
|
||||
#define DEVICEID_PN 0xf000 /* [15:12] Part Number */
|
||||
#define DEVICEID_MFGID 0x0fff /* [11:00] Manufacturer ID */
|
||||
|
||||
#define CHIPID 1 /* Chip ID Code */
|
||||
#define CHIPID_REVISION_NO 0xfc00 /* [15:10] Chip Reversion */
|
||||
|
||||
#define MPXCFG 2 /* Power Configuration */
|
||||
#define MPXCFG_CSR0_DIS_SMUTE 0x8000 /* [15:15] Disable Softmute */
|
||||
#define MPXCFG_CSR0_DIS_MUTE 0x4000 /* [14:14] Disable Mute */
|
||||
#define MPXCFG_CSR0_MONO 0x2000 /* [13:13] Mono or Auto Detect */
|
||||
#define MPXCFG_CSR0_DEEM 0x1000 /* [12:12] DE-emphasis */
|
||||
#define MPXCFG_CSR0_VOLUME_EXT 0x0400 /* [10:10] Volume Extend */
|
||||
#define MPXCFG_CSR0_BLNDADJUST 0x0300 /* [09:08] Blending Adjust */
|
||||
#define MPXCFG_CSR0_SMUTERATE 0x00c0 /* [07:06] Softmute Rate */
|
||||
#define MPXCFG_CSR0_SMUTEATT 0x0030 /* [05:04] Softmute Attenuation */
|
||||
#define MPXCFG_CSR0_VOLUME 0x000f /* [03:00] Volume */
|
||||
|
||||
#define CHANNEL 3 /* Tuning Channel Setting */
|
||||
#define CHANNEL_CSR0_TUNE 0x8000 /* [15:15] Tune */
|
||||
#define CHANNEL_CSR0_CH 0x7fff /* [14:00] Tuning Channel */
|
||||
|
||||
#define SYSCFG 4 /* System Configuration 1 */
|
||||
#define SYSCFG_CSR0_RDSIRQEN 0x8000 /* [15:15] RDS Interrupt Enable */
|
||||
#define SYSCFG_CSR0_STDIRQEN 0x4000 /* [14:14] STD Interrupt Enable */
|
||||
#define SYSCFG_CSR0_DIS_AGC 0x2000 /* [13:13] Disable AGC */
|
||||
#define SYSCFG_CSR0_RDS_EN 0x1000 /* [12:12] RDS Enable */
|
||||
#define SYSCFG_CSR0_RBDS_M 0x0300 /* [09:08] MMBS setting */
|
||||
|
||||
#define SEEKCFG1 5 /* Seek Configuration 1 */
|
||||
#define SEEKCFG1_CSR0_SEEK 0x8000 /* [15:15] Enable Seek Function */
|
||||
#define SEEKCFG1_CSR0_SEEKUP 0x4000 /* [14:14] Seek Direction */
|
||||
#define SEEKCFG1_CSR0_SKMODE 0x2000 /* [13:13] Seek Mode */
|
||||
#define SEEKCFG1_CSR0_RSSI_LOW_TH 0x0f00 /* [11:08] RSSI Seek Threshold */
|
||||
#define SEEKCFG1_CSR0_RSSI_MONO_TH 0x000f /* [03:00] RSSI Seek Threshold */
|
||||
|
||||
#define POWERCFG 6 /* Power Configuration */
|
||||
#define POWERCFG_CSR0_ENABLE 0x8000 /* [15:15] Power-up Enable */
|
||||
#define POWERCFG_CSR0_DISABLE 0x4000 /* [14:14] Power-up Disable */
|
||||
#define POWERCFG_CSR0_BLNDOFS 0x0f00 /* [11:08] Blending Offset Value */
|
||||
|
||||
#define PADCFG 7 /* PAD Configuration */
|
||||
#define PADCFG_CSR0_GPIO 0x0004 /* [03:02] General purpose I/O */
|
||||
|
||||
#define BANKCFG 8 /* Bank Serlection */
|
||||
|
||||
#define SEEKCFG2 9 /* Seek Configuration 2 */
|
||||
|
||||
#define STATUS 10 /* Status and Work channel */
|
||||
#define STATUS_RDS_RDY 0x8000 /* [15:15] RDS Ready */
|
||||
#define STATUS_STD 0x4000 /* [14:14] Seek/Tune Done */
|
||||
#define STATUS_SF 0x2000 /* [13:13] Seek Fail */
|
||||
#define STATUS_RDS_SYNC 0x0800 /* [11:11] RDS synchronization */
|
||||
#define STATUS_SI 0x0400 /* [10:10] Stereo Indicator */
|
||||
|
||||
#define RSSI 11 /* RSSI and RDS error */
|
||||
#define RSSI_RDS_BA_ERRS 0xc000 /* [15:14] RDS Block A Errors */
|
||||
#define RSSI_RDS_BB_ERRS 0x3000 /* [15:14] RDS Block B Errors */
|
||||
#define RSSI_RDS_BC_ERRS 0x0c00 /* [13:12] RDS Block C Errors */
|
||||
#define RSSI_RDS_BD_ERRS 0x0300 /* [11:10] RDS Block D Errors */
|
||||
#define RSSI_RSSI 0x00ff /* [09:00] Read Channel */
|
||||
|
||||
#define BA_DATA 12 /* Block A data */
|
||||
#define RDSA_RDSA 0xffff /* [15:00] RDS Block A Data */
|
||||
|
||||
#define BB_DATA 13 /* Block B data */
|
||||
#define RDSB_RDSB 0xffff /* [15:00] RDS Block B Data */
|
||||
|
||||
#define BC_DATA 14 /* Block C data */
|
||||
#define RDSC_RDSC 0xffff /* [15:00] RDS Block C Data */
|
||||
|
||||
#define BD_DATA 15 /* Block D data */
|
||||
#define RDSD_RDSD 0xffff /* [15:00] RDS Block D Data */
|
||||
|
||||
#define AUDIOCFG 0x12
|
||||
#define AUDIOCFG_CSR0_VOL_AUTOFIX 0x0800 //[11:11] LSB Volume Bit Auto Fix(1)
|
||||
|
||||
#define RADIOCFG 0x13
|
||||
#define CHANNEL_CSR0_CHSPACE 0x1f00 /* [12:08] Channel Sapcing */
|
||||
|
||||
#define RADIOSEEKCFG1 0x14
|
||||
/* [14:00] FM Seek Top CH, Unit 10KHz */
|
||||
#define CHANNEL_CSR0_FREQ_TOP 0x7fff
|
||||
|
||||
#define RADIOSEEKCFG2 0x15
|
||||
/*[14:00] FM Seek Bottom CH, Unit 10KHz */
|
||||
#define CHANNEL_CSR0_FREQ_BOT 0x7fff
|
||||
|
||||
#define I2SCFG 0x1c
|
||||
/* [13:13] I2S DSP Mode(0:Normal, 1:Special) */
|
||||
#define I2S_DSP_SEL 0x2000
|
||||
/* [12:12] BCLK Polarity(0:Falling, 1:Rising) */
|
||||
#define I2S_BCLK_POL 0x1000
|
||||
/* [11:10] Word Bits Select(0:8b, 1:16b, 2:20b, 3:24b) */
|
||||
#define I2S_WD_SEL 0x0c00
|
||||
/* [09:08] Right CH Control(0:On, 1:Off, 1x:Auto) */
|
||||
#define I2S_RCH_SEL 0x0300
|
||||
/* [07:07] I2S Enable */
|
||||
#define I2S_EN 0x0080 /* [07:07] I2S Enable */
|
||||
#define I2S_MSEL 0x0040 /* [06:06] I2S Master */
|
||||
/* [05:04] I2S Output Mode(0:I2S, 1:LJ, 2:DSPA, 3:DSPB) */
|
||||
#define I2S_MODE 0x0030
|
||||
/* [03:02] I2S Sample Rate(0:32K, 1:44.1K, 2:48K) */
|
||||
#define I2S_FS_AUD_SEL 0x000c
|
||||
/* [05:04] I2S BCLK Ratio(0:M32, 1:M64, 2:M128, 3:M256) */
|
||||
#define I2S_BCLK_AUD_SEL 0x0030
|
||||
|
||||
#define CHANNEL1 0x1e
|
||||
#define STATUS_READCH 0x7fff /* [14:00] Read Channel */
|
||||
|
||||
#define TURN_ON 1
|
||||
#define TURN_OFF 0
|
||||
#define SRCH_UP 1
|
||||
#define SRCH_DOWN 0
|
||||
|
||||
#define WRAP_ENABLE 1
|
||||
#define WRAP_DISABLE 0
|
||||
#define DEFAULT_RSSI_TH 8
|
||||
/* Standard buffer size */
|
||||
#define STD_BUF_SIZE 256
|
||||
|
||||
/* to distinguish between seek, tune during STC int. */
|
||||
#define NO_SEEK_TUNE_PENDING 0
|
||||
#define TUNE_PENDING 1
|
||||
#define SEEK_PENDING 2
|
||||
#define SCAN_PENDING 3
|
||||
#define START_SCAN 1
|
||||
#define TUNE_TIMEOUT_MSEC 3000
|
||||
#define SEEK_TIMEOUT_MSEC 15000
|
||||
|
||||
#define RTC6226_MIN_SRCH_MODE 0x00
|
||||
#define RTC6226_MAX_SRCH_MODE 0x02
|
||||
|
||||
#define MIN_DWELL_TIME 0x00
|
||||
#define MAX_DWELL_TIME 0x0F
|
||||
|
||||
#define TUNE_STEP_SIZE 10
|
||||
#define NO_OF_RDS_BLKS 4
|
||||
|
||||
#define GET_MSB(x)((x >> 8) & 0xFF)
|
||||
#define GET_LSB(x)((x) & 0xFF)
|
||||
|
||||
#define OFFSET_OF_GRP_TYP 11
|
||||
#define RDS_INT_BIT 0x01
|
||||
#define FIFO_CNT_16 0x10
|
||||
#define UNCORRECTABLE_RDS_EN 0xFF01
|
||||
|
||||
/* Write starts with the upper byte of register 0x02 */
|
||||
#define WRITE_REG_NUM 3
|
||||
#define WRITE_INDEX(i) ((i + 0x02)%16)
|
||||
|
||||
/* Read starts with the upper byte of register 0x0a */
|
||||
#define READ_REG_NUM 2
|
||||
#define READ_INDEX(i) ((i + RADIO_REGISTER_NUM - 0x0a) % READ_REG_NUM)
|
||||
|
||||
#define MSB_OF_BLK_0 4
|
||||
#define LSB_OF_BLK_0 5
|
||||
#define MSB_OF_BLK_1 6
|
||||
#define LSB_OF_BLK_1 7
|
||||
#define MSB_OF_BLK_2 8
|
||||
#define LSB_OF_BLK_2 9
|
||||
#define MSB_OF_BLK_3 10
|
||||
#define LSB_OF_BLK_3 11
|
||||
#define MAX_RT_LEN 64
|
||||
#define END_OF_RT 0x0d
|
||||
#define MAX_PS_LEN 8
|
||||
#define OFFSET_OF_PS 5
|
||||
#define PS_VALIDATE_LIMIT 2
|
||||
#define RT_VALIDATE_LIMIT 2
|
||||
#define RDS_CMD_LEN 3
|
||||
#define RDS_RSP_LEN 13
|
||||
#define PS_EVT_DATA_LEN (MAX_PS_LEN + OFFSET_OF_PS)
|
||||
#define NO_OF_PS 1
|
||||
#define OFFSET_OF_RT 5
|
||||
#define OFFSET_OF_PTY 5
|
||||
#define MAX_LEN_2B_GRP_RT 32
|
||||
#define CNT_FOR_2A_GRP_RT 4
|
||||
#define CNT_FOR_2B_GRP_RT 2
|
||||
#define PS_MASK 0x3
|
||||
#define PTY_MASK 0x1F
|
||||
#define NO_OF_CHARS_IN_EACH_ADD 2
|
||||
|
||||
#define CORRECTED_NONE 0
|
||||
#define CORRECTED_ONE_TO_TWO 1
|
||||
#define CORRECTED_THREE_TO_FIVE 2
|
||||
#define ERRORS_CORRECTED(data, block) ((data>>block)&0x03)
|
||||
/*Block Errors are reported in .5% increments*/
|
||||
#define BLER_SCALE_MAX 200
|
||||
|
||||
/* freqs are divided by 10. */
|
||||
#define SCALE_AF_CODE_TO_FREQ_KHZ(x) (87500 + (x*100))
|
||||
|
||||
#define RDS_TYPE_0A (0 * 2 + 0)
|
||||
#define RDS_TYPE_0B (0 * 2 + 1)
|
||||
#define RDS_TYPE_2A (2 * 2 + 0)
|
||||
#define RDS_TYPE_2B (2 * 2 + 1)
|
||||
#define RDS_TYPE_3A (3 * 2 + 0)
|
||||
#define UNCORRECTABLE 3
|
||||
|
||||
#define APP_GRP_typ_MASK 0x1F
|
||||
/*ERT*/
|
||||
#define ERT_AID 0x6552
|
||||
#define MAX_ERT_SEGMENT 31
|
||||
#define MAX_ERT_LEN 256
|
||||
#define ERT_OFFSET 3
|
||||
#define ERT_FORMAT_DIR_BIT 1
|
||||
#define ERT_CNT_PER_BLK 2
|
||||
/*RT PLUS*/
|
||||
#define DUMMY_CLASS 0
|
||||
#define RT_PLUS_LEN_1_TAG 3
|
||||
#define RT_ERT_FLAG_BIT 13
|
||||
#define RT_PLUS_AID 0x4bd7
|
||||
#define RT_ERT_FLAG_OFFSET 1
|
||||
#define RT_PLUS_OFFSET 2
|
||||
/*TAG1*/
|
||||
#define TAG1_MSB_OFFSET 3
|
||||
#define TAG1_MSB_MASK 7
|
||||
#define TAG1_LSB_OFFSET 13
|
||||
#define TAG1_POS_MSB_MASK 0x3F
|
||||
#define TAG1_POS_MSB_OFFSET 1
|
||||
#define TAG1_POS_LSB_OFFSET 7
|
||||
#define TAG1_LEN_OFFSET 1
|
||||
#define TAG1_LEN_MASK 0x3F
|
||||
/*TAG2*/
|
||||
#define TAG2_MSB_OFFSET 5
|
||||
#define TAG2_MSB_MASK 9
|
||||
#define TAG2_LSB_OFFSET 11
|
||||
#define TAG2_POS_MSB_MASK 0x3F
|
||||
#define TAG2_POS_MSB_OFFSET 3
|
||||
#define TAG2_POS_LSB_OFFSET 5
|
||||
#define TAG2_LEN_MASK 0x1F
|
||||
|
||||
#define DEFAULT_AF_RSSI_LOW_TH 25
|
||||
#define NO_OF_AF_IN_GRP 2
|
||||
#define MAX_NO_OF_AF 25
|
||||
#define MAX_AF_LIST_SIZE (MAX_NO_OF_AF * 4) /* 4 bytes per freq */
|
||||
#define GET_AF_EVT_LEN(x) (7 + x*4)
|
||||
#define GET_AF_LIST_LEN(x) (x*4)
|
||||
#define MIN_AF_FREQ_CODE 1
|
||||
#define MAX_AF_FREQ_CODE 204
|
||||
#define MIN_RSSI 0
|
||||
#define MAX_RSSI 15
|
||||
|
||||
/* 25 AFs supported for a freq. 224 means 1 AF. 225 means 2 AFs and so on */
|
||||
#define NO_AF_CNT_CODE 224
|
||||
#define MIN_AF_CNT_CODE 225
|
||||
#define MAX_AF_CNT_CODE 249
|
||||
#define AF_WAIT_SEC 10
|
||||
#define MAX_AF_WAIT_SEC 255
|
||||
#define AF_PI_WAIT_TIME 50 /* 50*100msec = 5sec */
|
||||
|
||||
#define CH_SPACING_200 200
|
||||
#define CH_SPACING_100 100
|
||||
#define CH_SPACING_50 50
|
||||
#define TURNING_ON 1
|
||||
#define TURNING_OFF 0
|
||||
|
||||
#define RW_PRIBASE (V4L2_CID_USER_BASE | 0xf000)
|
||||
|
||||
/* freqs are divided by 10. */
|
||||
#define SCALE_AF_CODE_TO_FREQ_KHZ(x) (87500 + (x*100))
|
||||
|
||||
#define EXTRACT_BIT(data, bit_pos) ((data >> bit_pos) & 1)
|
||||
|
||||
#define V4L2_CID_PRIVATE_CSR0_ENABLE (RW_PRIBASE + (DEVICEID<<4) + 1)
|
||||
#define V4L2_CID_PRIVATE_CSR0_DISABLE (RW_PRIBASE + (DEVICEID<<4) + 2)
|
||||
#define V4L2_CID_PRIVATE_DEVICEID (RW_PRIBASE + (DEVICEID<<4) + 3)
|
||||
|
||||
#define V4L2_CID_PRIVATE_CSR0_DIS_SMUTE (RW_PRIBASE + (DEVICEID<<4) + 4)
|
||||
#define V4L2_CID_PRIVATE_CSR0_DIS_MUTE (RW_PRIBASE + (DEVICEID<<4) + 5)
|
||||
#define V4L2_CID_PRIVATE_CSR0_DEEM (RW_PRIBASE + (DEVICEID<<4) + 6)
|
||||
#define V4L2_CID_PRIVATE_CSR0_BLNDADJUST (RW_PRIBASE + (DEVICEID<<4) + 7)
|
||||
#define V4L2_CID_PRIVATE_CSR0_VOLUME (RW_PRIBASE + (DEVICEID<<4) + 8)
|
||||
|
||||
#define V4L2_CID_PRIVATE_CSR0_BAND (RW_PRIBASE + (DEVICEID<<4) + 9)
|
||||
#define V4L2_CID_PRIVATE_CSR0_CHSPACE (RW_PRIBASE + (DEVICEID<<4) + 10)
|
||||
|
||||
#define V4L2_CID_PRIVATE_CSR0_DIS_AGC (RW_PRIBASE + (DEVICEID<<4) + 11)
|
||||
#define V4L2_CID_PRIVATE_CSR0_RDS_EN (RW_PRIBASE + (DEVICEID<<4) + 12)
|
||||
|
||||
#define V4L2_CID_PRIVATE_SEEK_CANCEL (RW_PRIBASE + (DEVICEID<<4) + 13)
|
||||
|
||||
#define V4L2_CID_PRIVATE_CSR0_SEEKRSSITH (RW_PRIBASE + (DEVICEID<<4) + 14)
|
||||
#define V4L2_CID_PRIVATE_RSSI (RW_PRIBASE + (CHIPID<<4) + 1)
|
||||
|
||||
#define V4L2_CID_PRIVATE_RDS_RDY (RW_PRIBASE + (CHIPID<<4) + 2)
|
||||
#define V4L2_CID_PRIVATE_STD (RW_PRIBASE + (CHIPID<<4) + 3)
|
||||
#define V4L2_CID_PRIVATE_SF (RW_PRIBASE + (CHIPID<<4) + 4)
|
||||
#define V4L2_CID_PRIVATE_RDS_SYNC (RW_PRIBASE + (CHIPID<<4) + 5)
|
||||
#define V4L2_CID_PRIVATE_SI (RW_PRIBASE + (CHIPID<<4) + 6)
|
||||
|
||||
#define NO_WAIT 2
|
||||
#define RDS_WAITING 5
|
||||
#define SEEK_CANCEL 6
|
||||
#define TUNE_PARAM 16
|
||||
|
||||
/**************************************************************************
|
||||
* General Driver Definitions
|
||||
**************************************************************************/
|
||||
|
||||
enum rtc6226_buf_t {
|
||||
RTC6226_FM_BUF_SRCH_LIST,
|
||||
RTC6226_FM_BUF_EVENTS,
|
||||
RTC6226_FM_BUF_RT_RDS,
|
||||
RTC6226_FM_BUF_PS_RDS,
|
||||
RTC6226_FM_BUF_RAW_RDS,
|
||||
RTC6226_FM_BUF_AF_LIST,
|
||||
RTC6226_FM_BUF_RT_PLUS = 11,
|
||||
RTC6226_FM_BUF_ERT,
|
||||
RTC6226_FM_BUF_MAX
|
||||
};
|
||||
|
||||
enum rtc6226_evt_t {
|
||||
RTC6226_EVT_RADIO_READY,
|
||||
RTC6226_EVT_TUNE_SUCC,
|
||||
RTC6226_EVT_SEEK_COMPLETE,
|
||||
RTC6226_EVT_SCAN_NEXT,
|
||||
RTC6226_EVT_NEW_RAW_RDS,
|
||||
RTC6226_EVT_NEW_RT_RDS,
|
||||
RTC6226_EVT_NEW_PS_RDS,
|
||||
RTC6226_EVT_ERROR,
|
||||
RTC6226_EVT_BELOW_TH,
|
||||
RTC6226_EVT_ABOVE_TH,
|
||||
RTC6226_EVT_STEREO,
|
||||
RTC6226_EVT_MONO,
|
||||
RTC6226_EVT_RDS_AVAIL,
|
||||
RTC6226_EVT_RDS_NOT_AVAIL,
|
||||
RTC6226_EVT_NEW_SRCH_LIST,
|
||||
RTC6226_EVT_NEW_AF_LIST,
|
||||
RTC6226_EVT_TXRDSDAT,
|
||||
RTC6226_EVT_TXRDSDONE,
|
||||
RTC6226_EVT_RADIO_DISABLED,
|
||||
RTC6226_EVT_NEW_ODA,
|
||||
RTC6226_EVT_NEW_RT_PLUS,
|
||||
RTC6226_EVT_NEW_ERT
|
||||
};
|
||||
|
||||
struct rtc6226_recv_conf_req {
|
||||
__u16 emphasis;
|
||||
__u16 ch_spacing;
|
||||
/* limits stored as actual freq / TUNE_STEP_SIZE */
|
||||
__u16 band_low_limit;
|
||||
__u16 band_high_limit;
|
||||
};
|
||||
|
||||
struct rtc6226_rel_freq {
|
||||
__u8 rel_freq_msb;
|
||||
__u8 rel_freq_lsb;
|
||||
} __packed;
|
||||
|
||||
struct rtc6226_srch_list_compl {
|
||||
__u8 num_stations_found;
|
||||
struct rtc6226_rel_freq rel_freq[20];
|
||||
} __packed;
|
||||
|
||||
struct af_list_ev {
|
||||
__le32 tune_freq_khz;
|
||||
__le16 pi_code;
|
||||
__u8 af_size;
|
||||
__u8 af_list[MAX_AF_LIST_SIZE];
|
||||
} __packed;
|
||||
|
||||
struct rtc6226_af_info {
|
||||
/* no. of invalid AFs. */
|
||||
u8 inval_freq_cnt;
|
||||
/* no. of AFs in the list. */
|
||||
u8 cnt;
|
||||
/* actual size of the list */
|
||||
u8 size;
|
||||
/* index of currently tuned station in the AF list. */
|
||||
u8 index;
|
||||
/* PI of the frequency */
|
||||
u16 pi;
|
||||
/* freq to which AF list belongs to. */
|
||||
u32 orig_freq_khz;
|
||||
/* AF list */
|
||||
u32 af_list[MAX_NO_OF_AF];
|
||||
};
|
||||
|
||||
struct fm_power_vreg_data {
|
||||
/* voltage regulator handle */
|
||||
struct regulator *reg;
|
||||
/* regulator name */
|
||||
const char *name;
|
||||
/* voltage levels to be set */
|
||||
unsigned int low_vol_level;
|
||||
unsigned int high_vol_level;
|
||||
int vdd_load;
|
||||
/* is this regulator enabled? */
|
||||
bool is_enabled;
|
||||
};
|
||||
|
||||
/*
|
||||
* rtc6226_device - private data
|
||||
*/
|
||||
struct rtc6226_device {
|
||||
int int_gpio;
|
||||
int fm_sw_gpio;
|
||||
int ext_ldo_gpio;
|
||||
int reset_gpio;
|
||||
struct regulator *vdd_reg;
|
||||
struct v4l2_device v4l2_dev;
|
||||
struct video_device videodev;
|
||||
struct pinctrl *fm_pinctrl;
|
||||
struct pinctrl_state *gpio_state_active;
|
||||
struct pinctrl_state *gpio_state_suspend;
|
||||
struct v4l2_ctrl_handler ctrl_handler;
|
||||
struct fm_power_vreg_data *vddreg;
|
||||
struct fm_power_vreg_data *vioreg;
|
||||
int band;
|
||||
int space;
|
||||
atomic_t users;
|
||||
unsigned int mode;
|
||||
u8 seek_tune_status;
|
||||
u8 rssi_th;
|
||||
/* Richwave internal registers (0..15) */
|
||||
unsigned short registers[RADIO_REGISTER_NUM];
|
||||
|
||||
/* RDS receive buffer */
|
||||
wait_queue_head_t read_queue;
|
||||
int irq;
|
||||
int tuned_freq_khz;
|
||||
int dwell_time_sec;
|
||||
struct mutex lock; /* buffer locking */
|
||||
unsigned char *buffer; /* size is always multiple of three */
|
||||
bool is_search_cancelled;
|
||||
u8 g_search_mode;
|
||||
struct rtc6226_srch_list_compl srch_list;
|
||||
/* buffer locks*/
|
||||
spinlock_t buf_lock[RTC6226_FM_BUF_MAX];
|
||||
struct rtc6226_recv_conf_req recv_conf;
|
||||
struct workqueue_struct *wqueue;
|
||||
struct workqueue_struct *wqueue_scan;
|
||||
struct workqueue_struct *wqueue_rds;
|
||||
struct work_struct rds_worker;
|
||||
struct rtc6226_af_info af_info1;
|
||||
struct rtc6226_af_info af_info2;
|
||||
|
||||
struct delayed_work work;
|
||||
struct delayed_work work_scan;
|
||||
|
||||
wait_queue_head_t event_queue;
|
||||
u8 write_buf[WRITE_REG_NUM];
|
||||
/* TO read events, data*/
|
||||
u8 read_buf[READ_REG_NUM];
|
||||
|
||||
u16 pi; /* PI of tuned channel */
|
||||
u8 pty; /* programe type of the tuned channel */
|
||||
|
||||
u16 block[NO_OF_RDS_BLKS];
|
||||
u8 rt_display[MAX_RT_LEN]; /* RT that will be displayed */
|
||||
u8 rt_tmp0[MAX_RT_LEN]; /* high probability RT */
|
||||
u8 rt_tmp1[MAX_RT_LEN]; /* low probability RT */
|
||||
u8 rt_cnt[MAX_RT_LEN]; /* high probability RT's hit count */
|
||||
u8 rt_flag; /* A/B flag of RT */
|
||||
bool valid_rt_flg; /* validity of A/B flag */
|
||||
u8 ps_display[MAX_PS_LEN]; /* PS that will be displayed */
|
||||
u8 ps_tmp0[MAX_PS_LEN]; /* high probability PS */
|
||||
u8 ps_tmp1[MAX_PS_LEN]; /* low probability PS */
|
||||
u8 ps_cnt[MAX_PS_LEN]; /* high probability PS's hit count */
|
||||
u8 bler[NO_OF_RDS_BLKS];
|
||||
u8 rt_plus_carrier;
|
||||
u8 ert_carrier;
|
||||
u8 ert_buf[MAX_ERT_LEN];
|
||||
u8 ert_len;
|
||||
u8 c_byt_pair_index;
|
||||
u8 utf_8_flag;
|
||||
u8 rt_ert_flag;
|
||||
u8 formatting_dir;
|
||||
unsigned int buf_size;
|
||||
unsigned int rd_index;
|
||||
unsigned int wr_index;
|
||||
|
||||
struct kfifo data_buf[RTC6226_FM_BUF_MAX];
|
||||
|
||||
struct completion completion;
|
||||
bool stci_enabled; /* Seek/Tune Complete Interrupt */
|
||||
|
||||
struct i2c_client *client;
|
||||
unsigned int tuner_state;
|
||||
int lna_en;
|
||||
int lna_gain;
|
||||
};
|
||||
|
||||
enum radio_state_t {
|
||||
FM_OFF,
|
||||
FM_RECV,
|
||||
FM_RESET,
|
||||
FM_CALIB,
|
||||
FM_TURNING_OFF,
|
||||
FM_RECV_TURNING_ON,
|
||||
FM_MAX_NO_STATES,
|
||||
};
|
||||
|
||||
enum search_t {
|
||||
SEEK,
|
||||
SCAN,
|
||||
SCAN_FOR_STRONG,
|
||||
};
|
||||
|
||||
/**************************************************************************
|
||||
* Frequency Multiplicator
|
||||
**************************************************************************/
|
||||
#define FREQ_MUL 1000
|
||||
#define CONFIG_RDS
|
||||
|
||||
enum v4l2_cid_private_rtc6226_t {
|
||||
V4L2_CID_PRIVATE_RTC6226_SRCHMODE = (V4L2_CID_PRIVATE_BASE + 1),
|
||||
V4L2_CID_PRIVATE_RTC6226_SCANDWELL,
|
||||
V4L2_CID_PRIVATE_RTC6226_SRCHON,
|
||||
V4L2_CID_PRIVATE_RTC6226_STATE,
|
||||
V4L2_CID_PRIVATE_RTC6226_TRANSMIT_MODE,
|
||||
V4L2_CID_PRIVATE_RTC6226_RDSGROUP_MASK,
|
||||
V4L2_CID_PRIVATE_RTC6226_REGION,
|
||||
V4L2_CID_PRIVATE_RTC6226_SIGNAL_TH,
|
||||
V4L2_CID_PRIVATE_RTC6226_SRCH_PTY,
|
||||
V4L2_CID_PRIVATE_RTC6226_SRCH_PI,
|
||||
V4L2_CID_PRIVATE_RTC6226_SRCH_CNT,
|
||||
V4L2_CID_PRIVATE_RTC6226_EMPHASIS, /* 800000c */
|
||||
V4L2_CID_PRIVATE_RTC6226_RDS_STD,
|
||||
V4L2_CID_PRIVATE_RTC6226_SPACING,
|
||||
V4L2_CID_PRIVATE_RTC6226_RDSON,
|
||||
V4L2_CID_PRIVATE_RTC6226_RDSGROUP_PROC,
|
||||
V4L2_CID_PRIVATE_RTC6226_LP_MODE,
|
||||
V4L2_CID_PRIVATE_RTC6226_ANTENNA,
|
||||
V4L2_CID_PRIVATE_RTC6226_RDSD_BUF,
|
||||
V4L2_CID_PRIVATE_RTC6226_PSALL,
|
||||
/*v4l2 Tx controls*/
|
||||
V4L2_CID_PRIVATE_RTC6226_TX_SETPSREPEATCOUNT,
|
||||
V4L2_CID_PRIVATE_RTC6226_STOP_RDS_TX_PS_NAME,
|
||||
V4L2_CID_PRIVATE_RTC6226_STOP_RDS_TX_RT,
|
||||
V4L2_CID_PRIVATE_RTC6226_IOVERC,
|
||||
V4L2_CID_PRIVATE_RTC6226_INTDET,
|
||||
V4L2_CID_PRIVATE_RTC6226_MPX_DCC,
|
||||
V4L2_CID_PRIVATE_RTC6226_AF_JUMP,
|
||||
V4L2_CID_PRIVATE_RTC6226_RSSI_DELTA,
|
||||
V4L2_CID_PRIVATE_RTC6226_HLSI,
|
||||
|
||||
/*
|
||||
* Here we have IOCTl's that are specific to IRIS
|
||||
* (V4L2_CID_PRIVATE_BASE + 0x1E to V4L2_CID_PRIVATE_BASE + 0x28)
|
||||
*/
|
||||
V4L2_CID_PRIVATE_RTC6226_SOFT_MUTE,/* 0x800001E*/
|
||||
V4L2_CID_PRIVATE_RTC6226_RIVA_ACCS_ADDR,
|
||||
V4L2_CID_PRIVATE_RTC6226_RIVA_ACCS_LEN,
|
||||
V4L2_CID_PRIVATE_RTC6226_RIVA_PEEK,
|
||||
V4L2_CID_PRIVATE_RTC6226_RIVA_POKE,
|
||||
V4L2_CID_PRIVATE_RTC6226_SSBI_ACCS_ADDR,
|
||||
V4L2_CID_PRIVATE_RTC6226_SSBI_PEEK,
|
||||
V4L2_CID_PRIVATE_RTC6226_SSBI_POKE,
|
||||
V4L2_CID_PRIVATE_RTC6226_TX_TONE,
|
||||
V4L2_CID_PRIVATE_RTC6226_RDS_GRP_COUNTERS,
|
||||
V4L2_CID_PRIVATE_RTC6226_SET_NOTCH_FILTER, /* 0x8000028 */
|
||||
|
||||
V4L2_CID_PRIVATE_RTC6226_SET_AUDIO_PATH, /* 0x8000029 */
|
||||
V4L2_CID_PRIVATE_RTC6226_DO_CALIBRATION, /* 0x800002A : IRIS */
|
||||
V4L2_CID_PRIVATE_RTC6226_SRCH_ALGORITHM, /* 0x800002B */
|
||||
V4L2_CID_PRIVATE_RTC6226_GET_SINR, /* 0x800002C : IRIS */
|
||||
V4L2_CID_PRIVATE_RTC6226_INTF_LOW_THRESHOLD, /* 0x800002D */
|
||||
V4L2_CID_PRIVATE_RTC6226_INTF_HIGH_THRESHOLD, /* 0x800002E */
|
||||
/* 0x800002F : IRIS, For Richwave Spike TH */
|
||||
V4L2_CID_PRIVATE_RTC6226_SINR_THRESHOLD,
|
||||
/* V4L2_CID_PRIVATE_RTC6226_QLT_THRESHOLD,
|
||||
*/ /* 0x800002F : IRIS, For Richwave Spike TH
|
||||
*/
|
||||
V4L2_CID_PRIVATE_RTC6226_SINR_SAMPLES, /* 0x8000030 : IRIS */
|
||||
V4L2_CID_PRIVATE_RTC6226_SPUR_FREQ,
|
||||
V4L2_CID_PRIVATE_RTC6226_SPUR_FREQ_RMSSI, /* For Richwave DC TH */
|
||||
/* V4L2_CID_PRIVATE_RTC6226_OFS_THRESHOLD, */ /* For Richwave DC TH */
|
||||
V4L2_CID_PRIVATE_RTC6226_SPUR_SELECTION,
|
||||
V4L2_CID_PRIVATE_RTC6226_UPDATE_SPUR_TABLE,
|
||||
V4L2_CID_PRIVATE_RTC6226_VALID_CHANNEL,
|
||||
V4L2_CID_PRIVATE_RTC6226_AF_RMSSI_TH,
|
||||
V4L2_CID_PRIVATE_RTC6226_AF_RMSSI_SAMPLES,
|
||||
V4L2_CID_PRIVATE_RTC6226_GOOD_CH_RMSSI_TH,
|
||||
V4L2_CID_PRIVATE_RTC6226_SRCHALGOTYPE,
|
||||
V4L2_CID_PRIVATE_RTC6226_CF0TH12,
|
||||
V4L2_CID_PRIVATE_RTC6226_SINRFIRSTSTAGE,
|
||||
V4L2_CID_PRIVATE_RTC6226_RMSSIFIRSTSTAGE,
|
||||
V4L2_CID_PRIVATE_RTC6226_RXREPEATCOUNT,
|
||||
V4L2_CID_PRIVATE_RTC6226_RSSI_TH, /* 0x800003E */
|
||||
V4L2_CID_PRIVATE_RTC6226_AF_JUMP_RSSI_TH /* 0x800003F */
|
||||
};
|
||||
|
||||
enum FMBAND {FMBAND_87_108_MHZ, FMBAND_76_108_MHZ, FMBAND_76_91_MHZ,
|
||||
FMBAND_64_76_MHZ};
|
||||
enum FMSPACE {FMSPACE_200_KHZ, FMSPACE_100_KHZ, FMSPACE_50_KHZ};
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
* Common Functions
|
||||
**************************************************************************/
|
||||
extern struct i2c_driver rtc6226_i2c_driver;
|
||||
extern struct video_device rtc6226_viddev_template;
|
||||
extern const struct v4l2_ioctl_ops rtc6226_ioctl_ops;
|
||||
extern const struct v4l2_ctrl_ops rtc6226_ctrl_ops;
|
||||
|
||||
extern struct tasklet_struct my_tasklet;
|
||||
extern int rtc6226_wq_flag;
|
||||
extern wait_queue_head_t rtc6226_wq;
|
||||
extern int rtc6226_get_all_registers(struct rtc6226_device *radio);
|
||||
extern int rtc6226_get_register(struct rtc6226_device *radio, int regnr);
|
||||
extern int rtc6226_set_register(struct rtc6226_device *radio, int regnr);
|
||||
extern int rtc6226_set_serial_registers(struct rtc6226_device *radio,
|
||||
u16 *data, int bytes);
|
||||
int rtc6226_i2c_init(void);
|
||||
int rtc6226_reset_rds_data(struct rtc6226_device *radio);
|
||||
int rtc6226_set_freq(struct rtc6226_device *radio, unsigned int freq);
|
||||
int rtc6226_start(struct rtc6226_device *radio);
|
||||
int rtc6226_stop(struct rtc6226_device *radio);
|
||||
int rtc6226_fops_open(struct file *file);
|
||||
int rtc6226_power_up(struct rtc6226_device *radio);
|
||||
int rtc6226_power_down(struct rtc6226_device *radio);
|
||||
int rtc6226_fops_release(struct file *file);
|
||||
int rtc6226_vidioc_querycap(struct file *file, void *priv,
|
||||
struct v4l2_capability *capability);
|
||||
int rtc6226_enable_irq(struct rtc6226_device *radio);
|
||||
void rtc6226_disable_irq(struct rtc6226_device *radio);
|
||||
void rtc6226_scan(struct work_struct *work);
|
||||
void rtc6226_search(struct rtc6226_device *radio, bool on);
|
||||
int rtc6226_cancel_seek(struct rtc6226_device *radio);
|
||||
void rtc6226_rds_handler(struct work_struct *worker);
|
||||
void rtc6226_q_event(struct rtc6226_device *radio, enum rtc6226_evt_t event);
|
||||
int rtc6226_reset_rds_data(struct rtc6226_device *radio);
|
||||
int rtc6226_rds_on(struct rtc6226_device *radio);
|
25
qcom/opensource/bt-kernel/slimbus/Kconfig
Normal file
25
qcom/opensource/bt-kernel/slimbus/Kconfig
Normal file
@ -0,0 +1,25 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config BTFM_SLIM
|
||||
tristate "MSM Bluetooth/FM Slimbus Device"
|
||||
depends on MSM_BT_POWER
|
||||
help
|
||||
This enables BT/FM slimbus driver to get multiple audio channel.
|
||||
This will make use of slimbus platform driver and slimbus
|
||||
codec driver to communicate with slimbus machine driver and LPSS which
|
||||
is Slimbus master.Slimbus slave initialization and configuration
|
||||
will be done through this driver.
|
||||
|
||||
Say Y here to compile support for Bluetooth slimbus driver
|
||||
into the kernel or say M to compile as a module.
|
||||
|
||||
config SLIM_BTFM_CODEC
|
||||
tristate "MSM Bluetooth/FM Slimbus Device using BTFM codec driver"
|
||||
depends on MSM_BT_POWER
|
||||
depends on BTFM_CODEC
|
||||
help
|
||||
This enables BT/FM slimbus driver to use btfm codec driver as
|
||||
interface to interacts with codec driver.
|
||||
|
||||
Say Y here to compile support for Bluetooth slimbus driver
|
||||
into the kernel or say M to compile as a module.
|
8
qcom/opensource/bt-kernel/slimbus/Makefile
Normal file
8
qcom/opensource/bt-kernel/slimbus/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
ccflags-y += -I$(BT_ROOT)/include
|
||||
ccflags-y += -I$(BT_ROOT)/btfmcodec/include
|
||||
#Below src is for BTFM SLAVE CODEC Driver support on LE platform.
|
||||
bt_fm_slim-objs := btfm_slim.o btfm_slim_codec.o btfm_slim_slave.o
|
||||
obj-$(CONFIG_BTFM_SLIM) += bt_fm_slim.o
|
||||
# Below src is for BTFM Driver support based on btfm codec
|
||||
btfm_slim_codec-objs := btfm_slim.o btfm_slim_hw_interface.o btfm_slim_slave.o
|
||||
obj-$(CONFIG_SLIM_BTFM_CODEC) += btfm_slim_codec.o
|
750
qcom/opensource/bt-kernel/slimbus/btfm_slim.c
Normal file
750
qcom/opensource/bt-kernel/slimbus/btfm_slim.c
Normal file
@ -0,0 +1,750 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/tlv.h>
|
||||
#include "btpower.h"
|
||||
#include "btfm_slim.h"
|
||||
#include "btfm_slim_slave.h"
|
||||
#if IS_ENABLED(CONFIG_SLIM_BTFM_CODEC)
|
||||
#include "btfm_slim_hw_interface.h"
|
||||
#endif
|
||||
|
||||
#define DELAY_FOR_PORT_OPEN_MS (200)
|
||||
#define SLIM_MANF_ID_QCOM 0x217
|
||||
#define SLIM_PROD_CODE 0x221
|
||||
#define BT_CMD_SLIM_TEST 0xbfac
|
||||
|
||||
struct class *btfm_slim_class;
|
||||
static int btfm_slim_major;
|
||||
|
||||
struct btfmslim *btfm_slim_drv_data;
|
||||
|
||||
static int btfm_num_ports_open;
|
||||
|
||||
static bool is_registered;
|
||||
|
||||
int btfm_slim_write(struct btfmslim *btfmslim,
|
||||
uint16_t reg, uint8_t reg_val, uint8_t pgd)
|
||||
{
|
||||
int ret = -1;
|
||||
uint32_t reg_addr;
|
||||
int slim_write_tries = SLIM_SLAVE_RW_MAX_TRIES;
|
||||
|
||||
BTFMSLIM_INFO("Write to %s", pgd?"PGD":"IFD");
|
||||
reg_addr = SLIM_SLAVE_REG_OFFSET + reg;
|
||||
|
||||
for ( ; slim_write_tries != 0; slim_write_tries--) {
|
||||
mutex_lock(&btfmslim->xfer_lock);
|
||||
ret = slim_writeb(pgd ? btfmslim->slim_pgd :
|
||||
&btfmslim->slim_ifd, reg_addr, reg_val);
|
||||
mutex_unlock(&btfmslim->xfer_lock);
|
||||
if (ret) {
|
||||
BTFMSLIM_DBG("retrying to Write 0x%02x to reg 0x%x ret %d",
|
||||
reg_val, reg_addr, ret);
|
||||
} else {
|
||||
BTFMSLIM_DBG("Written 0x%02x to reg 0x%x ret %d", reg_val, reg_addr, ret);
|
||||
break;
|
||||
}
|
||||
|
||||
usleep_range(5000, 5100);
|
||||
}
|
||||
if (ret) {
|
||||
BTFMSLIM_DBG("retrying to Write 0x%02x to reg 0x%x ret %d",
|
||||
reg_val, reg_addr, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int btfm_slim_read(struct btfmslim *btfmslim, uint32_t reg, uint8_t pgd)
|
||||
{
|
||||
int ret = -1;
|
||||
int slim_read_tries = SLIM_SLAVE_RW_MAX_TRIES;
|
||||
uint32_t reg_addr;
|
||||
BTFMSLIM_DBG("Read from %s", pgd?"PGD":"IFD");
|
||||
reg_addr = SLIM_SLAVE_REG_OFFSET + reg;
|
||||
|
||||
for ( ; slim_read_tries != 0; slim_read_tries--) {
|
||||
mutex_lock(&btfmslim->xfer_lock);
|
||||
|
||||
ret = slim_readb(pgd ? btfmslim->slim_pgd :
|
||||
&btfmslim->slim_ifd, reg_addr);
|
||||
BTFMSLIM_DBG("Read 0x%02x from reg 0x%x", ret, reg_addr);
|
||||
mutex_unlock(&btfmslim->xfer_lock);
|
||||
if (ret > 0)
|
||||
break;
|
||||
usleep_range(5000, 5100);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int btfm_slim_enable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch,
|
||||
uint8_t rxport, uint32_t rates, uint8_t nchan)
|
||||
{
|
||||
int ret = -1;
|
||||
int i = 0;
|
||||
struct btfmslim_ch *chan = ch;
|
||||
int chipset_ver;
|
||||
|
||||
if (!btfmslim || !ch)
|
||||
return -EINVAL;
|
||||
|
||||
BTFMSLIM_DBG("port: %d ch: %d", ch->port, ch->ch);
|
||||
|
||||
chan->dai.sruntime = slim_stream_allocate(btfmslim->slim_pgd, "BTFM_SLIM");
|
||||
if (chan->dai.sruntime == NULL) {
|
||||
BTFMSLIM_ERR("slim_stream_allocate failed");
|
||||
return -EINVAL;
|
||||
}
|
||||
chan->dai.sconfig.bps = btfmslim->bps;
|
||||
chan->dai.sconfig.direction = btfmslim->direction;
|
||||
chan->dai.sconfig.rate = rates;
|
||||
chan->dai.sconfig.ch_count = nchan;
|
||||
chan->dai.sconfig.chs = kcalloc(nchan, sizeof(unsigned int), GFP_KERNEL);
|
||||
if (!chan->dai.sconfig.chs)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < nchan; i++, ch++) {
|
||||
/* Enable port through registration setting */
|
||||
if (btfmslim->vendor_port_en) {
|
||||
ret = btfmslim->vendor_port_en(btfmslim, ch->port,
|
||||
rxport, 1);
|
||||
if (ret < 0) {
|
||||
BTFMSLIM_ERR("vendor_port_en failed ret[%d]",
|
||||
ret);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
chan->dai.sconfig.chs[i] = ch->ch;
|
||||
chan->dai.sconfig.port_mask |= BIT(ch->port);
|
||||
}
|
||||
|
||||
/* Activate the channel immediately */
|
||||
BTFMSLIM_INFO("port: %d, ch: %d", chan->port, chan->ch);
|
||||
chipset_ver = btpower_get_chipset_version();
|
||||
BTFMSLIM_INFO("chipset soc version:%x", chipset_ver);
|
||||
|
||||
/* for feedback channel, PCM bit should not be set */
|
||||
if (btfm_feedback_ch_setting) {
|
||||
BTFMSLIM_DBG("port open for feedback ch, not setting PCM bit");
|
||||
//prop.dataf = SLIM_CH_DATAF_NOT_DEFINED;
|
||||
/* reset so that next port open sets the data format properly */
|
||||
btfm_feedback_ch_setting = 0;
|
||||
}
|
||||
|
||||
ret = slim_stream_prepare(chan->dai.sruntime, &chan->dai.sconfig);
|
||||
if (ret) {
|
||||
BTFMSLIM_ERR("slim_stream_prepare failed = %d", ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = slim_stream_enable(chan->dai.sruntime);
|
||||
if (ret) {
|
||||
BTFMSLIM_ERR("slim_stream_enable failed = %d", ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
btfm_num_ports_open++;
|
||||
BTFMSLIM_INFO("btfm_num_ports_open: %d", btfm_num_ports_open);
|
||||
return ret;
|
||||
error:
|
||||
BTFMSLIM_INFO("error %d while opening port, btfm_num_ports_open: %d",
|
||||
ret, btfm_num_ports_open);
|
||||
kfree(chan->dai.sconfig.chs);
|
||||
chan->dai.sconfig.chs = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int btfm_slim_disable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch,
|
||||
uint8_t rxport, uint8_t nchan)
|
||||
{
|
||||
int ret = -1;
|
||||
int i = 0;
|
||||
int chipset_ver = 0;
|
||||
if (!btfmslim || !ch)
|
||||
return -EINVAL;
|
||||
|
||||
BTFMSLIM_INFO("port:%d ", ch->port);
|
||||
if (ch->dai.sruntime == NULL) {
|
||||
BTFMSLIM_ERR("Channel not enabled yet. returning");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (rxport && (btfmslim->sample_rate == 44100 ||
|
||||
btfmslim->sample_rate == 88200)) {
|
||||
BTFMSLIM_INFO("disconnecting the ports, removing the channel");
|
||||
/* disconnect the ports of the stream */
|
||||
ret = slim_stream_unprepare_disconnect_port(ch->dai.sruntime,
|
||||
true, false);
|
||||
if (ret != 0)
|
||||
BTFMSLIM_ERR("slim_stream_unprepare failed %d", ret);
|
||||
}
|
||||
|
||||
ret = slim_stream_disable(ch->dai.sruntime);
|
||||
if (ret != 0) {
|
||||
BTFMSLIM_ERR("slim_stream_disable failed returned val = %d", ret);
|
||||
if ((btfmslim->sample_rate != 44100) && (btfmslim->sample_rate != 88200)) {
|
||||
/* disconnect the ports of the stream */
|
||||
ret = slim_stream_unprepare_disconnect_port(ch->dai.sruntime,
|
||||
true, false);
|
||||
if (ret != 0)
|
||||
BTFMSLIM_ERR("slim_stream_unprepare failed %d", ret);
|
||||
}
|
||||
}
|
||||
|
||||
/* free the ports allocated to the stream */
|
||||
ret = slim_stream_unprepare_disconnect_port(ch->dai.sruntime, false, true);
|
||||
if (ret != 0)
|
||||
BTFMSLIM_ERR("slim_stream_unprepare failed returned val = %d", ret);
|
||||
|
||||
/* Disable port through registration setting */
|
||||
for (i = 0; i < nchan; i++, ch++) {
|
||||
if (btfmslim->vendor_port_en) {
|
||||
ret = btfmslim->vendor_port_en(btfmslim, ch->port,
|
||||
rxport, 0);
|
||||
if (ret < 0) {
|
||||
BTFMSLIM_ERR("vendor_port_en failed [%d]", ret);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ch->dai.sconfig.port_mask = 0;
|
||||
if (ch->dai.sconfig.chs != NULL) {
|
||||
kfree(ch->dai.sconfig.chs);
|
||||
BTFMSLIM_INFO("setting ch->dai.sconfig.chs to NULL");
|
||||
ch->dai.sconfig.chs = NULL;
|
||||
} else
|
||||
BTFMSLIM_ERR("ch->dai.sconfig.chs is already NULL");
|
||||
|
||||
if (btfm_num_ports_open > 0)
|
||||
btfm_num_ports_open--;
|
||||
|
||||
ch->dai.sruntime = NULL;
|
||||
|
||||
BTFMSLIM_INFO("btfm_num_ports_open: %d", btfm_num_ports_open);
|
||||
|
||||
chipset_ver = btpower_get_chipset_version();
|
||||
|
||||
if (btfm_num_ports_open == 0 && (chipset_ver == QCA_HSP_SOC_ID_0200 ||
|
||||
chipset_ver == QCA_HSP_SOC_ID_0210 ||
|
||||
chipset_ver == QCA_HSP_SOC_ID_1201 ||
|
||||
chipset_ver == QCA_HSP_SOC_ID_1211 ||
|
||||
chipset_ver == QCA_HAMILTON_SOC_ID_0100 ||
|
||||
chipset_ver == QCA_HAMILTON_SOC_ID_0101 ||
|
||||
chipset_ver == QCA_HAMILTON_SOC_ID_0200 ||
|
||||
chipset_ver == QCA_APACHE_SOC_ID_0100 ||
|
||||
chipset_ver == QCA_APACHE_SOC_ID_0110 ||
|
||||
chipset_ver == QCA_APACHE_SOC_ID_0121 ||
|
||||
chipset_ver == QCA_MOSELLE_SOC_ID_0100 ||
|
||||
chipset_ver == QCA_MOSELLE_SOC_ID_0110 ||
|
||||
chipset_ver == QCA_MOSELLE_SOC_ID_0120)) {
|
||||
BTFMSLIM_INFO("SB reset needed after all ports disabled, sleeping");
|
||||
msleep(DELAY_FOR_PORT_OPEN_MS);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int btfm_slim_alloc_port(struct btfmslim *btfmslim)
|
||||
{
|
||||
int ret = -EINVAL, i;
|
||||
int chipset_ver;
|
||||
struct btfmslim_ch *rx_chs;
|
||||
struct btfmslim_ch *tx_chs;
|
||||
|
||||
if (!btfmslim)
|
||||
return ret;
|
||||
|
||||
chipset_ver = btpower_get_chipset_version();
|
||||
BTFMSLIM_INFO("chipset soc version:%x", chipset_ver);
|
||||
|
||||
rx_chs = btfmslim->rx_chs;
|
||||
tx_chs = btfmslim->tx_chs;
|
||||
if ((chipset_ver >= QCA_CHEROKEE_SOC_ID_0310) &&
|
||||
(chipset_ver <= QCA_CHEROKEE_SOC_ID_0320_UMC)) {
|
||||
for (i = 0; (tx_chs->port != BTFM_SLIM_PGD_PORT_LAST) &&
|
||||
(i < BTFM_SLIM_NUM_CODEC_DAIS); i++, tx_chs++) {
|
||||
if (tx_chs->port == SLAVE_SB_PGD_PORT_TX1_FM)
|
||||
tx_chs->port = CHRKVER3_SB_PGD_PORT_TX1_FM;
|
||||
else if (tx_chs->port == SLAVE_SB_PGD_PORT_TX2_FM)
|
||||
tx_chs->port = CHRKVER3_SB_PGD_PORT_TX2_FM;
|
||||
BTFMSLIM_INFO("Tx port:%d", tx_chs->port);
|
||||
}
|
||||
tx_chs = btfmslim->tx_chs;
|
||||
}
|
||||
if (!rx_chs || !tx_chs)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btfm_slim_get_logical_addr(struct slim_device *slim)
|
||||
{
|
||||
int ret = 0;
|
||||
const unsigned long timeout = jiffies +
|
||||
msecs_to_jiffies(SLIM_SLAVE_PRESENT_TIMEOUT);
|
||||
BTFMSLIM_INFO("");
|
||||
|
||||
do {
|
||||
|
||||
ret = slim_get_logical_addr(slim);
|
||||
if (!ret) {
|
||||
BTFMSLIM_DBG("Assigned l-addr: 0x%x", slim->laddr);
|
||||
break;
|
||||
}
|
||||
/* Give SLIMBUS time to report present and be ready. */
|
||||
usleep_range(1000, 1100);
|
||||
BTFMSLIM_DBG("retyring get logical addr");
|
||||
} while (time_before(jiffies, timeout));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int btfm_slim_hw_init(struct btfmslim *btfmslim)
|
||||
{
|
||||
int ret = -1;
|
||||
int chipset_ver;
|
||||
struct slim_device *slim;
|
||||
struct slim_device *slim_ifd;
|
||||
|
||||
BTFMSLIM_DBG("");
|
||||
if (!btfmslim)
|
||||
return -EINVAL;
|
||||
|
||||
if (btfmslim->enabled) {
|
||||
BTFMSLIM_DBG("Already enabled");
|
||||
return 0;
|
||||
}
|
||||
|
||||
slim = btfmslim->slim_pgd;
|
||||
slim_ifd = &btfmslim->slim_ifd;
|
||||
|
||||
mutex_lock(&btfmslim->io_lock);
|
||||
BTFMSLIM_INFO(
|
||||
"PGD Enum Addr: mfr id:%.02x prod code:%.02x dev ind:%.02x ins:%.02x",
|
||||
slim->e_addr.manf_id, slim->e_addr.prod_code,
|
||||
slim->e_addr.dev_index, slim->e_addr.instance);
|
||||
|
||||
|
||||
chipset_ver = btpower_get_chipset_version();
|
||||
BTFMSLIM_INFO("chipset soc version:%x", chipset_ver);
|
||||
|
||||
if (chipset_ver == QCA_HSP_SOC_ID_0100 ||
|
||||
chipset_ver == QCA_HSP_SOC_ID_0110 ||
|
||||
chipset_ver == QCA_HSP_SOC_ID_0200 ||
|
||||
chipset_ver == QCA_HSP_SOC_ID_0210 ||
|
||||
chipset_ver == QCA_HSP_SOC_ID_1201 ||
|
||||
chipset_ver == QCA_HSP_SOC_ID_1211) {
|
||||
BTFMSLIM_INFO("chipset is hastings prime, overwriting EA");
|
||||
slim->is_laddr_valid = false;
|
||||
slim->e_addr.manf_id = SLIM_MANF_ID_QCOM;
|
||||
slim->e_addr.prod_code = SLIM_PROD_CODE;
|
||||
slim->e_addr.dev_index = 0x01;
|
||||
slim->e_addr.instance = 0x0;
|
||||
/* we are doing this to indicate that this is not a child node
|
||||
* (doesn't have call back functions). Needed only for querying
|
||||
* logical address.
|
||||
*/
|
||||
slim_ifd->dev.driver = NULL;
|
||||
slim_ifd->ctrl = btfmslim->slim_pgd->ctrl; //slimbus controller structure.
|
||||
slim_ifd->is_laddr_valid = false;
|
||||
slim_ifd->e_addr.manf_id = SLIM_MANF_ID_QCOM;
|
||||
slim_ifd->e_addr.prod_code = SLIM_PROD_CODE;
|
||||
slim_ifd->e_addr.dev_index = 0x0;
|
||||
slim_ifd->e_addr.instance = 0x0;
|
||||
slim_ifd->laddr = 0x0;
|
||||
} else if (chipset_ver == QCA_MOSELLE_SOC_ID_0100 ||
|
||||
chipset_ver == QCA_MOSELLE_SOC_ID_0110 ||
|
||||
chipset_ver == QCA_MOSELLE_SOC_ID_0120) {
|
||||
BTFMSLIM_INFO("chipset is Moselle, overwriting EA");
|
||||
slim->is_laddr_valid = false;
|
||||
slim->e_addr.manf_id = SLIM_MANF_ID_QCOM;
|
||||
slim->e_addr.prod_code = 0x222;
|
||||
slim->e_addr.dev_index = 0x01;
|
||||
slim->e_addr.instance = 0x0;
|
||||
/* we are doing this to indicate that this is not a child node
|
||||
* (doesn't have call back functions). Needed only for querying
|
||||
* logical address.
|
||||
*/
|
||||
slim_ifd->dev.driver = NULL;
|
||||
slim_ifd->ctrl = btfmslim->slim_pgd->ctrl; //slimbus controller structure.
|
||||
slim_ifd->is_laddr_valid = false;
|
||||
slim_ifd->e_addr.manf_id = SLIM_MANF_ID_QCOM;
|
||||
slim_ifd->e_addr.prod_code = 0x222;
|
||||
slim_ifd->e_addr.dev_index = 0x0;
|
||||
slim_ifd->e_addr.instance = 0x0;
|
||||
slim_ifd->laddr = 0x0;
|
||||
} else if (chipset_ver == QCA_HAMILTON_SOC_ID_0100 ||
|
||||
chipset_ver == QCA_HAMILTON_SOC_ID_0101 ||
|
||||
chipset_ver == QCA_HAMILTON_SOC_ID_0200) {
|
||||
BTFMSLIM_INFO("chipset is Hamliton, overwriting EA");
|
||||
slim->is_laddr_valid = false;
|
||||
slim->e_addr.manf_id = SLIM_MANF_ID_QCOM;
|
||||
slim->e_addr.prod_code = 0x220;
|
||||
slim->e_addr.dev_index = 0x01;
|
||||
slim->e_addr.instance = 0x0;
|
||||
/* we are doing this to indicate that this is not a child node
|
||||
* (doesn't have call back functions). Needed only for querying
|
||||
* logical address.
|
||||
*/
|
||||
slim_ifd->dev.driver = NULL;
|
||||
slim_ifd->ctrl = btfmslim->slim_pgd->ctrl; //slimbus controller structure.
|
||||
slim_ifd->is_laddr_valid = false;
|
||||
slim_ifd->e_addr.manf_id = SLIM_MANF_ID_QCOM;
|
||||
slim_ifd->e_addr.prod_code = 0x220;
|
||||
slim_ifd->e_addr.dev_index = 0x0;
|
||||
slim_ifd->e_addr.instance = 0x0;
|
||||
slim_ifd->laddr = 0x0;
|
||||
} else if (chipset_ver == QCA_CHEROKEE_SOC_ID_0200 ||
|
||||
chipset_ver == QCA_CHEROKEE_SOC_ID_0201 ||
|
||||
chipset_ver == QCA_CHEROKEE_SOC_ID_0210 ||
|
||||
chipset_ver == QCA_CHEROKEE_SOC_ID_0211 ||
|
||||
chipset_ver == QCA_CHEROKEE_SOC_ID_0310 ||
|
||||
chipset_ver == QCA_CHEROKEE_SOC_ID_0320 ||
|
||||
chipset_ver == QCA_CHEROKEE_SOC_ID_0320_UMC ||
|
||||
chipset_ver == QCA_APACHE_SOC_ID_0100 ||
|
||||
chipset_ver == QCA_APACHE_SOC_ID_0110 ||
|
||||
chipset_ver == QCA_APACHE_SOC_ID_0120 ||
|
||||
chipset_ver == QCA_APACHE_SOC_ID_0121 ||
|
||||
chipset_ver == QCA_COMANCHE_SOC_ID_0101 ||
|
||||
chipset_ver == QCA_COMANCHE_SOC_ID_0110 ||
|
||||
chipset_ver == QCA_COMANCHE_SOC_ID_0120 ||
|
||||
chipset_ver == QCA_COMANCHE_SOC_ID_0130 ||
|
||||
chipset_ver == QCA_COMANCHE_SOC_ID_4130 ||
|
||||
chipset_ver == QCA_COMANCHE_SOC_ID_5120 ||
|
||||
chipset_ver == QCA_COMANCHE_SOC_ID_5130 ) {
|
||||
BTFMSLIM_INFO("chipset is Chk/Apache/CMC, overwriting EA");
|
||||
slim->is_laddr_valid = false;
|
||||
slim->e_addr.manf_id = SLIM_MANF_ID_QCOM;
|
||||
slim->e_addr.prod_code = 0x220;
|
||||
slim->e_addr.dev_index = 0x01;
|
||||
slim->e_addr.instance = 0x0;
|
||||
/* we are doing this to indicate that this is not a child node
|
||||
* (doesn't have call back functions). Needed only for querying
|
||||
* logical address.
|
||||
*/
|
||||
slim_ifd->dev.driver = NULL;
|
||||
slim_ifd->ctrl = btfmslim->slim_pgd->ctrl; //slimbus controller structure.
|
||||
slim_ifd->is_laddr_valid = false;
|
||||
slim_ifd->e_addr.manf_id = SLIM_MANF_ID_QCOM;
|
||||
slim_ifd->e_addr.prod_code = 0x220;
|
||||
slim_ifd->e_addr.dev_index = 0x0;
|
||||
slim_ifd->e_addr.instance = 0x0;
|
||||
slim_ifd->laddr = 0x0;
|
||||
}
|
||||
BTFMSLIM_INFO(
|
||||
"PGD Enum Addr: manu id:%.02x prod code:%.02x dev idx:%.02x instance:%.02x",
|
||||
slim->e_addr.manf_id, slim->e_addr.prod_code,
|
||||
slim->e_addr.dev_index, slim->e_addr.instance);
|
||||
|
||||
BTFMSLIM_INFO(
|
||||
"IFD Enum Addr: manu id:%.02x prod code:%.02x dev idx:%.02x instance:%.02x",
|
||||
slim_ifd->e_addr.manf_id, slim_ifd->e_addr.prod_code,
|
||||
slim_ifd->e_addr.dev_index, slim_ifd->e_addr.instance);
|
||||
|
||||
/* Assign Logical Address for PGD (Ported Generic Device)
|
||||
* enumeration address
|
||||
*/
|
||||
ret = btfm_slim_get_logical_addr(btfmslim->slim_pgd);
|
||||
if (ret) {
|
||||
BTFMSLIM_ERR("failed to get slimbus logical address: %d", ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Assign Logical Address for Ported Generic Device
|
||||
* enumeration address
|
||||
*/
|
||||
ret = btfm_slim_get_logical_addr(&btfmslim->slim_ifd);
|
||||
if (ret) {
|
||||
BTFMSLIM_ERR("failed to get slimbus logical address: %d", ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = btfm_slim_alloc_port(btfmslim);
|
||||
if (ret != 0)
|
||||
goto error;
|
||||
/* Start vendor specific initialization and get port information */
|
||||
if (btfmslim->vendor_init)
|
||||
ret = btfmslim->vendor_init(btfmslim);
|
||||
|
||||
/* Only when all registers read/write successfully, it set to
|
||||
* enabled status
|
||||
*/
|
||||
btfmslim->enabled = 1;
|
||||
error:
|
||||
mutex_unlock(&btfmslim->io_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int btfm_slim_hw_deinit(struct btfmslim *btfmslim)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
BTFMSLIM_INFO("");
|
||||
if (!btfmslim)
|
||||
return -EINVAL;
|
||||
|
||||
if (!btfmslim->enabled) {
|
||||
BTFMSLIM_DBG("Already disabled");
|
||||
return 0;
|
||||
}
|
||||
mutex_lock(&btfmslim->io_lock);
|
||||
btfmslim->enabled = 0;
|
||||
mutex_unlock(&btfmslim->io_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if IS_ENABLED (CONFIG_BTFM_SLIM)
|
||||
void btfm_slim_get_hwep_details(struct slim_device *dev, struct btfmslim *btfm_slim)
|
||||
{
|
||||
}
|
||||
#else
|
||||
void btfm_slim_get_hwep_details(struct slim_device *slim, struct btfmslim *btfm_slim)
|
||||
{
|
||||
struct device_node *np = slim->dev.of_node;
|
||||
const __be32 *prop;
|
||||
struct btfmslim_ch *rx_chs = btfm_slim->rx_chs;
|
||||
struct btfmslim_ch *tx_chs = btfm_slim->tx_chs;
|
||||
int len;
|
||||
|
||||
prop = of_get_property(np, "qcom,btslim-address", &len);
|
||||
if (prop) {
|
||||
btfm_slim->device_id = be32_to_cpup(&prop[0]);
|
||||
BTFMSLIM_DBG("hwep slim address define in dt %08x", btfm_slim->device_id);
|
||||
} else {
|
||||
BTFMSLIM_ERR("btslim-address is not defined in dt using default address");
|
||||
btfm_slim->device_id = 0;
|
||||
}
|
||||
|
||||
if (!rx_chs || !tx_chs) {
|
||||
BTFMSLIM_ERR("either rx/tx channels are configured to null");
|
||||
return;
|
||||
}
|
||||
|
||||
prop = of_get_property(np, "qcom,btslimrx-channels", &len);
|
||||
if (prop) {
|
||||
/* Check if we need any protection for index */
|
||||
rx_chs[0].ch = (uint8_t)be32_to_cpup(&prop[0]);
|
||||
rx_chs[1].ch = (uint8_t)be32_to_cpup(&prop[1]);
|
||||
BTFMSLIM_DBG("Rx: id\tname\tport\tch");
|
||||
BTFMSLIM_DBG(" %d\t%s\t%d\t%d", rx_chs[0].id,
|
||||
rx_chs[0].name, rx_chs[0].port,
|
||||
rx_chs[0].ch);
|
||||
BTFMSLIM_DBG(" %d\t%s\t%d\t%d", rx_chs[1].id,
|
||||
rx_chs[1].name, rx_chs[1].port,
|
||||
rx_chs[1].ch);
|
||||
} else {
|
||||
BTFMSLIM_ERR("btslimrx channels are missing in dt using default values");
|
||||
}
|
||||
|
||||
prop = of_get_property(np, "qcom,btslimtx-channels", &len);
|
||||
if (prop) {
|
||||
/* Check if we need any protection for index */
|
||||
tx_chs[0].ch = (uint8_t)be32_to_cpup(&prop[0]);
|
||||
tx_chs[1].ch = (uint8_t)be32_to_cpup(&prop[1]);
|
||||
BTFMSLIM_DBG("Tx: id\tname\tport\tch");
|
||||
BTFMSLIM_DBG(" %d\t%s\t%d\t%x", tx_chs[0].id,
|
||||
tx_chs[0].name, tx_chs[0].port,
|
||||
tx_chs[0].ch);
|
||||
BTFMSLIM_DBG(" %d\t%s\t%d\t%x", tx_chs[1].id,
|
||||
tx_chs[1].name, tx_chs[1].port,
|
||||
tx_chs[1].ch);
|
||||
} else {
|
||||
BTFMSLIM_ERR("btslimtx channels are missing in dt using default values");
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
static int btfm_slim_status(struct slim_device *sdev,
|
||||
enum slim_device_status status)
|
||||
{
|
||||
int ret = 0;
|
||||
struct device *dev = &sdev->dev;
|
||||
struct btfmslim *btfm_slim;
|
||||
btfm_slim = dev_get_drvdata(dev);
|
||||
|
||||
#if IS_ENABLED(CONFIG_BTFM_SLIM)
|
||||
if (!is_registered) {
|
||||
ret = btfm_slim_register_codec(btfm_slim);
|
||||
}
|
||||
#else
|
||||
if (!is_registered) {
|
||||
btfm_slim_get_hwep_details(sdev, btfm_slim);
|
||||
ret = btfm_slim_register_hw_ep(btfm_slim);
|
||||
}
|
||||
#endif
|
||||
if (!ret)
|
||||
is_registered = true;
|
||||
else
|
||||
BTFMSLIM_ERR("error, registering slimbus codec failed");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long btfm_slim_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case BT_CMD_SLIM_TEST:
|
||||
BTFMSLIM_INFO("cmd BT_CMD_SLIM_TEST, call btfm_slim_hw_init");
|
||||
ret = btfm_slim_hw_init(btfm_slim_drv_data);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations bt_dev_fops = {
|
||||
.unlocked_ioctl = btfm_slim_ioctl,
|
||||
.compat_ioctl = btfm_slim_ioctl,
|
||||
};
|
||||
|
||||
static int btfm_slim_probe(struct slim_device *slim)
|
||||
{
|
||||
int ret = 0;
|
||||
struct btfmslim *btfm_slim;
|
||||
|
||||
pr_info("%s: name = %s\n", __func__, dev_name(&slim->dev));
|
||||
/*this as true during the probe then slimbus won't check for logical address*/
|
||||
slim->is_laddr_valid = true;
|
||||
is_registered = false;
|
||||
|
||||
dev_set_name(&slim->dev, "%s", BTFMSLIM_DEV_NAME);
|
||||
pr_info("%s: name = %s\n", __func__, dev_name(&slim->dev));
|
||||
|
||||
BTFMSLIM_DBG("");
|
||||
BTFMSLIM_ERR("is_laddr_valid is true");
|
||||
if (!slim->ctrl)
|
||||
return -EINVAL;
|
||||
|
||||
/* Allocation btfmslim data pointer */
|
||||
btfm_slim = kzalloc(sizeof(struct btfmslim), GFP_KERNEL);
|
||||
if (btfm_slim == NULL) {
|
||||
BTFMSLIM_ERR("error, allocation failed");
|
||||
return -ENOMEM;
|
||||
}
|
||||
/* BTFM Slimbus driver control data configuration */
|
||||
btfm_slim->slim_pgd = slim;
|
||||
/* Assign vendor specific function */
|
||||
btfm_slim->rx_chs = SLIM_SLAVE_RXPORT;
|
||||
btfm_slim->tx_chs = SLIM_SLAVE_TXPORT;
|
||||
btfm_slim->vendor_init = SLIM_SLAVE_INIT;
|
||||
btfm_slim->vendor_port_en = SLIM_SLAVE_PORT_EN;
|
||||
|
||||
/* Created Mutex for slimbus data transfer */
|
||||
mutex_init(&btfm_slim->io_lock);
|
||||
mutex_init(&btfm_slim->xfer_lock);
|
||||
dev_set_drvdata(&slim->dev, btfm_slim);
|
||||
|
||||
/* Driver specific data allocation */
|
||||
btfm_slim->dev = &slim->dev;
|
||||
ret = btpower_register_slimdev(&slim->dev);
|
||||
if (ret < 0) {
|
||||
#if IS_ENABLED(CONFIG_BTFM_SLIM)
|
||||
btfm_slim_unregister_codec(&slim->dev);
|
||||
#else
|
||||
btfm_slim_unregister_hwep();
|
||||
#endif
|
||||
ret = -EPROBE_DEFER;
|
||||
goto dealloc;
|
||||
}
|
||||
|
||||
btfm_slim_drv_data = btfm_slim;
|
||||
btfm_slim_major = register_chrdev(0, "btfm_slim", &bt_dev_fops);
|
||||
if (btfm_slim_major < 0) {
|
||||
BTFMSLIM_ERR("%s: failed to allocate char dev\n", __func__);
|
||||
ret = -1;
|
||||
goto register_err;
|
||||
}
|
||||
|
||||
btfm_slim_class = class_create(THIS_MODULE, "btfmslim-dev");
|
||||
if (IS_ERR(btfm_slim_class)) {
|
||||
BTFMSLIM_ERR("%s: coudn't create class\n", __func__);
|
||||
ret = -1;
|
||||
goto class_err;
|
||||
}
|
||||
|
||||
if (device_create(btfm_slim_class, NULL, MKDEV(btfm_slim_major, 0),
|
||||
NULL, "btfmslim") == NULL) {
|
||||
BTFMSLIM_ERR("%s: failed to allocate char dev\n", __func__);
|
||||
ret = -1;
|
||||
goto device_err;
|
||||
}
|
||||
return ret;
|
||||
|
||||
device_err:
|
||||
class_destroy(btfm_slim_class);
|
||||
class_err:
|
||||
unregister_chrdev(btfm_slim_major, "btfm_slim");
|
||||
register_err:
|
||||
#if IS_ENABLED(CONFIG_BTFM_SLIM)
|
||||
btfm_slim_unregister_codec(&slim->dev);
|
||||
#else
|
||||
btfm_slim_unregister_hwep();
|
||||
#endif
|
||||
dealloc:
|
||||
mutex_destroy(&btfm_slim->io_lock);
|
||||
mutex_destroy(&btfm_slim->xfer_lock);
|
||||
kfree(btfm_slim);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void btfm_slim_remove(struct slim_device *slim)
|
||||
{
|
||||
struct device *dev = &slim->dev;
|
||||
struct btfmslim *btfm_slim = dev_get_drvdata(dev);
|
||||
BTFMSLIM_DBG("");
|
||||
mutex_destroy(&btfm_slim->io_lock);
|
||||
mutex_destroy(&btfm_slim->xfer_lock);
|
||||
snd_soc_unregister_component(&slim->dev);
|
||||
kfree(btfm_slim);
|
||||
}
|
||||
|
||||
static const struct slim_device_id btfm_slim_id[] = {
|
||||
{
|
||||
.manf_id = SLIM_MANF_ID_QCOM,
|
||||
.prod_code = SLIM_PROD_CODE,
|
||||
.dev_index = 0x1,
|
||||
.instance = 0x0,
|
||||
},
|
||||
{
|
||||
.manf_id = SLIM_MANF_ID_QCOM,
|
||||
.prod_code = 0x220,
|
||||
.dev_index = 0x1,
|
||||
.instance = 0x0,
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(slim, btfm_slim_id);
|
||||
|
||||
static struct slim_driver btfm_slim_driver = {
|
||||
.driver = {
|
||||
.name = "btfmslim-driver",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = btfm_slim_probe,
|
||||
.device_status = btfm_slim_status,
|
||||
.remove = btfm_slim_remove,
|
||||
.id_table = btfm_slim_id
|
||||
};
|
||||
|
||||
module_slim_driver(btfm_slim_driver);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("BTFM Slimbus Slave driver");
|
176
qcom/opensource/bt-kernel/slimbus/btfm_slim.h
Normal file
176
qcom/opensource/bt-kernel/slimbus/btfm_slim.h
Normal file
@ -0,0 +1,176 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef BTFM_SLIM_H
|
||||
#define BTFM_SLIM_H
|
||||
#include <linux/slimbus.h>
|
||||
|
||||
#define BTFMSLIM_DBG(fmt, arg...) pr_debug("%s: " fmt "\n", __func__, ## arg)
|
||||
#define BTFMSLIM_INFO(fmt, arg...) pr_info("%s: " fmt "\n", __func__, ## arg)
|
||||
#define BTFMSLIM_ERR(fmt, arg...) pr_err("%s: " fmt "\n", __func__, ## arg)
|
||||
|
||||
/* Vendor specific defines
|
||||
* This should redefines in slimbus slave specific header
|
||||
*/
|
||||
#define SLIM_SLAVE_COMPATIBLE_STR "btfmslim_slave"
|
||||
#define SLIM_SLAVE_REG_OFFSET 0x0000
|
||||
#define SLIM_SLAVE_RXPORT NULL
|
||||
#define SLIM_SLAVE_TXPORT NULL
|
||||
#define SLIM_SLAVE_INIT NULL
|
||||
#define SLIM_SLAVE_PORT_EN NULL
|
||||
|
||||
/* Misc defines */
|
||||
#define SLIM_SLAVE_RW_MAX_TRIES 3
|
||||
#define SLIM_SLAVE_PRESENT_TIMEOUT 100
|
||||
|
||||
#define PGD 1
|
||||
#define IFD 0
|
||||
|
||||
#if IS_ENABLED(CONFIG_BTFM_SLIM)
|
||||
#define BTFMSLIM_DEV_NAME "btfmslim_slave"
|
||||
#else
|
||||
#define BTFMSLIM_DEV_NAME "btfmslim"
|
||||
#endif
|
||||
|
||||
/* Codec driver defines */
|
||||
enum {
|
||||
BTFM_FM_SLIM_TX = 0,
|
||||
BTFM_BT_SCO_SLIM_TX,
|
||||
BTFM_BT_SCO_A2DP_SLIM_RX,
|
||||
BTFM_BT_SPLIT_A2DP_SLIM_RX,
|
||||
BTFM_SLIM_NUM_CODEC_DAIS
|
||||
};
|
||||
|
||||
struct btfm_slim_codec_dai_data {
|
||||
struct slim_stream_config sconfig;
|
||||
struct slim_stream_runtime *sruntime;
|
||||
};
|
||||
|
||||
struct btfmslim_ch {
|
||||
int id;
|
||||
char *name;
|
||||
uint16_t port; /* slimbus port number */
|
||||
uint8_t ch; /* slimbus channel number */
|
||||
struct btfm_slim_codec_dai_data dai;
|
||||
};
|
||||
|
||||
/* Slimbus Port defines - This should be redefined in specific device file */
|
||||
#define BTFM_SLIM_PGD_PORT_LAST 0xFF
|
||||
|
||||
struct btfmslim {
|
||||
struct device *dev;
|
||||
struct slim_device *slim_pgd; //Physical address
|
||||
struct slim_device slim_ifd; //Interface address
|
||||
struct mutex io_lock;
|
||||
struct mutex xfer_lock;
|
||||
uint8_t enabled;
|
||||
uint32_t num_rx_port;
|
||||
uint32_t num_tx_port;
|
||||
uint32_t sample_rate;
|
||||
uint32_t bps;
|
||||
uint16_t direction;
|
||||
struct btfmslim_ch *rx_chs;
|
||||
struct btfmslim_ch *tx_chs;
|
||||
int (*vendor_init)(struct btfmslim *btfmslim);
|
||||
int (*vendor_port_en)(struct btfmslim *btfmslim, uint8_t port_num,
|
||||
uint8_t rxport, uint8_t enable);
|
||||
#if IS_ENABLED(CONFIG_SLIM_BTFM_CODEC)
|
||||
int device_id;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern int btfm_feedback_ch_setting;
|
||||
|
||||
/**
|
||||
* btfm_slim_hw_init: Initialize slimbus slave device
|
||||
* Returns:
|
||||
* 0: Success
|
||||
* else: Fail
|
||||
*/
|
||||
int btfm_slim_hw_init(struct btfmslim *btfmslim);
|
||||
|
||||
/**
|
||||
* btfm_slim_hw_deinit: Deinitialize slimbus slave device
|
||||
* Returns:
|
||||
* 0: Success
|
||||
* else: Fail
|
||||
*/
|
||||
int btfm_slim_hw_deinit(struct btfmslim *btfmslim);
|
||||
|
||||
/**
|
||||
* btfm_slim_write: write value to pgd or ifd device
|
||||
* @btfmslim: slimbus slave device data pointer.
|
||||
* @reg: slimbus slave register address
|
||||
* @reg_val: value to write at register address
|
||||
* @pgd: selection for device: either PGD or IFD
|
||||
* Returns:
|
||||
No of bytes written
|
||||
-1
|
||||
*/
|
||||
int btfm_slim_write(struct btfmslim *btfmslim,
|
||||
uint16_t reg, uint8_t reg_val, uint8_t pgd);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* btfm_slim_read: read value from pgd or ifd device
|
||||
* @btfmslim: slimbus slave device data pointer.
|
||||
* @reg: slimbus slave register address
|
||||
* @dest: data pointer to read
|
||||
* @pgd: selection for device: either PGD or IFD
|
||||
* Returns:
|
||||
No of bytes read
|
||||
-1
|
||||
*/
|
||||
int btfm_slim_read(struct btfmslim *btfmslim,
|
||||
uint32_t reg, uint8_t pgd);
|
||||
|
||||
|
||||
/**
|
||||
* btfm_slim_enable_ch: enable channel for slimbus slave port
|
||||
* @btfmslim: slimbus slave device data pointer.
|
||||
* @ch: slimbus slave channel pointer
|
||||
* @rxport: rxport or txport
|
||||
* Returns:
|
||||
* -EINVAL
|
||||
* -ETIMEDOUT
|
||||
* -ENOMEM
|
||||
*/
|
||||
int btfm_slim_enable_ch(struct btfmslim *btfmslim,
|
||||
struct btfmslim_ch *ch, uint8_t rxport, uint32_t rates,
|
||||
uint8_t nchan);
|
||||
|
||||
/**
|
||||
* btfm_slim_disable_ch: disable channel for slimbus slave port
|
||||
* @btfmslim: slimbus slave device data pointer.
|
||||
* @ch: slimbus slave channel pointer
|
||||
* @rxport: rxport or txport
|
||||
* @nChan: number of chaneels.
|
||||
* Returns:
|
||||
* -EINVAL
|
||||
* -ETIMEDOUT
|
||||
* -ENOMEM
|
||||
*/
|
||||
int btfm_slim_disable_ch(struct btfmslim *btfmslim,
|
||||
struct btfmslim_ch *ch, uint8_t rxport, uint8_t nchan);
|
||||
|
||||
/**
|
||||
* btfm_slim_register_codec: Register codec driver in slimbus device node
|
||||
* @btfmslim: slimbus slave device data pointer.
|
||||
* Returns:
|
||||
* -ENOMEM
|
||||
* 0
|
||||
*/
|
||||
int btfm_slim_register_codec(struct btfmslim *btfmslim);
|
||||
|
||||
/**
|
||||
* btfm_slim_unregister_codec: Unregister codec driver in slimbus device node
|
||||
* @dev: device node
|
||||
* Returns:
|
||||
* VOID
|
||||
*/
|
||||
void btfm_slim_unregister_codec(struct device *dev);
|
||||
#endif /* BTFM_SLIM_H */
|
466
qcom/opensource/bt-kernel/slimbus/btfm_slim_codec.c
Normal file
466
qcom/opensource/bt-kernel/slimbus/btfm_slim_codec.c
Normal file
@ -0,0 +1,466 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/slimbus.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/tlv.h>
|
||||
#include "btfm_slim.h"
|
||||
|
||||
static int bt_soc_enable_status;
|
||||
int btfm_feedback_ch_setting;
|
||||
|
||||
static int btfm_slim_codec_write(struct snd_soc_component *codec,
|
||||
unsigned int reg, unsigned int value)
|
||||
{
|
||||
BTFMSLIM_DBG("");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int btfm_slim_codec_read(struct snd_soc_component *codec,
|
||||
unsigned int reg)
|
||||
{
|
||||
BTFMSLIM_DBG("");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btfm_soc_status_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
BTFMSLIM_DBG("");
|
||||
ucontrol->value.integer.value[0] = bt_soc_enable_status;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int btfm_soc_status_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
BTFMSLIM_DBG("");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int btfm_get_feedback_ch_setting(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
BTFMSLIM_DBG("");
|
||||
ucontrol->value.integer.value[0] = btfm_feedback_ch_setting;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int btfm_put_feedback_ch_setting(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
BTFMSLIM_DBG("");
|
||||
btfm_feedback_ch_setting = ucontrol->value.integer.value[0];
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new status_controls[] = {
|
||||
SOC_SINGLE_EXT("BT SOC status", 0, 0, 1, 0,
|
||||
btfm_soc_status_get,
|
||||
btfm_soc_status_put),
|
||||
SOC_SINGLE_EXT("BT set feedback channel", 0, 0, 1, 0,
|
||||
btfm_get_feedback_ch_setting,
|
||||
btfm_put_feedback_ch_setting)
|
||||
};
|
||||
|
||||
|
||||
static int btfm_slim_codec_probe(struct snd_soc_component *codec)
|
||||
{
|
||||
BTFMSLIM_DBG("");
|
||||
snd_soc_add_component_controls(codec, status_controls,
|
||||
ARRAY_SIZE(status_controls));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void btfm_slim_codec_remove(struct snd_soc_component *codec)
|
||||
{
|
||||
BTFMSLIM_DBG("");
|
||||
}
|
||||
|
||||
static int btfm_slim_dai_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
int ret = -1;
|
||||
struct btfmslim *btfmslim = snd_soc_component_get_drvdata(dai->component);
|
||||
|
||||
BTFMSLIM_DBG("substream = %s stream = %d dai->name = %s",
|
||||
substream->name, substream->stream, dai->name);
|
||||
ret = btfm_slim_hw_init(btfmslim);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void btfm_slim_dai_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
int i;
|
||||
struct btfmslim *btfmslim = snd_soc_component_get_drvdata(dai->component);
|
||||
struct btfmslim_ch *ch;
|
||||
uint8_t rxport, nchan = 1;
|
||||
|
||||
BTFMSLIM_DBG("dai->name: %s, dai->id: %d, dai->rate: %d", dai->name,
|
||||
dai->id, dai->rate);
|
||||
|
||||
switch (dai->id) {
|
||||
case BTFM_FM_SLIM_TX:
|
||||
nchan = 2;
|
||||
ch = btfmslim->tx_chs;
|
||||
rxport = 0;
|
||||
break;
|
||||
case BTFM_BT_SCO_SLIM_TX:
|
||||
ch = btfmslim->tx_chs;
|
||||
rxport = 0;
|
||||
break;
|
||||
case BTFM_BT_SCO_A2DP_SLIM_RX:
|
||||
case BTFM_BT_SPLIT_A2DP_SLIM_RX:
|
||||
ch = btfmslim->rx_chs;
|
||||
rxport = 1;
|
||||
break;
|
||||
case BTFM_SLIM_NUM_CODEC_DAIS:
|
||||
default:
|
||||
BTFMSLIM_ERR("dai->id is invalid:%d", dai->id);
|
||||
return;
|
||||
}
|
||||
/* Search for dai->id matched port handler */
|
||||
for (i = 0; (i < BTFM_SLIM_NUM_CODEC_DAIS) &&
|
||||
(ch->id != BTFM_SLIM_NUM_CODEC_DAIS) &&
|
||||
(ch->id != dai->id); ch++, i++)
|
||||
;
|
||||
|
||||
if ((ch->port == BTFM_SLIM_PGD_PORT_LAST) ||
|
||||
(ch->id == BTFM_SLIM_NUM_CODEC_DAIS)) {
|
||||
BTFMSLIM_ERR("ch is invalid!!");
|
||||
return;
|
||||
}
|
||||
|
||||
btfm_slim_disable_ch(btfmslim, ch, rxport, nchan);
|
||||
btfm_slim_hw_deinit(btfmslim);
|
||||
}
|
||||
|
||||
static int btfm_slim_dai_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct btfmslim *btfmslim;
|
||||
|
||||
btfmslim = snd_soc_component_get_drvdata(dai->component);
|
||||
btfmslim->bps = params_width(params);
|
||||
btfmslim->direction = substream->stream;
|
||||
BTFMSLIM_DBG("dai->name = %s DAI-ID %x rate %d bps %d num_ch %d",
|
||||
dai->name, dai->id, params_rate(params), params_width(params),
|
||||
params_channels(params));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btfm_slim_dai_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
int i = 0;
|
||||
struct btfmslim_ch *ch;
|
||||
uint8_t rxport, nchan = 1;
|
||||
struct btfmslim *btfmslim;
|
||||
|
||||
btfmslim = snd_soc_component_get_drvdata(dai->component);
|
||||
btfmslim->direction = substream->stream;
|
||||
bt_soc_enable_status = 0;
|
||||
BTFMSLIM_INFO("dai->name: %s, dai->id: %d, dai->rate: %d direction: %d", dai->name,
|
||||
dai->id, dai->rate, btfmslim->direction);
|
||||
|
||||
/* save sample rate */
|
||||
btfmslim->sample_rate = dai->rate;
|
||||
|
||||
switch (dai->id) {
|
||||
case BTFM_FM_SLIM_TX:
|
||||
nchan = 2;
|
||||
ch = btfmslim->tx_chs;
|
||||
rxport = 0;
|
||||
break;
|
||||
case BTFM_BT_SCO_SLIM_TX:
|
||||
ch = btfmslim->tx_chs;
|
||||
rxport = 0;
|
||||
break;
|
||||
case BTFM_BT_SCO_A2DP_SLIM_RX:
|
||||
case BTFM_BT_SPLIT_A2DP_SLIM_RX:
|
||||
ch = btfmslim->rx_chs;
|
||||
rxport = 1;
|
||||
break;
|
||||
case BTFM_SLIM_NUM_CODEC_DAIS:
|
||||
default:
|
||||
BTFMSLIM_ERR("dai->id is invalid:%d", dai->id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Search for dai->id matched port handler */
|
||||
for (i = 0; (i < BTFM_SLIM_NUM_CODEC_DAIS) &&
|
||||
(ch->id != BTFM_SLIM_NUM_CODEC_DAIS) &&
|
||||
(ch->id != dai->id); ch++, i++)
|
||||
;
|
||||
|
||||
if ((ch->port == BTFM_SLIM_PGD_PORT_LAST) ||
|
||||
(ch->id == BTFM_SLIM_NUM_CODEC_DAIS)) {
|
||||
BTFMSLIM_ERR("ch is invalid!!");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = btfm_slim_enable_ch(btfmslim, ch, rxport, dai->rate, nchan);
|
||||
|
||||
/* save the enable channel status */
|
||||
if (ret == 0)
|
||||
bt_soc_enable_status = 1;
|
||||
|
||||
if (ret == -EISCONN) {
|
||||
BTFMSLIM_ERR("channel opened without closing, returning success");
|
||||
ret = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* This function will be called once during boot up */
|
||||
static int btfm_slim_dai_set_channel_map(struct snd_soc_dai *dai,
|
||||
unsigned int tx_num, unsigned int *tx_slot,
|
||||
unsigned int rx_num, unsigned int *rx_slot)
|
||||
{
|
||||
int ret = 0, i;
|
||||
struct btfmslim *btfmslim = snd_soc_component_get_drvdata(dai->component);
|
||||
struct btfmslim_ch *rx_chs;
|
||||
struct btfmslim_ch *tx_chs;
|
||||
|
||||
BTFMSLIM_DBG("");
|
||||
|
||||
if (!btfmslim)
|
||||
return -EINVAL;
|
||||
|
||||
rx_chs = btfmslim->rx_chs;
|
||||
tx_chs = btfmslim->tx_chs;
|
||||
|
||||
if (!rx_chs || !tx_chs)
|
||||
return ret;
|
||||
|
||||
BTFMSLIM_DBG("Rx: id\tname\tport\tch");
|
||||
for (i = 0; (rx_chs->port != BTFM_SLIM_PGD_PORT_LAST) && (i < rx_num);
|
||||
i++, rx_chs++) {
|
||||
/* Set Rx Channel number from machine driver and
|
||||
* get channel handler from slimbus driver
|
||||
*/
|
||||
rx_chs->ch = *(uint8_t *)(rx_slot + i);
|
||||
BTFMSLIM_DBG(" %d\t%s\t%d\t%d\t", rx_chs->id,
|
||||
rx_chs->name, rx_chs->port, rx_chs->ch);
|
||||
}
|
||||
|
||||
BTFMSLIM_DBG("Tx: id\tname\tport\tch");
|
||||
for (i = 0; (tx_chs->port != BTFM_SLIM_PGD_PORT_LAST) && (i < tx_num);
|
||||
i++, tx_chs++) {
|
||||
/* Set Tx Channel number from machine driver and
|
||||
* get channel handler from slimbus driver
|
||||
*/
|
||||
tx_chs->ch = *(uint8_t *)(tx_slot + i);
|
||||
BTFMSLIM_DBG(" %d\t%s\t%d\t%d\t", tx_chs->id,
|
||||
tx_chs->name, tx_chs->port, tx_chs->ch);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int btfm_slim_dai_get_channel_map(struct snd_soc_dai *dai,
|
||||
unsigned int *tx_num, unsigned int *tx_slot,
|
||||
unsigned int *rx_num, unsigned int *rx_slot)
|
||||
{
|
||||
int i, ret = -EINVAL, *slot = NULL, j = 0, num = 1;
|
||||
struct btfmslim *btfmslim = snd_soc_component_get_drvdata(dai->component);
|
||||
struct btfmslim_ch *ch = NULL;
|
||||
|
||||
if (!btfmslim)
|
||||
return ret;
|
||||
|
||||
switch (dai->id) {
|
||||
case BTFM_FM_SLIM_TX:
|
||||
num = 2;
|
||||
/* fall through */
|
||||
fallthrough;
|
||||
case BTFM_BT_SCO_SLIM_TX:
|
||||
if (!tx_slot || !tx_num) {
|
||||
BTFMSLIM_ERR("Invalid tx_slot %p or tx_num %p",
|
||||
tx_slot, tx_num);
|
||||
return -EINVAL;
|
||||
}
|
||||
ch = btfmslim->tx_chs;
|
||||
if (!ch)
|
||||
return -EINVAL;
|
||||
slot = tx_slot;
|
||||
*rx_slot = 0;
|
||||
*tx_num = num;
|
||||
*rx_num = 0;
|
||||
break;
|
||||
case BTFM_BT_SCO_A2DP_SLIM_RX:
|
||||
case BTFM_BT_SPLIT_A2DP_SLIM_RX:
|
||||
if (!rx_slot || !rx_num) {
|
||||
BTFMSLIM_ERR("Invalid rx_slot %p or rx_num %p",
|
||||
rx_slot, rx_num);
|
||||
return -EINVAL;
|
||||
}
|
||||
ch = btfmslim->rx_chs;
|
||||
if (!ch)
|
||||
return -EINVAL;
|
||||
slot = rx_slot;
|
||||
*tx_slot = 0;
|
||||
*tx_num = 0;
|
||||
*rx_num = num;
|
||||
break;
|
||||
default:
|
||||
BTFMSLIM_ERR("Unsupported DAI %d", dai->id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
do {
|
||||
if (!ch)
|
||||
return -EINVAL;
|
||||
for (i = 0; (i < BTFM_SLIM_NUM_CODEC_DAIS) && (ch->id !=
|
||||
BTFM_SLIM_NUM_CODEC_DAIS) && (ch->id != dai->id);
|
||||
ch++, i++)
|
||||
;
|
||||
|
||||
if (ch->id == BTFM_SLIM_NUM_CODEC_DAIS ||
|
||||
i == BTFM_SLIM_NUM_CODEC_DAIS) {
|
||||
BTFMSLIM_ERR(
|
||||
"No channel has been allocated for dai (%d)",
|
||||
dai->id);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!slot)
|
||||
return -EINVAL;
|
||||
*(slot + j) = ch->ch;
|
||||
BTFMSLIM_DBG("id:%d, port:%d, ch:%d, slot: %d", ch->id,
|
||||
ch->port, ch->ch, *(slot + j));
|
||||
|
||||
/* In case it has mulitiple channels */
|
||||
if (++j < num)
|
||||
ch++;
|
||||
} while (j < num);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_ops btfmslim_dai_ops = {
|
||||
.startup = btfm_slim_dai_startup,
|
||||
.shutdown = btfm_slim_dai_shutdown,
|
||||
.hw_params = btfm_slim_dai_hw_params,
|
||||
.prepare = btfm_slim_dai_prepare,
|
||||
.set_channel_map = btfm_slim_dai_set_channel_map,
|
||||
.get_channel_map = btfm_slim_dai_get_channel_map,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver btfmslim_dai[] = {
|
||||
{ /* FM Audio data multiple channel : FM -> qdsp */
|
||||
.name = "btfm_fm_slim_tx",
|
||||
.id = BTFM_FM_SLIM_TX,
|
||||
.capture = {
|
||||
.stream_name = "FM TX Capture",
|
||||
.rates = SNDRV_PCM_RATE_48000, /* 48 KHz */
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */
|
||||
.rate_max = 48000,
|
||||
.rate_min = 48000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
},
|
||||
.ops = &btfmslim_dai_ops,
|
||||
},
|
||||
{ /* Bluetooth SCO voice uplink: bt -> lpass */
|
||||
.name = "btfm_bt_sco_slim_tx",
|
||||
.id = BTFM_BT_SCO_SLIM_TX,
|
||||
.capture = {
|
||||
.stream_name = "SCO TX Capture",
|
||||
/* 8 KHz or 16 KHz */
|
||||
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000
|
||||
| SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000
|
||||
| SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000
|
||||
| SNDRV_PCM_RATE_192000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */
|
||||
.rate_max = 192000,
|
||||
.rate_min = 8000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
},
|
||||
.ops = &btfmslim_dai_ops,
|
||||
},
|
||||
{ /* Bluetooth SCO voice downlink: lpass -> bt or A2DP Playback */
|
||||
.name = "btfm_bt_sco_a2dp_slim_rx",
|
||||
.id = BTFM_BT_SCO_A2DP_SLIM_RX,
|
||||
.playback = {
|
||||
.stream_name = "SCO A2DP RX Playback",
|
||||
/* 8/16/44.1/48/88.2/96 Khz */
|
||||
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000
|
||||
| SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000
|
||||
| SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000
|
||||
| SNDRV_PCM_RATE_192000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */
|
||||
.rate_max = 192000,
|
||||
.rate_min = 8000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
},
|
||||
.ops = &btfmslim_dai_ops,
|
||||
},
|
||||
{ /* Bluetooth Split A2DP data: qdsp -> bt */
|
||||
.name = "btfm_bt_split_a2dp_slim_rx",
|
||||
.id = BTFM_BT_SPLIT_A2DP_SLIM_RX,
|
||||
.playback = {
|
||||
.stream_name = "SPLIT A2DP Playback",
|
||||
.rates = SNDRV_PCM_RATE_48000, /* 48 KHz */
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */
|
||||
.rate_max = 48000,
|
||||
.rate_min = 48000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
},
|
||||
.ops = &btfmslim_dai_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver btfmslim_codec = {
|
||||
.probe = btfm_slim_codec_probe,
|
||||
.remove = btfm_slim_codec_remove,
|
||||
.read = btfm_slim_codec_read,
|
||||
.write = btfm_slim_codec_write,
|
||||
};
|
||||
|
||||
int btfm_slim_register_codec(struct btfmslim *btfm_slim)
|
||||
{
|
||||
int ret = 0;
|
||||
struct device *dev = btfm_slim->dev;
|
||||
|
||||
BTFMSLIM_DBG("");
|
||||
dev_err(dev, "\n");
|
||||
|
||||
/* Register Codec driver */
|
||||
ret = snd_soc_register_component(dev, &btfmslim_codec,
|
||||
btfmslim_dai, ARRAY_SIZE(btfmslim_dai));
|
||||
if (ret)
|
||||
BTFMSLIM_ERR("failed to register codec (%d)", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void btfm_slim_unregister_codec(struct device *dev)
|
||||
{
|
||||
BTFMSLIM_DBG("");
|
||||
/* Unregister Codec driver */
|
||||
snd_soc_unregister_component(dev);
|
||||
}
|
||||
|
||||
MODULE_DESCRIPTION("BTFM Slimbus Codec driver");
|
||||
MODULE_LICENSE("GPL v2");
|
546
qcom/opensource/bt-kernel/slimbus/btfm_slim_hw_interface.c
Normal file
546
qcom/opensource/bt-kernel/slimbus/btfm_slim_hw_interface.c
Normal file
@ -0,0 +1,546 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021, 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/slimbus.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/tlv.h>
|
||||
#include "btfm_slim.h"
|
||||
#include "btfm_slim_hw_interface.h"
|
||||
#include "btfm_codec_hw_interface.h"
|
||||
|
||||
static int bt_soc_enable_status;
|
||||
int btfm_feedback_ch_setting;
|
||||
static uint8_t usecase_codec;
|
||||
|
||||
static int btfm_slim_hwep_write(struct snd_soc_component *codec,
|
||||
unsigned int reg, unsigned int value)
|
||||
{
|
||||
BTFMSLIM_DBG("");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int btfm_slim_hwep_read(struct snd_soc_component *codec,
|
||||
unsigned int reg)
|
||||
{
|
||||
BTFMSLIM_DBG("");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btfm_soc_status_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
BTFMSLIM_DBG("");
|
||||
ucontrol->value.integer.value[0] = bt_soc_enable_status;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int btfm_soc_status_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
BTFMSLIM_DBG("");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int btfm_get_feedback_ch_setting(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
BTFMSLIM_DBG("");
|
||||
ucontrol->value.integer.value[0] = btfm_feedback_ch_setting;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int btfm_put_feedback_ch_setting(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
BTFMSLIM_DBG("");
|
||||
btfm_feedback_ch_setting = ucontrol->value.integer.value[0];
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int btfm_get_codec_type(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
BTFMSLIM_DBG("current codec type:%s", codec_text[usecase_codec]);
|
||||
ucontrol->value.integer.value[0] = usecase_codec;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int btfm_put_codec_type(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
usecase_codec = ucontrol->value.integer.value[0];
|
||||
BTFMSLIM_DBG("codec type set to:%s", codec_text[usecase_codec]);
|
||||
return 1;
|
||||
}
|
||||
static struct snd_kcontrol_new status_controls[] = {
|
||||
SOC_SINGLE_EXT("BT SOC status", 0, 0, 1, 0,
|
||||
btfm_soc_status_get, btfm_soc_status_put),
|
||||
SOC_SINGLE_EXT("BT set feedback channel", 0, 0, 1, 0,
|
||||
btfm_get_feedback_ch_setting,
|
||||
btfm_put_feedback_ch_setting),
|
||||
SOC_ENUM_EXT("BT codec type", codec_display,
|
||||
btfm_get_codec_type, btfm_put_codec_type),
|
||||
};
|
||||
|
||||
|
||||
static int btfm_slim_hwep_probe(struct snd_soc_component *codec)
|
||||
{
|
||||
BTFMSLIM_DBG("");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void btfm_slim_hwep_remove(struct snd_soc_component *codec)
|
||||
{
|
||||
BTFMSLIM_DBG("");
|
||||
}
|
||||
|
||||
static int btfm_slim_dai_startup(void *dai)
|
||||
{
|
||||
struct hwep_data *hwep_info = (struct hwep_data *)dai;
|
||||
struct btfmslim *btfmslim = dev_get_drvdata(hwep_info->dev);
|
||||
int ret = -1;
|
||||
|
||||
BTFMSLIM_DBG("");
|
||||
ret = btfm_slim_hw_init(btfmslim);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void btfm_slim_dai_shutdown(void *dai, int id)
|
||||
{
|
||||
struct hwep_data *hwep_info = (struct hwep_data *)dai;
|
||||
struct btfmslim *btfmslim = dev_get_drvdata(hwep_info->dev);
|
||||
struct btfmslim_ch *ch;
|
||||
int i;
|
||||
uint8_t rxport, nchan = 1;
|
||||
|
||||
BTFMSLIM_DBG("");
|
||||
switch (id) {
|
||||
case BTFM_FM_SLIM_TX:
|
||||
nchan = 2;
|
||||
ch = btfmslim->tx_chs;
|
||||
rxport = 0;
|
||||
break;
|
||||
case BTFM_BT_SCO_SLIM_TX:
|
||||
ch = btfmslim->tx_chs;
|
||||
rxport = 0;
|
||||
break;
|
||||
case BTFM_BT_SCO_A2DP_SLIM_RX:
|
||||
case BTFM_BT_SPLIT_A2DP_SLIM_RX:
|
||||
ch = btfmslim->rx_chs;
|
||||
rxport = 1;
|
||||
break;
|
||||
case BTFM_SLIM_NUM_CODEC_DAIS:
|
||||
default:
|
||||
BTFMSLIM_ERR("id is invalid:%d", id);
|
||||
return;
|
||||
}
|
||||
/* Search for dai->id matched port handler */
|
||||
for (i = 0; (i < BTFM_SLIM_NUM_CODEC_DAIS) &&
|
||||
(ch->id != BTFM_SLIM_NUM_CODEC_DAIS) &&
|
||||
(ch->id != id); ch++, i++)
|
||||
;
|
||||
|
||||
if ((ch->port == BTFM_SLIM_PGD_PORT_LAST) ||
|
||||
(ch->id == BTFM_SLIM_NUM_CODEC_DAIS)) {
|
||||
BTFMSLIM_ERR("ch is invalid!!");
|
||||
return;
|
||||
}
|
||||
|
||||
btfm_slim_disable_ch(btfmslim, ch, rxport, nchan);
|
||||
btfm_slim_hw_deinit(btfmslim);
|
||||
}
|
||||
|
||||
static int btfm_slim_dai_hw_params(void *dai, uint32_t bps,
|
||||
uint32_t direction,
|
||||
uint8_t num_channels) {
|
||||
struct hwep_data *hwep_info = (struct hwep_data *)dai;
|
||||
struct btfmslim *btfmslim = dev_get_drvdata(hwep_info->dev);
|
||||
|
||||
BTFMSLIM_DBG("");
|
||||
btfmslim->bps = bps;
|
||||
btfmslim->direction = direction;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void btfm_get_sampling_rate(uint32_t *sampling_rate)
|
||||
{
|
||||
uint8_t codec_types_avb = ARRAY_SIZE(codec_text);
|
||||
if (usecase_codec > (codec_types_avb - 1)) {
|
||||
BTFMSLIM_ERR("falling back to use default sampling_rate: %u",
|
||||
*sampling_rate);
|
||||
return;
|
||||
}
|
||||
|
||||
if (*sampling_rate == 44100 || *sampling_rate == 48000) {
|
||||
if (usecase_codec == LDAC ||
|
||||
usecase_codec == APTX_AD)
|
||||
*sampling_rate = (*sampling_rate) *2;
|
||||
}
|
||||
|
||||
if (usecase_codec == LC3_VOICE ||
|
||||
usecase_codec == APTX_AD_SPEECH ||
|
||||
usecase_codec == LC3 || usecase_codec == APTX_AD_R4) {
|
||||
*sampling_rate = 96000;
|
||||
}
|
||||
|
||||
if (usecase_codec == APTX_AD_QLEA)
|
||||
*sampling_rate = 192000;
|
||||
|
||||
BTFMSLIM_INFO("current usecase codec type %s and sampling rate:%u khz",
|
||||
codec_text[usecase_codec], *sampling_rate);
|
||||
}
|
||||
static int btfm_slim_dai_prepare(void *dai, uint32_t sampling_rate, uint32_t direction, int id)
|
||||
{
|
||||
struct hwep_data *hwep_info = (struct hwep_data *)dai;
|
||||
struct btfmslim *btfmslim = dev_get_drvdata(hwep_info->dev);
|
||||
struct btfmslim_ch *ch;
|
||||
int ret = -EINVAL;
|
||||
int i = 0;
|
||||
uint8_t rxport, nchan = 1;
|
||||
|
||||
btfmslim->direction = direction;
|
||||
bt_soc_enable_status = 0;
|
||||
|
||||
btfm_get_sampling_rate(&sampling_rate);
|
||||
/* save sample rate */
|
||||
btfmslim->sample_rate = sampling_rate;
|
||||
|
||||
switch (id) {
|
||||
case BTFM_FM_SLIM_TX:
|
||||
nchan = 2;
|
||||
ch = btfmslim->tx_chs;
|
||||
rxport = 0;
|
||||
break;
|
||||
case BTFM_BT_SCO_SLIM_TX:
|
||||
ch = btfmslim->tx_chs;
|
||||
rxport = 0;
|
||||
break;
|
||||
case BTFM_BT_SCO_A2DP_SLIM_RX:
|
||||
case BTFM_BT_SPLIT_A2DP_SLIM_RX:
|
||||
ch = btfmslim->rx_chs;
|
||||
rxport = 1;
|
||||
break;
|
||||
case BTFM_SLIM_NUM_CODEC_DAIS:
|
||||
default:
|
||||
BTFMSLIM_ERR("id is invalid:%d", id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Search for dai->id matched port handler */
|
||||
for (i = 0; (i < BTFM_SLIM_NUM_CODEC_DAIS) &&
|
||||
(ch->id != BTFM_SLIM_NUM_CODEC_DAIS) &&
|
||||
(ch->id != id); ch++, i++)
|
||||
;
|
||||
|
||||
if ((ch->port == BTFM_SLIM_PGD_PORT_LAST) ||
|
||||
(ch->id == BTFM_SLIM_NUM_CODEC_DAIS)) {
|
||||
BTFMSLIM_ERR("ch is invalid!!");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = btfm_slim_enable_ch(btfmslim, ch, rxport, sampling_rate, nchan);
|
||||
|
||||
/* save the enable channel status */
|
||||
if (ret == 0)
|
||||
bt_soc_enable_status = 1;
|
||||
|
||||
if (ret == -EISCONN) {
|
||||
BTFMSLIM_ERR("channel opened without closing, returning success");
|
||||
ret = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* This function will be called once during boot up */
|
||||
static int btfm_slim_dai_set_channel_map(void *dai,
|
||||
unsigned int tx_num, unsigned int *tx_slot,
|
||||
unsigned int rx_num, unsigned int *rx_slot)
|
||||
{
|
||||
|
||||
struct hwep_data *hwep_info = (struct hwep_data *)dai;
|
||||
struct btfmslim *btfmslim = dev_get_drvdata(hwep_info->dev);
|
||||
struct btfmslim_ch *rx_chs;
|
||||
struct btfmslim_ch *tx_chs;
|
||||
int ret = 0, i;
|
||||
|
||||
BTFMSLIM_DBG("");
|
||||
|
||||
if (!btfmslim)
|
||||
return -EINVAL;
|
||||
|
||||
rx_chs = btfmslim->rx_chs;
|
||||
tx_chs = btfmslim->tx_chs;
|
||||
|
||||
if (!rx_chs || !tx_chs)
|
||||
return ret;
|
||||
|
||||
BTFMSLIM_DBG("Rx: id\tname\tport\tch");
|
||||
for (i = 0; (rx_chs->port != BTFM_SLIM_PGD_PORT_LAST) && (i < rx_num);
|
||||
i++, rx_chs++) {
|
||||
/* Set Rx Channel number from machine driver and
|
||||
* get channel handler from slimbus driver
|
||||
*/
|
||||
rx_chs->ch = *(uint8_t *)(rx_slot + i);
|
||||
BTFMSLIM_DBG(" %d\t%s\t%d\t%x", rx_chs->id,
|
||||
rx_chs->name, rx_chs->port, rx_chs->ch);
|
||||
}
|
||||
|
||||
BTFMSLIM_DBG("Tx: id\tname\tport\tch");
|
||||
for (i = 0; (tx_chs->port != BTFM_SLIM_PGD_PORT_LAST) && (i < tx_num);
|
||||
i++, tx_chs++) {
|
||||
/* Set Tx Channel number from machine driver and
|
||||
* get channel handler from slimbus driver
|
||||
*/
|
||||
tx_chs->ch = *(uint8_t *)(tx_slot + i);
|
||||
BTFMSLIM_DBG(" %d\t%s\t%d\t%x", tx_chs->id,
|
||||
tx_chs->name, tx_chs->port, tx_chs->ch);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int btfm_slim_dai_get_channel_map(void *dai,
|
||||
unsigned int *tx_num, unsigned int *tx_slot,
|
||||
unsigned int *rx_num, unsigned int *rx_slot, int id)
|
||||
{
|
||||
|
||||
struct hwep_data *hwep_info = (struct hwep_data *)dai;
|
||||
struct btfmslim *btfmslim = dev_get_drvdata(hwep_info->dev);
|
||||
int i, ret = -EINVAL, *slot = NULL, j = 0, num = 1;
|
||||
struct btfmslim_ch *ch = NULL;
|
||||
|
||||
BTFMSLIM_DBG("");
|
||||
if (!btfmslim)
|
||||
return ret;
|
||||
|
||||
switch (id) {
|
||||
case BTFM_FM_SLIM_TX:
|
||||
num = 2;
|
||||
fallthrough;
|
||||
case BTFM_BT_SCO_SLIM_TX:
|
||||
if (!tx_slot || !tx_num) {
|
||||
BTFMSLIM_ERR("Invalid tx_slot %p or tx_num %p",
|
||||
tx_slot, tx_num);
|
||||
return -EINVAL;
|
||||
}
|
||||
ch = btfmslim->tx_chs;
|
||||
if (!ch)
|
||||
return -EINVAL;
|
||||
slot = tx_slot;
|
||||
*rx_slot = 0;
|
||||
*tx_num = num;
|
||||
*rx_num = 0;
|
||||
break;
|
||||
case BTFM_BT_SCO_A2DP_SLIM_RX:
|
||||
case BTFM_BT_SPLIT_A2DP_SLIM_RX:
|
||||
if (!rx_slot || !rx_num) {
|
||||
BTFMSLIM_ERR("Invalid rx_slot %p or rx_num %p",
|
||||
rx_slot, rx_num);
|
||||
return -EINVAL;
|
||||
}
|
||||
ch = btfmslim->rx_chs;
|
||||
if (!ch)
|
||||
return -EINVAL;
|
||||
slot = rx_slot;
|
||||
*tx_slot = 0;
|
||||
*tx_num = 0;
|
||||
*rx_num = num;
|
||||
break;
|
||||
default:
|
||||
BTFMSLIM_ERR("Unsupported DAI %d", id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
do {
|
||||
if (!ch)
|
||||
return -EINVAL;
|
||||
for (i = 0; (i < BTFM_SLIM_NUM_CODEC_DAIS) && (ch->id !=
|
||||
BTFM_SLIM_NUM_CODEC_DAIS) && (ch->id != id);
|
||||
ch++, i++)
|
||||
;
|
||||
|
||||
if (ch->id == BTFM_SLIM_NUM_CODEC_DAIS ||
|
||||
i == BTFM_SLIM_NUM_CODEC_DAIS) {
|
||||
BTFMSLIM_ERR(
|
||||
"No channel has been allocated for dai (%d)",
|
||||
id);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!slot)
|
||||
return -EINVAL;
|
||||
*(slot + j) = ch->ch;
|
||||
BTFMSLIM_DBG("id:%d, port:%d, ch:%d, slot: %d", ch->id,
|
||||
ch->port, ch->ch, *(slot + j));
|
||||
|
||||
/* In case it has mulitiple channels */
|
||||
if (++j < num)
|
||||
ch++;
|
||||
} while (j < num);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int btfm_slim_dai_get_configs(void *dai, void *config, uint8_t id)
|
||||
{
|
||||
struct hwep_data *hwep_info = (struct hwep_data *)dai;
|
||||
struct btfmslim *btfmslim = dev_get_drvdata(hwep_info->dev);
|
||||
struct master_hwep_configurations *hwep_config;
|
||||
struct btfmslim_ch *ch = NULL;
|
||||
int i = 0;
|
||||
|
||||
BTFMSLIM_DBG("");
|
||||
|
||||
hwep_config = (struct master_hwep_configurations *) config;
|
||||
hwep_config->stream_id = id;
|
||||
hwep_config->device_id = btfmslim->device_id;
|
||||
hwep_config->sample_rate = btfmslim->sample_rate;
|
||||
hwep_config->bit_width = (uint8_t)btfmslim->bps;
|
||||
hwep_config->codectype = usecase_codec;
|
||||
hwep_config->direction = btfmslim->direction;
|
||||
|
||||
switch (id) {
|
||||
case BTFM_FM_SLIM_TX:
|
||||
case BTFM_BT_SCO_SLIM_TX:
|
||||
ch = btfmslim->tx_chs;
|
||||
break;
|
||||
case BTFM_BT_SCO_A2DP_SLIM_RX:
|
||||
case BTFM_BT_SPLIT_A2DP_SLIM_RX:
|
||||
ch = btfmslim->rx_chs;
|
||||
break;
|
||||
}
|
||||
|
||||
for (; i < id ; i++) {
|
||||
if (ch[i].id == id) {
|
||||
BTFMSLIM_DBG("id matched");
|
||||
hwep_config->num_channels = 1;
|
||||
hwep_config->chan_num = ch[i].ch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct hwep_dai_ops btfmslim_hw_dai_ops = {
|
||||
.hwep_startup = btfm_slim_dai_startup,
|
||||
.hwep_shutdown = btfm_slim_dai_shutdown,
|
||||
.hwep_hw_params = btfm_slim_dai_hw_params,
|
||||
.hwep_prepare = btfm_slim_dai_prepare,
|
||||
.hwep_set_channel_map = btfm_slim_dai_set_channel_map,
|
||||
.hwep_get_channel_map = btfm_slim_dai_get_channel_map,
|
||||
.hwep_get_configs = btfm_slim_dai_get_configs,
|
||||
.hwep_codectype = &usecase_codec,
|
||||
};
|
||||
|
||||
static struct hwep_dai_driver btfmslim_dai_driver[] = {
|
||||
{ /* Bluetooth SCO voice uplink: bt -> lpass */
|
||||
.dai_name = "btaudio_tx",
|
||||
.id = BTAUDIO_TX,
|
||||
.capture = {
|
||||
.stream_name = "BT Audio Slim Tx Capture",
|
||||
/* 8 KHz or 16 KHz */
|
||||
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000
|
||||
| SNDRV_PCM_RATE_8000_192000
|
||||
| SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000
|
||||
| SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000
|
||||
| SNDRV_PCM_RATE_192000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */
|
||||
.rate_max = 192000,
|
||||
.rate_min = 8000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
},
|
||||
.dai_ops = &btfmslim_hw_dai_ops,
|
||||
},
|
||||
{ /* Bluetooth SCO voice downlink: lpass -> bt or A2DP Playback */
|
||||
.dai_name = "btaudio_rx",
|
||||
.id = BTAUDIO_RX,
|
||||
.playback = {
|
||||
.stream_name = "BT Audio Slim Rx Playback",
|
||||
/* 8/16/44.1/48/88.2/96 Khz */
|
||||
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000
|
||||
| SNDRV_PCM_RATE_8000_192000
|
||||
| SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000
|
||||
| SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000
|
||||
| SNDRV_PCM_RATE_192000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */
|
||||
.rate_max = 192000,
|
||||
.rate_min = 8000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
},
|
||||
.dai_ops = &btfmslim_hw_dai_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static struct hwep_comp_drv btfmslim_hw_driver = {
|
||||
.hwep_probe = btfm_slim_hwep_probe,
|
||||
.hwep_remove = btfm_slim_hwep_remove,
|
||||
.hwep_read = btfm_slim_hwep_read,
|
||||
.hwep_write = btfm_slim_hwep_write,
|
||||
};
|
||||
|
||||
int btfm_slim_register_hw_ep(struct btfmslim *btfm_slim)
|
||||
{
|
||||
struct device *dev = btfm_slim->dev;
|
||||
struct hwep_data *hwep_info;
|
||||
int ret = 0;
|
||||
|
||||
BTFMSLIM_INFO("Registering with BTFMCODEC HWEP interface\n");
|
||||
hwep_info = kzalloc(sizeof(struct hwep_data), GFP_KERNEL);
|
||||
|
||||
if (!hwep_info) {
|
||||
BTFMSLIM_ERR("%s: failed to allocate memory\n", __func__);
|
||||
ret = -ENOMEM;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Copy EP device parameters as intercations will be on the same device */
|
||||
hwep_info->dev = dev;
|
||||
strlcpy(hwep_info->driver_name, BTFMSLIM_DEV_NAME, DEVICE_NAME_MAX_LEN);
|
||||
hwep_info->drv = &btfmslim_hw_driver;
|
||||
hwep_info->dai_drv = btfmslim_dai_driver;
|
||||
hwep_info->num_dai = ARRAY_SIZE(btfmslim_dai_driver);
|
||||
hwep_info->num_dai = 2;
|
||||
hwep_info->num_mixer_ctrl = ARRAY_SIZE(status_controls);
|
||||
hwep_info->mixer_ctrl = status_controls;
|
||||
/* Register to hardware endpoint */
|
||||
ret = btfmcodec_register_hw_ep(hwep_info);
|
||||
if (ret) {
|
||||
BTFMSLIM_ERR("failed to register with btfmcodec driver hw interface (%d)", ret);
|
||||
goto end;
|
||||
}
|
||||
|
||||
BTFMSLIM_INFO("Registered succesfull with BTFMCODEC HWEP interface\n");
|
||||
return ret;
|
||||
end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void btfm_slim_unregister_hwep(void)
|
||||
{
|
||||
BTFMSLIM_INFO("Unregistered with BTFMCODEC HWEP interface");
|
||||
/* Unregister with BTFMCODEC HWEP driver */
|
||||
btfmcodec_unregister_hw_ep(BTFMSLIM_DEV_NAME);
|
||||
|
||||
}
|
||||
|
||||
MODULE_DESCRIPTION("BTFM Slimbus driver");
|
||||
MODULE_LICENSE("GPL v2");
|
43
qcom/opensource/bt-kernel/slimbus/btfm_slim_hw_interface.h
Normal file
43
qcom/opensource/bt-kernel/slimbus/btfm_slim_hw_interface.h
Normal file
@ -0,0 +1,43 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_BTFM_SLIM_HW_INTERFACE_H
|
||||
#define __LINUX_BTFM_SLIM_HW_INTERFACE_H
|
||||
|
||||
// Todo protect with flags
|
||||
int btfm_slim_register_hw_ep(struct btfmslim *btfm_slim);
|
||||
void btfm_slim_unregister_hwep(void);
|
||||
|
||||
/* Codec driver defines */
|
||||
enum {
|
||||
BTAUDIO_TX = 1,
|
||||
BTAUDIO_RX = 2,
|
||||
BTAUDIO_NUM_CODEC_DAIS
|
||||
};
|
||||
|
||||
typedef enum Codec {
|
||||
SBC = 0,
|
||||
AAC,
|
||||
LDAC,
|
||||
APTX,
|
||||
APTX_HD,
|
||||
APTX_AD,
|
||||
LC3,
|
||||
APTX_AD_SPEECH,
|
||||
LC3_VOICE,
|
||||
APTX_AD_QLEA,
|
||||
APTX_AD_R4,
|
||||
NO_CODEC
|
||||
} codectype;
|
||||
|
||||
static char const *codec_text[] = {"CODEC_TYPE_SBC", "CODEC_TYPE_AAC",
|
||||
"CODEC_TYPE_LDAC", "CODEC_TYPE_APTX",
|
||||
"CODEC_TYPE_APTX_HD", "CODEC_TYPE_APTX_AD",
|
||||
"CODEC_TYPE_LC3", "CODEC_TYPE_APTX_AD_SPEECH",
|
||||
"CODEC_TYPE_LC3_VOICE", "CODEC_TYPE_APTX_AD_QLEA",
|
||||
"CODEC_TYPE_APTX_AD_R4","CODEC_TYPE_INVALID"};
|
||||
|
||||
static SOC_ENUM_SINGLE_EXT_DECL(codec_display, codec_text);
|
||||
#endif /*__LINUX_BTFM_SLIM_HW_INTERFACE_H*/
|
187
qcom/opensource/bt-kernel/slimbus/btfm_slim_slave.c
Normal file
187
qcom/opensource/bt-kernel/slimbus/btfm_slim_slave.c
Normal file
@ -0,0 +1,187 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/slimbus.h>
|
||||
#include "btfm_slim.h"
|
||||
#include "btfm_slim_slave.h"
|
||||
|
||||
/* SLAVE (WCN3990/QCA6390) Port assignment */
|
||||
struct btfmslim_ch slave_rxport[] = {
|
||||
{.id = BTFM_BT_SCO_A2DP_SLIM_RX, .name = "SCO_A2P_Rx",
|
||||
.port = SLAVE_SB_PGD_PORT_RX_SCO},
|
||||
{.id = BTFM_BT_SPLIT_A2DP_SLIM_RX, .name = "A2P_Rx",
|
||||
.port = SLAVE_SB_PGD_PORT_RX_A2P},
|
||||
{.id = BTFM_SLIM_NUM_CODEC_DAIS, .name = "",
|
||||
.port = BTFM_SLIM_PGD_PORT_LAST},
|
||||
};
|
||||
|
||||
struct btfmslim_ch slave_txport[] = {
|
||||
{.id = BTFM_BT_SCO_SLIM_TX, .name = "SCO_Tx",
|
||||
.port = SLAVE_SB_PGD_PORT_TX_SCO},
|
||||
{.id = BTFM_FM_SLIM_TX, .name = "FM_Tx1",
|
||||
.port = SLAVE_SB_PGD_PORT_TX1_FM},
|
||||
{.id = BTFM_FM_SLIM_TX, .name = "FM_Tx2",
|
||||
.port = SLAVE_SB_PGD_PORT_TX2_FM},
|
||||
{.id = BTFM_SLIM_NUM_CODEC_DAIS, .name = "",
|
||||
.port = BTFM_SLIM_PGD_PORT_LAST},
|
||||
};
|
||||
|
||||
/* Function description */
|
||||
int btfm_slim_slave_hw_init(struct btfmslim *btfmslim)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t reg;
|
||||
|
||||
BTFMSLIM_DBG("");
|
||||
|
||||
if (!btfmslim)
|
||||
return -EINVAL;
|
||||
|
||||
/* Get SB_SLAVE_HW_REV_MSB value*/
|
||||
reg = SLAVE_SB_SLAVE_HW_REV_MSB;
|
||||
ret = btfm_slim_read(btfmslim, reg, IFD);
|
||||
if (ret < 0)
|
||||
BTFMSLIM_ERR("failed to read (%d) reg 0x%x", ret, reg);
|
||||
|
||||
BTFMSLIM_DBG("Major Rev: 0x%x, Minor Rev: 0x%x",
|
||||
(ret & 0xF0) >> 4, (ret & 0x0F));
|
||||
|
||||
/* Get SB_SLAVE_HW_REV_LSB value*/
|
||||
reg = SLAVE_SB_SLAVE_HW_REV_LSB;
|
||||
ret = btfm_slim_read(btfmslim, reg, IFD);
|
||||
if (ret < 0)
|
||||
BTFMSLIM_ERR("failed to read (%d) reg 0x%x", ret, reg);
|
||||
else {
|
||||
BTFMSLIM_INFO("read (%d) reg 0x%x", ret, reg);
|
||||
ret = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int is_fm_port(uint8_t port_num)
|
||||
{
|
||||
if (port_num == SLAVE_SB_PGD_PORT_TX1_FM ||
|
||||
port_num == CHRKVER3_SB_PGD_PORT_TX1_FM ||
|
||||
port_num == CHRKVER3_SB_PGD_PORT_TX2_FM ||
|
||||
port_num == SLAVE_SB_PGD_PORT_TX2_FM)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int btfm_slim_slave_enable_port(struct btfmslim *btfmslim, uint8_t port_num,
|
||||
uint8_t rxport, uint8_t enable)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t reg_val = 0, en;
|
||||
uint8_t rxport_num = 0;
|
||||
uint16_t reg;
|
||||
|
||||
BTFMSLIM_DBG("port(%d) enable(%d)", port_num, enable);
|
||||
if (rxport) {
|
||||
BTFMSLIM_DBG("sample rate is %d", btfmslim->sample_rate);
|
||||
if (enable &&
|
||||
btfmslim->sample_rate != 44100 &&
|
||||
btfmslim->sample_rate != 88200) {
|
||||
BTFMSLIM_DBG("setting multichannel bit");
|
||||
/* For SCO Rx, A2DP Rx other than 44.1 and 88.2Khz */
|
||||
if (port_num < 24) {
|
||||
rxport_num = port_num - 16;
|
||||
reg_val = 0x01 << rxport_num;
|
||||
reg = SLAVE_SB_PGD_RX_PORTn_MULTI_CHNL_0(
|
||||
rxport_num);
|
||||
} else {
|
||||
rxport_num = port_num - 24;
|
||||
reg_val = 0x01 << rxport_num;
|
||||
reg = SLAVE_SB_PGD_RX_PORTn_MULTI_CHNL_1(
|
||||
rxport_num);
|
||||
}
|
||||
|
||||
BTFMSLIM_DBG("writing reg_val (%d) to reg(%x)",
|
||||
reg_val, reg);
|
||||
ret = btfm_slim_write(btfmslim, reg, reg_val, IFD);
|
||||
if (ret < 0) {
|
||||
BTFMSLIM_ERR("failed to write (%d) reg 0x%x",
|
||||
ret, reg);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
/* Port enable */
|
||||
reg = SLAVE_SB_PGD_PORT_RX_CFGN(port_num - 0x10);
|
||||
goto enable_disable_rxport;
|
||||
}
|
||||
if (!enable)
|
||||
goto enable_disable_txport;
|
||||
|
||||
/* txport */
|
||||
/* Multiple Channel Setting */
|
||||
if (is_fm_port(port_num)) {
|
||||
if (port_num == CHRKVER3_SB_PGD_PORT_TX1_FM)
|
||||
reg_val = (0x1 << CHRKVER3_SB_PGD_PORT_TX1_FM);
|
||||
else if (port_num == CHRKVER3_SB_PGD_PORT_TX2_FM)
|
||||
reg_val = (0x1 << CHRKVER3_SB_PGD_PORT_TX2_FM);
|
||||
else
|
||||
reg_val = (0x1 << SLAVE_SB_PGD_PORT_TX1_FM) |
|
||||
(0x1 << SLAVE_SB_PGD_PORT_TX2_FM);
|
||||
reg = SLAVE_SB_PGD_TX_PORTn_MULTI_CHNL_0(port_num);
|
||||
BTFMSLIM_INFO("writing reg_val (%d) to reg(%x)", reg_val, reg);
|
||||
ret = btfm_slim_write(btfmslim, reg, reg_val, IFD);
|
||||
if (ret < 0) {
|
||||
BTFMSLIM_ERR("failed to write (%d) reg 0x%x", ret, reg);
|
||||
goto error;
|
||||
}
|
||||
} else if (port_num == SLAVE_SB_PGD_PORT_TX_SCO) {
|
||||
/* SCO Tx */
|
||||
reg_val = 0x1 << SLAVE_SB_PGD_PORT_TX_SCO;
|
||||
reg = SLAVE_SB_PGD_TX_PORTn_MULTI_CHNL_0(port_num);
|
||||
BTFMSLIM_DBG("writing reg_val (%d) to reg(%x)",
|
||||
reg_val, reg);
|
||||
ret = btfm_slim_write(btfmslim, reg, reg_val, IFD);
|
||||
if (ret < 0) {
|
||||
BTFMSLIM_ERR("failed to write (%d) reg 0x%x",
|
||||
ret, reg);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable Tx port hw auto recovery for underrun or overrun error */
|
||||
reg_val = (SLAVE_ENABLE_OVERRUN_AUTO_RECOVERY |
|
||||
SLAVE_ENABLE_UNDERRUN_AUTO_RECOVERY);
|
||||
reg = SLAVE_SB_PGD_PORT_TX_OR_UR_CFGN(port_num);
|
||||
ret = btfm_slim_write(btfmslim, reg, reg_val, IFD);
|
||||
if (ret < 0) {
|
||||
BTFMSLIM_ERR("failed to write (%d) reg 0x%x", ret, reg);
|
||||
goto error;
|
||||
}
|
||||
|
||||
enable_disable_txport:
|
||||
/* Port enable */
|
||||
reg = SLAVE_SB_PGD_PORT_TX_CFGN(port_num);
|
||||
|
||||
enable_disable_rxport:
|
||||
if (enable)
|
||||
en = SLAVE_SB_PGD_PORT_ENABLE;
|
||||
else
|
||||
en = SLAVE_SB_PGD_PORT_DISABLE;
|
||||
|
||||
if (is_fm_port(port_num))
|
||||
reg_val = en | SLAVE_SB_PGD_PORT_WM_L8;
|
||||
else if (port_num == SLAVE_SB_PGD_PORT_TX_SCO)
|
||||
reg_val = enable ? en | SLAVE_SB_PGD_PORT_WM_L1 : en;
|
||||
else
|
||||
reg_val = enable ? en | SLAVE_SB_PGD_PORT_WM_LB : en;
|
||||
|
||||
if (enable && port_num == SLAVE_SB_PGD_PORT_TX_SCO)
|
||||
BTFMSLIM_INFO("programming SCO Tx with reg_val %d to reg 0x%x",
|
||||
reg_val, reg);
|
||||
|
||||
ret = btfm_slim_write(btfmslim, reg, reg_val, IFD);
|
||||
if (ret < 0)
|
||||
BTFMSLIM_ERR("failed to write (%d) reg 0x%x", ret, reg);
|
||||
|
||||
error:
|
||||
return ret;
|
||||
}
|
187
qcom/opensource/bt-kernel/slimbus/btfm_slim_slave.h
Normal file
187
qcom/opensource/bt-kernel/slimbus/btfm_slim_slave.h
Normal file
@ -0,0 +1,187 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef BTFM_SLIM_SLAVE_H
|
||||
#define BTFM_SLIM_SLAVE_H
|
||||
#include "btfm_slim.h"
|
||||
|
||||
/* Registers Address */
|
||||
#define SLAVE_SB_COMP_TEST 0x00000000
|
||||
#define SLAVE_SB_SLAVE_HW_REV_MSB 0x00000001
|
||||
#define SLAVE_SB_SLAVE_HW_REV_LSB 0x00000002
|
||||
#define SLAVE_SB_DEBUG_FEATURES 0x00000005
|
||||
#define SLAVE_SB_INTF_INT_EN 0x00000010
|
||||
#define SLAVE_SB_INTF_INT_STATUS 0x00000011
|
||||
#define SLAVE_SB_INTF_INT_CLR 0x00000012
|
||||
#define SLAVE_SB_FRM_CFG 0x00000013
|
||||
#define SLAVE_SB_FRM_STATUS 0x00000014
|
||||
#define SLAVE_SB_FRM_INT_EN 0x00000015
|
||||
#define SLAVE_SB_FRM_INT_STATUS 0x00000016
|
||||
#define SLAVE_SB_FRM_INT_CLR 0x00000017
|
||||
#define SLAVE_SB_FRM_WAKEUP 0x00000018
|
||||
#define SLAVE_SB_FRM_CLKCTL_DONE 0x00000019
|
||||
#define SLAVE_SB_FRM_IE_STATUS 0x0000001A
|
||||
#define SLAVE_SB_FRM_VE_STATUS 0x0000001B
|
||||
#define SLAVE_SB_PGD_TX_CFG_STATUS 0x00000020
|
||||
#define SLAVE_SB_PGD_RX_CFG_STATUS 0x00000021
|
||||
#define SLAVE_SB_PGD_DEV_INT_EN 0x00000022
|
||||
#define SLAVE_SB_PGD_DEV_INT_STATUS 0x00000023
|
||||
#define SLAVE_SB_PGD_DEV_INT_CLR 0x00000024
|
||||
#define SLAVE_SB_PGD_PORT_INT_EN_RX_0 0x00000030
|
||||
#define SLAVE_SB_PGD_PORT_INT_EN_RX_1 0x00000031
|
||||
#define SLAVE_SB_PGD_PORT_INT_EN_TX_0 0x00000032
|
||||
#define SLAVE_SB_PGD_PORT_INT_EN_TX_1 0x00000033
|
||||
#define SLAVE_SB_PGD_PORT_INT_STATUS_RX_0 0x00000034
|
||||
#define SLAVE_SB_PGD_PORT_INT_STATUS_RX_1 0x00000035
|
||||
#define SLAVE_SB_PGD_PORT_INT_STATUS_TX_0 0x00000036
|
||||
#define SLAVE_SB_PGD_PORT_INT_STATUS_TX_1 0x00000037
|
||||
#define SLAVE_SB_PGD_PORT_INT_CLR_RX_0 0x00000038
|
||||
#define SLAVE_SB_PGD_PORT_INT_CLR_RX_1 0x00000039
|
||||
#define SLAVE_SB_PGD_PORT_INT_CLR_TX_0 0x0000003A
|
||||
#define SLAVE_SB_PGD_PORT_INT_CLR_TX_1 0x0000003B
|
||||
#define SLAVE_SB_PGD_PORT_RX_CFGN(n) (0x00000040 + n)
|
||||
#define SLAVE_SB_PGD_PORT_TX_CFGN(n) (0x00000050 + n)
|
||||
#define SLAVE_SB_PGD_PORT_INT_RX_SOURCEN(n) (0x00000060 + n)
|
||||
#define SLAVE_SB_PGD_PORT_INT_TX_SOURCEN(n) (0x00000070 + n)
|
||||
#define SLAVE_SB_PGD_PORT_RX_STATUSN(n) (0x00000080 + n)
|
||||
#define SLAVE_SB_PGD_PORT_TX_STATUSN(n) (0x00000090 + n)
|
||||
#define SLAVE_SB_PGD_TX_PORTn_MULTI_CHNL_0(n) (0x00000100 + 0x4*n)
|
||||
#define SLAVE_SB_PGD_TX_PORTn_MULTI_CHNL_1(n) (0x00000101 + 0x4*n)
|
||||
#define SLAVE_SB_PGD_RX_PORTn_MULTI_CHNL_0(n) (0x00000180 + 0x4*n)
|
||||
#define SLAVE_SB_PGD_RX_PORTn_MULTI_CHNL_1(n) (0x00000181 + 0x4*n)
|
||||
#define SLAVE_SB_PGD_PORT_TX_OR_UR_CFGN(n) (0x000001F0 + n)
|
||||
|
||||
/* Register Bit Setting */
|
||||
#define SLAVE_ENABLE_OVERRUN_AUTO_RECOVERY (0x1 << 1)
|
||||
#define SLAVE_ENABLE_UNDERRUN_AUTO_RECOVERY (0x1 << 0)
|
||||
#define SLAVE_SB_PGD_PORT_ENABLE (0x1 << 0)
|
||||
#define SLAVE_SB_PGD_PORT_DISABLE (0x0 << 0)
|
||||
#define SLAVE_SB_PGD_PORT_WM_L1 (0x1 << 1)
|
||||
#define SLAVE_SB_PGD_PORT_WM_L2 (0x2 << 1)
|
||||
#define SLAVE_SB_PGD_PORT_WM_L3 (0x3 << 1)
|
||||
#define SLAVE_SB_PGD_PORT_WM_L8 (0x8 << 1)
|
||||
#define SLAVE_SB_PGD_PORT_WM_LB (0xB << 1)
|
||||
|
||||
#define SLAVE_SB_PGD_PORT_RX_NUM 16
|
||||
#define SLAVE_SB_PGD_PORT_TX_NUM 16
|
||||
|
||||
/* PGD Port Map */
|
||||
#define SLAVE_SB_PGD_PORT_TX_SCO 0
|
||||
#define SLAVE_SB_PGD_PORT_TX1_FM 1
|
||||
#define SLAVE_SB_PGD_PORT_TX2_FM 2
|
||||
#define CHRKVER3_SB_PGD_PORT_TX1_FM 5
|
||||
#define CHRKVER3_SB_PGD_PORT_TX2_FM 4
|
||||
#define SLAVE_SB_PGD_PORT_RX_SCO 16
|
||||
#define SLAVE_SB_PGD_PORT_RX_A2P 17
|
||||
|
||||
enum {
|
||||
QCA_CHEROKEE_SOC_ID_0200 = 0x40010200,
|
||||
QCA_CHEROKEE_SOC_ID_0201 = 0x40010201,
|
||||
QCA_CHEROKEE_SOC_ID_0210 = 0x40010214,
|
||||
QCA_CHEROKEE_SOC_ID_0211 = 0x40010224,
|
||||
QCA_CHEROKEE_SOC_ID_0310 = 0x40010310,
|
||||
QCA_CHEROKEE_SOC_ID_0320 = 0x40010320,
|
||||
QCA_CHEROKEE_SOC_ID_0320_UMC = 0x40014320,
|
||||
};
|
||||
|
||||
enum {
|
||||
QCA_APACHE_SOC_ID_0100 = 0x40020120,
|
||||
QCA_APACHE_SOC_ID_0110 = 0x40020130,
|
||||
QCA_APACHE_SOC_ID_0120 = 0x40020140,
|
||||
QCA_APACHE_SOC_ID_0121 = 0x40020150,
|
||||
};
|
||||
|
||||
enum {
|
||||
QCA_COMANCHE_SOC_ID_0101 = 0x40070101,
|
||||
QCA_COMANCHE_SOC_ID_0110 = 0x40070110,
|
||||
QCA_COMANCHE_SOC_ID_0120 = 0x40070120,
|
||||
QCA_COMANCHE_SOC_ID_0130 = 0x40070130,
|
||||
QCA_COMANCHE_SOC_ID_4130 = 0x40074130,
|
||||
QCA_COMANCHE_SOC_ID_5120 = 0x40075120,
|
||||
QCA_COMANCHE_SOC_ID_5130 = 0x40075130,
|
||||
};
|
||||
|
||||
enum {
|
||||
QCA_HASTINGS_SOC_ID_0200 = 0x400A0200,
|
||||
};
|
||||
|
||||
enum {
|
||||
QCA_HSP_SOC_ID_0100 = 0x400C0100,
|
||||
QCA_HSP_SOC_ID_0110 = 0x400C0110,
|
||||
QCA_HSP_SOC_ID_0200 = 0x400C0200,
|
||||
QCA_HSP_SOC_ID_0210 = 0x400C0210,
|
||||
QCA_HSP_SOC_ID_1201 = 0x400C1201,
|
||||
QCA_HSP_SOC_ID_1211 = 0x400C1211,
|
||||
};
|
||||
|
||||
enum {
|
||||
QCA_MOSELLE_SOC_ID_0100 = 0x40140100,
|
||||
QCA_MOSELLE_SOC_ID_0110 = 0x40140110,
|
||||
QCA_MOSELLE_SOC_ID_0120 = 0x40140120,
|
||||
};
|
||||
|
||||
enum {
|
||||
QCA_HAMILTON_SOC_ID_0100 = 0x40170100,
|
||||
QCA_HAMILTON_SOC_ID_0101 = 0x40170101,
|
||||
QCA_HAMILTON_SOC_ID_0200 = 0x40170200,
|
||||
};
|
||||
|
||||
/* Function Prototype */
|
||||
|
||||
/*
|
||||
* btfm_slim_slave_hw_init: Initialize slave specific slimbus slave device
|
||||
* @btfmslim: slimbus slave device data pointer.
|
||||
* Returns:
|
||||
* 0: Success
|
||||
* else: Fail
|
||||
*/
|
||||
int btfm_slim_slave_hw_init(struct btfmslim *btfmslim);
|
||||
|
||||
/*
|
||||
* btfm_slim_slave_enable_rxport: Enable slave Rx port by given port number
|
||||
* @btfmslim: slimbus slave device data pointer.
|
||||
* @portNum: slimbus slave port number to enable
|
||||
* @rxport: rxport or txport
|
||||
* @enable: enable port or disable port
|
||||
* Returns:
|
||||
* 0: Success
|
||||
* else: Fail
|
||||
*/
|
||||
int btfm_slim_slave_enable_port(struct btfmslim *btfmslim, uint8_t portNum,
|
||||
uint8_t rxport, uint8_t enable);
|
||||
|
||||
/* Specific defines for slave slimbus device */
|
||||
#define SLAVE_SLIM_REG_OFFSET 0x0800
|
||||
|
||||
#ifdef SLIM_SLAVE_REG_OFFSET
|
||||
#undef SLIM_SLAVE_REG_OFFSET
|
||||
#define SLIM_SLAVE_REG_OFFSET SLAVE_SLIM_REG_OFFSET
|
||||
#endif
|
||||
|
||||
/* Assign vendor specific function */
|
||||
extern struct btfmslim_ch slave_txport[];
|
||||
extern struct btfmslim_ch slave_rxport[];
|
||||
|
||||
#ifdef SLIM_SLAVE_RXPORT
|
||||
#undef SLIM_SLAVE_RXPORT
|
||||
#define SLIM_SLAVE_RXPORT (&slave_rxport[0])
|
||||
#endif
|
||||
|
||||
#ifdef SLIM_SLAVE_TXPORT
|
||||
#undef SLIM_SLAVE_TXPORT
|
||||
#define SLIM_SLAVE_TXPORT (&slave_txport[0])
|
||||
#endif
|
||||
|
||||
#ifdef SLIM_SLAVE_INIT
|
||||
#undef SLIM_SLAVE_INIT
|
||||
#define SLIM_SLAVE_INIT btfm_slim_slave_hw_init
|
||||
#endif
|
||||
|
||||
#ifdef SLIM_SLAVE_PORT_EN
|
||||
#undef SLIM_SLAVE_PORT_EN
|
||||
#define SLIM_SLAVE_PORT_EN btfm_slim_slave_enable_port
|
||||
#endif
|
||||
#endif
|
12
qcom/opensource/bt-kernel/soundwire/Kconfig
Normal file
12
qcom/opensource/bt-kernel/soundwire/Kconfig
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
config BTFM_SWR
|
||||
tristate "MSM Bluetooth/FM SoundWire Device"
|
||||
depends on MSM_BT_POWER
|
||||
help
|
||||
This enables BT/FM soundwire driver to open/close ports.
|
||||
This will make use of soundwire bus driver and soundwire
|
||||
master driver to communicate with soundwire slave.
|
||||
|
||||
Say Y here to compile support for Bluetooth/FM soundwire slave driver
|
||||
into the kernel or say M to compile as a module.
|
||||
|
3
qcom/opensource/bt-kernel/soundwire/Makefile
Normal file
3
qcom/opensource/bt-kernel/soundwire/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
ccflags-y += -I$(BT_ROOT)/include
|
||||
bt_fm_swr-objs := btfm_swr.o btfm_swr_hw_interface.o btfm_swr_slave.o
|
||||
obj-$(CONFIG_BTFM_SWR) += bt_fm_swr.o
|
288
qcom/opensource/bt-kernel/soundwire/btfm_swr.c
Normal file
288
qcom/opensource/bt-kernel/soundwire/btfm_swr.c
Normal file
@ -0,0 +1,288 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/tlv.h>
|
||||
#include "btpower.h"
|
||||
#include "btfm_swr.h"
|
||||
#include "btfm_swr_hw_interface.h"
|
||||
#include "btfm_swr_slave.h"
|
||||
|
||||
struct class *btfm_swr_class;
|
||||
static int btfm_swr_major;
|
||||
struct btfmswr *pbtfmswr;
|
||||
static int btfm_num_ports_open;
|
||||
|
||||
#define BT_CMD_SWR_TEST 0xbfac
|
||||
|
||||
static int btfm_swr_probe(struct swr_device *pdev);
|
||||
|
||||
int btfm_get_bt_soc_index(int chipset_ver)
|
||||
{
|
||||
switch (chipset_ver) {
|
||||
case QCA_GANGES_SOC_ID_0100:
|
||||
case QCA_GANGES_SOC_ID_0200:
|
||||
return GANGES;
|
||||
case QCA_EVROS_SOC_ID_0100:
|
||||
case QCA_EVROS_SOC_ID_0200:
|
||||
return EVROS;
|
||||
default:
|
||||
BTFMSWR_ERR("no BT SOC id defined, returning EVROS");
|
||||
return EVROS;
|
||||
}
|
||||
}
|
||||
|
||||
int btfm_swr_hw_init(void)
|
||||
{
|
||||
uint8_t dev_num = 0;
|
||||
int ret = 0;
|
||||
int chipset_ver;
|
||||
|
||||
BTFMSWR_DBG("");
|
||||
|
||||
if (pbtfmswr->initialized)
|
||||
BTFMSWR_INFO("Already initialized");
|
||||
|
||||
// get BT chipset version
|
||||
chipset_ver = btpower_get_chipset_version();
|
||||
|
||||
// get BT/FM SOC slave port details
|
||||
pbtfmswr->soc_index = btfm_get_bt_soc_index(chipset_ver);
|
||||
|
||||
BTFMSWR_INFO("chipset soc version:%x, soc index: %x", chipset_ver,
|
||||
pbtfmswr->soc_index);
|
||||
|
||||
pbtfmswr->p_dai_port = &slave_port[pbtfmswr->soc_index];
|
||||
|
||||
// get logical address
|
||||
/*
|
||||
* Add 5msec delay to provide sufficient time for
|
||||
* soundwire auto enumeration of slave devices as
|
||||
* per HW requirement.
|
||||
*/
|
||||
usleep_range(5000, 5010);
|
||||
ret = swr_get_logical_dev_num(pbtfmswr->swr_slave, pbtfmswr->p_dai_port->ea,
|
||||
&dev_num);
|
||||
if (ret) {
|
||||
BTFMSWR_ERR("error while getting logical device number");
|
||||
goto err;
|
||||
}
|
||||
|
||||
pbtfmswr->swr_slave->dev_num = dev_num;
|
||||
pbtfmswr->initialized = true;
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int btfm_swr_enable_port(u8 port_num, u8 ch_count, u32 sample_rate, u8 usecase)
|
||||
{
|
||||
int ret = 0;
|
||||
u8 port_id[MAX_BT_PORTS];
|
||||
u8 num_ch[MAX_BT_PORTS];
|
||||
u8 ch_mask[MAX_BT_PORTS];
|
||||
u32 ch_rate[MAX_BT_PORTS];
|
||||
u8 port_type[MAX_BT_PORTS];
|
||||
u8 num_port = 1;
|
||||
|
||||
// master expects port num -1 to be sent
|
||||
port_id[0] = port_num-1;
|
||||
num_ch[0] = ch_count;
|
||||
ch_mask[0] = ch_count == 2 ? TWO_CHANNEL_MASK : ONE_CHANNEL_MASK;
|
||||
ch_rate[0] = sample_rate;
|
||||
port_type[0] = usecase;
|
||||
|
||||
BTFMSWR_INFO("enabling port : %d\n", port_num);
|
||||
ret = swr_connect_port(pbtfmswr->swr_slave, &port_id[0], num_port,
|
||||
&ch_mask[0], &ch_rate[0], &num_ch[0],
|
||||
&port_type[0]);
|
||||
|
||||
if (ret < 0) {
|
||||
BTFMSWR_ERR("swr_connect_port failed, error %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
BTFMSWR_INFO("calling swr_slvdev_datapath_control\n");
|
||||
ret = swr_slvdev_datapath_control(pbtfmswr->swr_slave,
|
||||
pbtfmswr->swr_slave->dev_num,
|
||||
true);
|
||||
if (ret < 0)
|
||||
BTFMSWR_ERR("swr_slvdev_datapath_control failed");
|
||||
|
||||
if (ret == 0)
|
||||
btfm_num_ports_open++;
|
||||
|
||||
BTFMSWR_INFO("btfm_num_ports_open: %d", btfm_num_ports_open);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int btfm_swr_disable_port(u8 port_num, u8 ch_count, u8 usecase)
|
||||
{
|
||||
int ret = 0;
|
||||
u8 port_id[MAX_BT_PORTS];
|
||||
u8 ch_mask[MAX_BT_PORTS];
|
||||
u8 port_type[MAX_BT_PORTS];
|
||||
u8 num_port = 1;
|
||||
|
||||
// master expects port num -1 to be sent
|
||||
port_id[0] = port_num-1;
|
||||
ch_mask[0] = ch_count == 2 ? TWO_CHANNEL_MASK : ONE_CHANNEL_MASK;
|
||||
port_type[0] = usecase;
|
||||
|
||||
BTFMSWR_INFO("disabling port : %d\n", port_num);
|
||||
ret = swr_disconnect_port(pbtfmswr->swr_slave, &port_id[0], num_port,
|
||||
&ch_mask[0], &port_type[0]);
|
||||
|
||||
if (ret < 0)
|
||||
BTFMSWR_ERR("swr_disconnect_port port failed, error %d", ret);
|
||||
|
||||
BTFMSWR_INFO("calling swr_slvdev_datapath_control\n");
|
||||
ret = swr_slvdev_datapath_control(pbtfmswr->swr_slave,
|
||||
pbtfmswr->swr_slave->dev_num,
|
||||
false);
|
||||
if (ret < 0)
|
||||
BTFMSWR_ERR("swr_slvdev_datapath_control failed");
|
||||
|
||||
if (btfm_num_ports_open > 0)
|
||||
btfm_num_ports_open--;
|
||||
|
||||
BTFMSWR_INFO("btfm_num_ports_open: %d", btfm_num_ports_open);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long btfm_swr_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
BTFMSWR_INFO("");
|
||||
switch (cmd) {
|
||||
case BT_CMD_SWR_TEST:
|
||||
BTFMSWR_INFO("cmd BT_CMD_SLIM_TEST, call btfm_swr_hw_init");
|
||||
ret = btfm_swr_hw_init();
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations bt_dev_fops = {
|
||||
.unlocked_ioctl = btfm_swr_ioctl,
|
||||
.compat_ioctl = btfm_swr_ioctl,
|
||||
};
|
||||
|
||||
static int btfm_swr_probe(struct swr_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
BTFMSWR_INFO("");
|
||||
|
||||
pbtfmswr = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct btfmswr), GFP_KERNEL);
|
||||
if (!pbtfmswr) {
|
||||
BTFMSWR_ERR("memory allocation to driver failed");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
swr_set_dev_data(pdev, pbtfmswr);
|
||||
pbtfmswr->swr_slave = pdev;
|
||||
pbtfmswr->dev = &pdev->dev;
|
||||
pbtfmswr->initialized = false;
|
||||
|
||||
// register with ALSA
|
||||
ret = btfm_swr_register_hw_ep(pbtfmswr);
|
||||
if (ret) {
|
||||
BTFMSWR_ERR("registration with ALSA failed, returning");
|
||||
goto dealloc;
|
||||
}
|
||||
|
||||
btfm_swr_major = register_chrdev(0, "btfm_swr", &bt_dev_fops);
|
||||
if (btfm_swr_major < 0) {
|
||||
BTFMSWR_ERR("%s: failed to allocate char dev\n", __func__);
|
||||
ret = -1;
|
||||
goto register_err;
|
||||
}
|
||||
|
||||
btfm_swr_class = class_create(THIS_MODULE, "btfmswr-dev");
|
||||
if (IS_ERR(btfm_swr_class)) {
|
||||
BTFMSWR_ERR("%s: coudn't create class\n", __func__);
|
||||
ret = -1;
|
||||
goto class_err;
|
||||
}
|
||||
|
||||
if (device_create(btfm_swr_class, NULL, MKDEV(btfm_swr_major, 0),
|
||||
NULL, "btfmswr") == NULL) {
|
||||
BTFMSWR_ERR("%s: failed to allocate char dev\n", __func__);
|
||||
ret = -1;
|
||||
goto device_err;
|
||||
}
|
||||
return ret;
|
||||
|
||||
device_err:
|
||||
class_destroy(btfm_swr_class);
|
||||
class_err:
|
||||
unregister_chrdev(btfm_swr_major, "btfm_swr");
|
||||
|
||||
register_err:
|
||||
btfm_swr_unregister_hwep();
|
||||
dealloc:
|
||||
kfree(pbtfmswr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct swr_device_id btfm_swr_id[] = {
|
||||
{SWR_SLAVE_COMPATIBLE_STR, 0},
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct of_device_id btfm_swr_dt_match[] = {
|
||||
{
|
||||
.compatible = "qcom,btfmswr_slave",
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct swr_driver btfm_swr_driver = {
|
||||
.driver = {
|
||||
.name = "btfmswr-driver",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = btfm_swr_dt_match,
|
||||
},
|
||||
.probe = btfm_swr_probe,
|
||||
.id_table = btfm_swr_id,
|
||||
};
|
||||
|
||||
static int __init btfm_swr_init(void)
|
||||
{
|
||||
BTFMSWR_INFO("");
|
||||
return swr_driver_register(&btfm_swr_driver);
|
||||
}
|
||||
|
||||
static void __exit btfm_swr_exit(void)
|
||||
{
|
||||
BTFMSWR_INFO("");
|
||||
swr_driver_unregister(&btfm_swr_driver);
|
||||
}
|
||||
|
||||
module_init(btfm_swr_init);
|
||||
module_exit(btfm_swr_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("BTFM SoundWire Slave driver");
|
||||
|
86
qcom/opensource/bt-kernel/soundwire/btfm_swr.h
Normal file
86
qcom/opensource/bt-kernel/soundwire/btfm_swr.h
Normal file
@ -0,0 +1,86 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef BTFM_SWR_H
|
||||
#define BTFM_SWR_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <bindings/audio-codec-port-types.h>
|
||||
#include "soc/soundwire.h"
|
||||
|
||||
#define SWR_SLAVE_COMPATIBLE_STR "btfmswr_slave"
|
||||
|
||||
|
||||
#define BTFMSWR_DBG(fmt, arg...) pr_debug("%s: " fmt "\n", __func__, ## arg)
|
||||
#define BTFMSWR_INFO(fmt, arg...) pr_info("%s: " fmt "\n", __func__, ## arg)
|
||||
#define BTFMSWR_ERR(fmt, arg...) pr_err("%s: " fmt "\n", __func__, ## arg)
|
||||
|
||||
extern struct btfmswr *pbtfmswr;
|
||||
|
||||
// assumption is that we use adjacent channels
|
||||
#define ONE_CHANNEL_MASK 1
|
||||
#define TWO_CHANNEL_MASK 3
|
||||
|
||||
#define MAX_BT_PORTS 1
|
||||
|
||||
/* Codec driver defines */
|
||||
enum {
|
||||
FMAUDIO_TX = 0,
|
||||
BTAUDIO_TX,
|
||||
BTAUDIO_RX,
|
||||
BTAUDIO_A2DP_SINK_TX,
|
||||
BTFM_NUM_CODEC_DAIS
|
||||
};
|
||||
|
||||
enum {
|
||||
EVROS = 0,
|
||||
GANGES = 1,
|
||||
MAX_SOC_ID = 0xFF
|
||||
};
|
||||
|
||||
|
||||
struct btfmswr_dai_port_info {
|
||||
int dai_id;
|
||||
char *dai_name;
|
||||
uint8_t port;
|
||||
};
|
||||
|
||||
struct soc_port_mapping {
|
||||
// enumeration address of BT SOC
|
||||
u64 ea;
|
||||
struct btfmswr_dai_port_info port_info[BTFM_NUM_CODEC_DAIS];
|
||||
};
|
||||
|
||||
|
||||
struct btfmswr {
|
||||
struct device *dev;
|
||||
struct swr_device *swr_slave;
|
||||
bool initialized;
|
||||
uint32_t sample_rate;
|
||||
uint32_t bps;
|
||||
uint16_t direction;
|
||||
uint8_t num_channels;
|
||||
int soc_index;
|
||||
struct soc_port_mapping *p_dai_port;
|
||||
};
|
||||
|
||||
/**
|
||||
* btfm_swr_hw_init: Initialize soundwire slave device
|
||||
* Returns:
|
||||
* 0: Success
|
||||
* else: Fail
|
||||
*/
|
||||
int btfm_swr_hw_init(void);
|
||||
|
||||
int btfm_get_bt_soc_index(int chipset_ver);
|
||||
|
||||
int btfm_swr_enable_port(u8 port_num, u8 ch_count, u32 sample_rate,
|
||||
u8 port_type);
|
||||
|
||||
|
||||
int btfm_swr_disable_port(u8 port_num, u8 ch_count, u8 usecase);
|
||||
#endif /* BTFM_SWR_H */
|
442
qcom/opensource/bt-kernel/soundwire/btfm_swr_hw_interface.c
Normal file
442
qcom/opensource/bt-kernel/soundwire/btfm_swr_hw_interface.c
Normal file
@ -0,0 +1,442 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include "btfm_swr.h"
|
||||
#include "btfm_swr_hw_interface.h"
|
||||
#include "btfm_codec_hw_interface.h"
|
||||
|
||||
#define LPAIF_AUD 0x05
|
||||
|
||||
static int bt_soc_enable_status;
|
||||
int btfm_feedback_ch_setting;
|
||||
static uint8_t usecase_codec;
|
||||
|
||||
static int btfm_swr_hwep_write(struct snd_soc_component *codec,
|
||||
unsigned int reg, unsigned int value)
|
||||
{
|
||||
BTFMSWR_DBG("");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int btfm_swr_hwep_read(struct snd_soc_component *codec,
|
||||
unsigned int reg)
|
||||
{
|
||||
BTFMSWR_DBG("");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btfm_soc_status_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
BTFMSWR_DBG("");
|
||||
ucontrol->value.integer.value[0] = bt_soc_enable_status;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int btfm_soc_status_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
BTFMSWR_DBG("");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int btfm_get_feedback_ch_setting(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
BTFMSWR_DBG("");
|
||||
ucontrol->value.integer.value[0] = btfm_feedback_ch_setting;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int btfm_put_feedback_ch_setting(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
BTFMSWR_DBG("");
|
||||
btfm_feedback_ch_setting = ucontrol->value.integer.value[0];
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int btfm_get_codec_type(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
BTFMSWR_DBG("current codec type:%s", codec_text[usecase_codec]);
|
||||
ucontrol->value.integer.value[0] = usecase_codec;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int btfm_put_codec_type(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
usecase_codec = ucontrol->value.integer.value[0];
|
||||
BTFMSWR_DBG("codec type set to:%s", codec_text[usecase_codec]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new status_controls[] = {
|
||||
SOC_SINGLE_EXT("BT SOC status", 0, 0, 1, 0,
|
||||
btfm_soc_status_get, btfm_soc_status_put),
|
||||
SOC_SINGLE_EXT("BT set feedback channel", 0, 0, 1, 0,
|
||||
btfm_get_feedback_ch_setting,
|
||||
btfm_put_feedback_ch_setting),
|
||||
SOC_ENUM_EXT("BT codec type", codec_display,
|
||||
btfm_get_codec_type, btfm_put_codec_type),
|
||||
};
|
||||
|
||||
|
||||
static int btfm_swr_hwep_probe(struct snd_soc_component *codec)
|
||||
{
|
||||
BTFMSWR_DBG("");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void btfm_swr_hwep_remove(struct snd_soc_component *codec)
|
||||
{
|
||||
BTFMSWR_DBG("");
|
||||
}
|
||||
|
||||
static int btfm_swr_dai_startup(void *dai)
|
||||
{
|
||||
//struct hwep_data *hwep_info = (struct hwep_data *)dai;
|
||||
int ret = -1;
|
||||
|
||||
BTFMSWR_DBG("");
|
||||
|
||||
ret = btfm_swr_hw_init();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void btfm_swr_dai_shutdown(void *dai, int id)
|
||||
{
|
||||
struct hwep_data *hwep_info = (struct hwep_data *)dai;
|
||||
struct btfmswr *btfmswr = dev_get_drvdata(hwep_info->dev);
|
||||
int ret = 0;
|
||||
u8 port_type;
|
||||
|
||||
BTFMSWR_INFO("");
|
||||
|
||||
if (btfmswr == NULL || btfmswr->p_dai_port == NULL) {
|
||||
BTFMSWR_INFO("port shutdown might have called with out open\n");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (id) {
|
||||
case FMAUDIO_TX:
|
||||
port_type = FM_AUDIO_TX1;
|
||||
break;
|
||||
case BTAUDIO_TX:
|
||||
port_type = BT_AUDIO_TX1;
|
||||
break;
|
||||
case BTAUDIO_RX:
|
||||
port_type = BT_AUDIO_RX1;
|
||||
break;
|
||||
case BTAUDIO_A2DP_SINK_TX:
|
||||
port_type = BT_AUDIO_TX2;
|
||||
break;
|
||||
case BTFM_NUM_CODEC_DAIS:
|
||||
default:
|
||||
BTFMSWR_ERR("dai->id is invalid:%d", id);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = btfm_swr_disable_port(btfmswr->p_dai_port->port_info[id].port,
|
||||
btfmswr->num_channels, port_type);
|
||||
}
|
||||
|
||||
static int btfm_swr_dai_hw_params(void *dai, uint32_t bps,
|
||||
uint32_t direction, uint8_t num_channels)
|
||||
{
|
||||
struct hwep_data *hwep_info = (struct hwep_data *)dai;
|
||||
struct btfmswr *btfmswr = dev_get_drvdata(hwep_info->dev);
|
||||
|
||||
BTFMSWR_DBG("");
|
||||
btfmswr->bps = bps;
|
||||
btfmswr->direction = direction;
|
||||
btfmswr->num_channels = num_channels;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void btfm_get_sampling_rate(uint32_t *sampling_rate)
|
||||
{
|
||||
uint8_t codec_types_avb = ARRAY_SIZE(codec_text);
|
||||
|
||||
if (usecase_codec > (codec_types_avb - 1)) {
|
||||
BTFMSWR_ERR("falling back to use default sampling_rate: %u",
|
||||
*sampling_rate);
|
||||
return;
|
||||
}
|
||||
|
||||
if (*sampling_rate == 44100 || *sampling_rate == 48000) {
|
||||
if (usecase_codec == LDAC ||
|
||||
usecase_codec == APTX_AD)
|
||||
*sampling_rate = (*sampling_rate) * 2;
|
||||
}
|
||||
|
||||
if (usecase_codec == LC3_VOICE ||
|
||||
usecase_codec == APTX_AD_SPEECH ||
|
||||
usecase_codec == LC3 || usecase_codec == APTX_AD_QLEA ||
|
||||
usecase_codec == APTX_AD_R4) {
|
||||
*sampling_rate = 96000;
|
||||
}
|
||||
|
||||
BTFMSWR_INFO("current usecase codec type %s and sampling rate:%u khz",
|
||||
codec_text[usecase_codec], *sampling_rate);
|
||||
}
|
||||
|
||||
static int btfm_swr_dai_prepare(void *dai, uint32_t sampling_rate, uint32_t direction, int id)
|
||||
{
|
||||
struct hwep_data *hwep_info = (struct hwep_data *)dai;
|
||||
struct btfmswr *btfmswr = dev_get_drvdata(hwep_info->dev);
|
||||
int ret = -EINVAL;
|
||||
u8 port_type;
|
||||
|
||||
bt_soc_enable_status = 0;
|
||||
BTFMSWR_INFO("dai->id: %d, dai->rate: %d direction: %d", id, sampling_rate, direction);
|
||||
|
||||
btfm_get_sampling_rate(&sampling_rate);
|
||||
btfmswr->sample_rate = sampling_rate;
|
||||
|
||||
switch (id) {
|
||||
case FMAUDIO_TX:
|
||||
port_type = FM_AUDIO_TX1;
|
||||
break;
|
||||
case BTAUDIO_TX:
|
||||
port_type = BT_AUDIO_TX1;
|
||||
break;
|
||||
case BTAUDIO_RX:
|
||||
port_type = BT_AUDIO_RX1;
|
||||
break;
|
||||
case BTAUDIO_A2DP_SINK_TX:
|
||||
port_type = BT_AUDIO_TX2;
|
||||
break;
|
||||
case BTFM_NUM_CODEC_DAIS:
|
||||
default:
|
||||
BTFMSWR_ERR("dai->id is invalid:%d", id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = btfm_swr_enable_port(btfmswr->p_dai_port->port_info[id].port,
|
||||
btfmswr->num_channels, sampling_rate, port_type);
|
||||
|
||||
/* save the enable channel status */
|
||||
if (ret == 0)
|
||||
bt_soc_enable_status = 1;
|
||||
|
||||
if (ret == -EISCONN) {
|
||||
BTFMSWR_ERR("channel opened without closing, returning success");
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* This function will be called once during boot up */
|
||||
static int btfm_swr_dai_set_channel_map(void *dai,
|
||||
unsigned int tx_num, unsigned int *tx_slot,
|
||||
unsigned int rx_num, unsigned int *rx_slot)
|
||||
{
|
||||
BTFMSWR_DBG("");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btfm_swr_dai_get_channel_map(void *dai,
|
||||
unsigned int *tx_num, unsigned int *tx_slot,
|
||||
unsigned int *rx_num, unsigned int *rx_slot, int id)
|
||||
{
|
||||
|
||||
struct hwep_data *hwep_info = (struct hwep_data *)dai;
|
||||
struct btfmswr *btfmswr = dev_get_drvdata(hwep_info->dev);
|
||||
|
||||
*rx_slot = 0;
|
||||
*tx_slot = 0;
|
||||
*rx_num = 0;
|
||||
*tx_num = 0;
|
||||
|
||||
switch (id) {
|
||||
case FMAUDIO_TX:
|
||||
case BTAUDIO_TX:
|
||||
case BTAUDIO_A2DP_SINK_TX:
|
||||
*tx_num = btfmswr->num_channels;
|
||||
*tx_slot = btfmswr->num_channels == 2 ? TWO_CHANNEL_MASK : ONE_CHANNEL_MASK;
|
||||
break;
|
||||
case BTAUDIO_RX:
|
||||
*rx_num = btfmswr->num_channels;
|
||||
*rx_slot = btfmswr->num_channels == 2 ? TWO_CHANNEL_MASK : ONE_CHANNEL_MASK;
|
||||
break;
|
||||
|
||||
default:
|
||||
BTFMSWR_ERR("Unsupported DAI %d", id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int btfm_swr_dai_get_configs(void *dai, void *config, uint8_t id)
|
||||
{
|
||||
struct hwep_data *hwep_info = (struct hwep_data *)dai;
|
||||
struct btfmswr *btfmswr = dev_get_drvdata(hwep_info->dev);
|
||||
struct hwep_dma_configurations *hwep_config;
|
||||
|
||||
BTFMSWR_DBG("");
|
||||
hwep_config = (struct hwep_dma_configurations *)config;
|
||||
|
||||
hwep_config->stream_id = id;
|
||||
hwep_config->sample_rate = btfmswr->sample_rate;
|
||||
hwep_config->bit_width = (uint8_t)btfmswr->bps;
|
||||
hwep_config->codectype = usecase_codec;
|
||||
|
||||
hwep_config->num_channels = btfmswr->num_channels;
|
||||
hwep_config->active_channel_mask = (btfmswr->num_channels == 2 ?
|
||||
TWO_CHANNEL_MASK : ONE_CHANNEL_MASK);
|
||||
hwep_config->lpaif = LPAIF_AUD;
|
||||
hwep_config->inf_index = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct hwep_dai_ops btfmswr_hw_dai_ops = {
|
||||
.hwep_startup = btfm_swr_dai_startup,
|
||||
.hwep_shutdown = btfm_swr_dai_shutdown,
|
||||
.hwep_hw_params = btfm_swr_dai_hw_params,
|
||||
.hwep_prepare = btfm_swr_dai_prepare,
|
||||
.hwep_set_channel_map = btfm_swr_dai_set_channel_map,
|
||||
.hwep_get_channel_map = btfm_swr_dai_get_channel_map,
|
||||
.hwep_get_configs = btfm_swr_dai_get_configs,
|
||||
.hwep_codectype = &usecase_codec,
|
||||
};
|
||||
|
||||
static struct hwep_dai_driver btfmswr_dai_driver[] = {
|
||||
{ /* FM Audio data multiple channel : FM -> lpass */
|
||||
.dai_name = "btaudio_fm_tx",
|
||||
.id = FMAUDIO_TX,
|
||||
.capture = {
|
||||
.stream_name = "FM SWR TX Capture",
|
||||
.rates = SNDRV_PCM_RATE_48000, /* 48 KHz */
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */
|
||||
.rate_max = 48000,
|
||||
.rate_min = 48000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
},
|
||||
.dai_ops = &btfmswr_hw_dai_ops,
|
||||
},
|
||||
{ /* Bluetooth SCO voice uplink: bt -> lpass */
|
||||
.dai_name = "btaudio_tx",
|
||||
.id = BTAUDIO_TX,
|
||||
.capture = {
|
||||
.stream_name = "BT Audio SWR Tx Capture",
|
||||
/* 8 KHz or 16 KHz */
|
||||
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000
|
||||
| SNDRV_PCM_RATE_8000_192000
|
||||
| SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000
|
||||
| SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000
|
||||
| SNDRV_PCM_RATE_192000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */
|
||||
.rate_max = 192000,
|
||||
.rate_min = 8000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
},
|
||||
.dai_ops = &btfmswr_hw_dai_ops,
|
||||
},
|
||||
{ /* Bluetooth SCO voice downlink: lpass -> bt or A2DP Playback */
|
||||
.dai_name = "btaudio_rx",
|
||||
.id = BTAUDIO_RX,
|
||||
.playback = {
|
||||
.stream_name = "BT Audio SWR Rx Playback",
|
||||
/* 8/16/44.1/48/88.2/96 Khz */
|
||||
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000
|
||||
| SNDRV_PCM_RATE_8000_192000
|
||||
| SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000
|
||||
| SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000
|
||||
| SNDRV_PCM_RATE_192000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */
|
||||
.rate_max = 192000,
|
||||
.rate_min = 8000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
},
|
||||
.dai_ops = &btfmswr_hw_dai_ops,
|
||||
},
|
||||
{ /* Bluetooth A2DP sink: bt -> lpass */
|
||||
.dai_name = "btfm_a2dp_sink_swr_tx",
|
||||
.id = BTAUDIO_A2DP_SINK_TX,
|
||||
.capture = {
|
||||
.stream_name = "A2DP sink TX Capture",
|
||||
/* 8/16/44.1/48/88.2/96/192 Khz */
|
||||
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000
|
||||
| SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000
|
||||
| SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000
|
||||
| SNDRV_PCM_RATE_192000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */
|
||||
.rate_max = 192000,
|
||||
.rate_min = 8000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
},
|
||||
.dai_ops = &btfmswr_hw_dai_ops,
|
||||
}
|
||||
};
|
||||
|
||||
static struct hwep_comp_drv btfmswr_hw_driver = {
|
||||
.hwep_probe = btfm_swr_hwep_probe,
|
||||
.hwep_remove = btfm_swr_hwep_remove,
|
||||
.hwep_read = btfm_swr_hwep_read,
|
||||
.hwep_write = btfm_swr_hwep_write,
|
||||
};
|
||||
|
||||
int btfm_swr_register_hw_ep(struct btfmswr *btfm_swr)
|
||||
{
|
||||
struct device *dev = btfm_swr->dev;
|
||||
struct hwep_data *hwep_info;
|
||||
int ret = 0;
|
||||
|
||||
BTFMSWR_INFO("Registering with BTFMCODEC HWEP interface\n");
|
||||
hwep_info = kzalloc(sizeof(struct hwep_data), GFP_KERNEL);
|
||||
|
||||
if (!hwep_info) {
|
||||
BTFMSWR_ERR("%s: failed to allocate memory\n", __func__);
|
||||
ret = -ENOMEM;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Copy EP device parameters as intercations will be on the same device */
|
||||
hwep_info->dev = dev;
|
||||
strscpy(hwep_info->driver_name, SWR_SLAVE_COMPATIBLE_STR, DEVICE_NAME_MAX_LEN);
|
||||
hwep_info->drv = &btfmswr_hw_driver;
|
||||
hwep_info->dai_drv = btfmswr_dai_driver;
|
||||
hwep_info->num_dai = ARRAY_SIZE(btfmswr_dai_driver);
|
||||
hwep_info->num_dai = 4;
|
||||
hwep_info->num_mixer_ctrl = ARRAY_SIZE(status_controls);
|
||||
hwep_info->mixer_ctrl = status_controls;
|
||||
/* Register to hardware endpoint */
|
||||
ret = btfmcodec_register_hw_ep(hwep_info);
|
||||
if (ret) {
|
||||
BTFMSWR_ERR("failed to register with btfmcodec driver hw interface (%d)", ret);
|
||||
goto end;
|
||||
}
|
||||
|
||||
BTFMSWR_INFO("Registered succesfull with BTFMCODEC HWEP interface\n");
|
||||
return ret;
|
||||
end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void btfm_swr_unregister_hwep(void)
|
||||
{
|
||||
BTFMSWR_INFO("Unregistered with BTFMCODEC HWEP interface");
|
||||
/* Unregister with BTFMCODEC HWEP driver */
|
||||
btfmcodec_unregister_hw_ep(SWR_SLAVE_COMPATIBLE_STR);
|
||||
|
||||
}
|
||||
|
||||
MODULE_DESCRIPTION("BTFM SoundWire Codec driver");
|
||||
MODULE_LICENSE("GPL v2");
|
35
qcom/opensource/bt-kernel/soundwire/btfm_swr_hw_interface.h
Normal file
35
qcom/opensource/bt-kernel/soundwire/btfm_swr_hw_interface.h
Normal file
@ -0,0 +1,35 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_BTFM_SWR_HW_INTERFACE_H
|
||||
#define __LINUX_BTFM_SWR_HW_INTERFACE_H
|
||||
|
||||
int btfm_swr_register_hw_ep(struct btfmswr *a);
|
||||
void btfm_swr_unregister_hwep(void);
|
||||
|
||||
enum Codec {
|
||||
SBC = 0,
|
||||
AAC,
|
||||
LDAC,
|
||||
APTX,
|
||||
APTX_HD,
|
||||
APTX_AD,
|
||||
LC3,
|
||||
APTX_AD_SPEECH,
|
||||
LC3_VOICE,
|
||||
APTX_AD_QLEA,
|
||||
APTX_AD_R4,
|
||||
NO_CODEC
|
||||
};
|
||||
|
||||
static const char * const codec_text[] = {"CODEC_TYPE_SBC", "CODEC_TYPE_AAC",
|
||||
"CODEC_TYPE_LDAC", "CODEC_TYPE_APTX",
|
||||
"CODEC_TYPE_APTX_HD", "CODEC_TYPE_APTX_AD",
|
||||
"CODEC_TYPE_LC3", "CODEC_TYPE_APTX_AD_SPEECH",
|
||||
"CODEC_TYPE_LC3_VOICE", "CODEC_TYPE_APTX_AD_QLEA",
|
||||
"CODEC_TYPE_APTX_AD_R4", "CODEC_TYPE_INVALID"};
|
||||
|
||||
static SOC_ENUM_SINGLE_EXT_DECL(codec_display, codec_text);
|
||||
#endif /*__LINUX_BTFM_SWR_HW_INTERFACE_H*/
|
44
qcom/opensource/bt-kernel/soundwire/btfm_swr_slave.c
Normal file
44
qcom/opensource/bt-kernel/soundwire/btfm_swr_slave.c
Normal file
@ -0,0 +1,44 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
|
||||
#include "btfm_swr.h"
|
||||
#include "btfm_swr_slave.h"
|
||||
|
||||
struct soc_port_mapping slave_port[] = {
|
||||
// Evros
|
||||
{
|
||||
.ea = EVROS_EA,
|
||||
.port_info[0].dai_id = FMAUDIO_TX,
|
||||
.port_info[0].port = 5,
|
||||
|
||||
.port_info[1].dai_id = BTAUDIO_TX,
|
||||
.port_info[1].port = 3,
|
||||
|
||||
.port_info[2].dai_id = BTAUDIO_RX,
|
||||
.port_info[2].port = 1,
|
||||
|
||||
.port_info[3].dai_id = BTAUDIO_A2DP_SINK_TX,
|
||||
.port_info[3].port = 4,
|
||||
},
|
||||
|
||||
// Ganges
|
||||
{
|
||||
.ea = GANGES_EA,
|
||||
// FM is not supported on Ganges. populate with invalid port number
|
||||
.port_info[0].dai_id = FMAUDIO_TX,
|
||||
.port_info[0].port = BTFM_INVALID_PORT,
|
||||
|
||||
.port_info[1].dai_id = BTAUDIO_TX,
|
||||
.port_info[1].port = 4,
|
||||
|
||||
.port_info[2].dai_id = BTAUDIO_RX,
|
||||
.port_info[2].port = 1,
|
||||
|
||||
.port_info[3].dai_id = BTAUDIO_A2DP_SINK_TX,
|
||||
.port_info[3].port = 5,
|
||||
},
|
||||
};
|
||||
|
45
qcom/opensource/bt-kernel/soundwire/btfm_swr_slave.h
Normal file
45
qcom/opensource/bt-kernel/soundwire/btfm_swr_slave.h
Normal file
@ -0,0 +1,45 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef BTFM_SWR_SLAVE_H
|
||||
#define BTFM_SWR_SLAVE_H
|
||||
|
||||
#include "btfm_swr.h"
|
||||
|
||||
/* Registers Address */
|
||||
|
||||
/* Register Bit Setting */
|
||||
#define SLAVE_ENABLE_OVERRUN_AUTO_RECOVERY (0x1 << 1)
|
||||
#define SLAVE_ENABLE_UNDERRUN_AUTO_RECOVERY (0x1 << 0)
|
||||
#define SLAVE_SB_PGD_PORT_ENABLE (0x1 << 0)
|
||||
#define SLAVE_SB_PGD_PORT_DISABLE (0x0 << 0)
|
||||
|
||||
|
||||
#define BTFM_INVALID_PORT 0xFF
|
||||
|
||||
extern struct soc_port_mapping slave_port[];
|
||||
|
||||
enum {
|
||||
QCA_GANGES_SOC_ID_0100 = 0x40210100,
|
||||
QCA_GANGES_SOC_ID_0200 = 0x40210200,
|
||||
};
|
||||
|
||||
|
||||
enum {
|
||||
QCA_EVROS_SOC_ID_0100 = 0x40200100,
|
||||
QCA_EVROS_SOC_ID_0200 = 0x40200200,
|
||||
};
|
||||
|
||||
|
||||
enum {
|
||||
EVROS_EA = 0x0108170220,
|
||||
GANGES_EA = 0x0208170220,
|
||||
};
|
||||
|
||||
/* Specific defines for slave slimbus device */
|
||||
#define SLAVE_SWR_REG_OFFSET 0x0800
|
||||
|
||||
#endif
|
83
qcom/opensource/bt-kernel/target.bzl
Normal file
83
qcom/opensource/bt-kernel/target.bzl
Normal file
@ -0,0 +1,83 @@
|
||||
load(":bt_kernel.bzl", "define_bt_modules")
|
||||
|
||||
def define_pineapple():
|
||||
define_bt_modules(
|
||||
target = "pineapple",
|
||||
modules = [
|
||||
"btpower",
|
||||
"bt_fm_slim",
|
||||
"radio-i2c-rtc6226-qca",
|
||||
# "btfm_slim_codec",
|
||||
],
|
||||
config_options = [
|
||||
"CONFIG_MSM_BT_POWER",
|
||||
"CONFIG_BTFM_SLIM",
|
||||
"CONFIG_I2C_RTC6226_QCA",
|
||||
# "CONFIG_SLIM_BTFM_CODEC",
|
||||
"CONFIG_BT_HW_SECURE_DISABLE",
|
||||
]
|
||||
)
|
||||
|
||||
def define_blair():
|
||||
define_bt_modules(
|
||||
target = "blair",
|
||||
modules = [
|
||||
"btpower",
|
||||
"bt_fm_slim",
|
||||
"radio-i2c-rtc6226-qca",
|
||||
],
|
||||
config_options = [
|
||||
"CONFIG_MSM_BT_POWER",
|
||||
"CONFIG_BTFM_SLIM",
|
||||
"CONFIG_I2C_RTC6226_QCA",
|
||||
"CONFIG_BT_HW_SECURE_DISABLE",
|
||||
]
|
||||
)
|
||||
|
||||
def define_pitti():
|
||||
define_bt_modules(
|
||||
target = "pitti",
|
||||
modules = [
|
||||
"btpower",
|
||||
"bt_fm_slim",
|
||||
"radio-i2c-rtc6226-qca",
|
||||
],
|
||||
config_options = [
|
||||
"CONFIG_MSM_BT_POWER",
|
||||
"CONFIG_BTFM_SLIM",
|
||||
"CONFIG_I2C_RTC6226_QCA",
|
||||
"CONFIG_BT_HW_SECURE_DISABLE",
|
||||
]
|
||||
)
|
||||
|
||||
def define_niobe():
|
||||
define_bt_modules(
|
||||
target = "niobe",
|
||||
modules = [
|
||||
"btpower",
|
||||
"bt_fm_slim",
|
||||
"radio-i2c-rtc6226-qca",
|
||||
],
|
||||
config_options = [
|
||||
"CONFIG_MSM_BT_POWER",
|
||||
"CONFIG_BTFM_SLIM",
|
||||
"CONFIG_I2C_RTC6226_QCA",
|
||||
"CONFIG_BT_HW_SECURE_DISABLE",
|
||||
]
|
||||
)
|
||||
|
||||
def define_anorak61():
|
||||
define_bt_modules(
|
||||
target = "anorak61",
|
||||
modules = [
|
||||
"btpower",
|
||||
"bt_fm_slim",
|
||||
"radio-i2c-rtc6226-qca",
|
||||
],
|
||||
config_options = [
|
||||
"CONFIG_MSM_BT_POWER",
|
||||
"CONFIG_BTFM_SLIM",
|
||||
"CONFIG_I2C_RTC6226_QCA",
|
||||
"CONFIG_BT_HW_SECURE_DISABLE",
|
||||
]
|
||||
)
|
Loading…
Reference in New Issue
Block a user