ASoC: Add WCD937X slave and core driver

Add wcd937x swr slave and codec driver.
This adds only basic support for codec registration.

Change-Id: I87519a234f14d34a019c8f66652b7224759e639c
Signed-off-by: Rohit kumar <rohitkr@codeaurora.org>
This commit is contained in:
Rohit kumar 2018-05-14 18:21:52 +05:30 committed by Tanya Dixit
parent 2e628184b9
commit 759426ec4b
8 changed files with 627 additions and 2 deletions

View file

@ -30,8 +30,8 @@ include $(MY_LOCAL_PATH)/asoc/codecs/aqt1000/Android.mk
endif
ifeq ($(call is-board-platform-in-list,$(MSMSTEPPE)),true)
$(shell rm -rf $(PRODUCT_OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/bolero/Module.symvers)
include $(MY_LOCAL_PATH)/asoc/codecs/bolero/Android.mk
$(shell rm -rf $(PRODUCT_OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/wcd937x/Module.symvers)
include $(MY_LOCAL_PATH)/asoc/codecs/wcd937x/Android.mk
endif
ifeq ($(call is-board-platform-in-list,msm8953 sdm670 qcs605),true)

View file

@ -186,6 +186,7 @@ ifeq ($(KERNEL_BUILD), 1)
obj-y += sdm660_cdc/
obj-y += msm_sdw/
obj-y += wcd9360/
obj-y += wcd937x/
endif
# Module information used by KBuild framework
obj-$(CONFIG_WCD9XXX_CODEC_CORE) += wcd_core_dlkm.o

View file

@ -0,0 +1,57 @@
# Android makefile for audio kernel modules
# Assume no targets will be supported
# Check if this driver needs be built for current target
ifeq ($(call is-board-platform,$(MSMSTEPPE)),true)
AUDIO_SELECT := CONFIG_SND_SOC_SM6150=m
endif
AUDIO_CHIPSET := audio
# Build/Package only in case of supported target
ifeq ($(call is-board-platform-in-list,msm8953 sdm845 sdm670 qcs605 msmnile $(MSMSTEPPE)),true)
LOCAL_PATH := $(call my-dir)
# This makefile is only for DLKM
ifneq ($(findstring vendor,$(LOCAL_PATH)),)
ifneq ($(findstring opensource,$(LOCAL_PATH)),)
AUDIO_BLD_DIR := $(ANDROID_BUILD_TOP)/vendor/qcom/opensource/audio-kernel
endif # opensource
DLKM_DIR := $(TOP)/device/qcom/common/dlkm
# Build audio.ko as $(AUDIO_CHIPSET)_audio.ko
###########################################################
# This is set once per LOCAL_PATH, not per (kernel) module
KBUILD_OPTIONS := AUDIO_ROOT=$(AUDIO_BLD_DIR)
# We are actually building audio.ko here, as per the
# requirement we are specifying <chipset>_audio.ko as LOCAL_MODULE.
# This means we need to rename the module to <chipset>_audio.ko
# after audio.ko is built.
KBUILD_OPTIONS += MODNAME=wcd937x_dlkm
KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM)
KBUILD_OPTIONS += $(AUDIO_SELECT)
###########################################################
include $(CLEAR_VARS)
LOCAL_MODULE := $(AUDIO_CHIPSET)_wcd937x.ko
LOCAL_MODULE_KBUILD_NAME := wcd937x_dlkm.ko
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_DEBUG_ENABLE := true
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
include $(DLKM_DIR)/AndroidKernelModule.mk
###########################################################
include $(CLEAR_VARS)
LOCAL_MODULE := $(AUDIO_CHIPSET)_wcd937x_slave.ko
LOCAL_MODULE_KBUILD_NAME := wcd937x_slave_dlkm.ko
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_DEBUG_ENABLE := true
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
include $(DLKM_DIR)/AndroidKernelModule.mk
###########################################################
endif # DLKM check
endif # supported target check

110
asoc/codecs/wcd937x/Kbuild Normal file
View file

@ -0,0 +1,110 @@
# We can build either as part of a standalone Kernel build or as
# an external module. Determine which mechanism is being used
ifeq ($(MODNAME),)
KERNEL_BUILD := 1
else
KERNEL_BUILD := 0
endif
ifeq ($(KERNEL_BUILD), 1)
# These are configurable via Kconfig for kernel-based builds
# Need to explicitly configure for Android-based builds
AUDIO_BLD_DIR := $(ANDROID_BUILD_TOP)/kernel/msm-4.9
AUDIO_ROOT := $(AUDIO_BLD_DIR)/techpack/audio
endif
ifeq ($(KERNEL_BUILD), 0)
ifeq ($(CONFIG_ARCH_SM6150), y)
include $(AUDIO_ROOT)/config/sm6150auto.conf
export
INCS += -include $(AUDIO_ROOT)/config/sm6150autoconf.h
endif
endif
# As per target team, build is done as follows:
# Defconfig : build with default flags
# Slub : defconfig + CONFIG_SLUB_DEBUG := y +
# CONFIG_SLUB_DEBUG_ON := y + CONFIG_PAGE_POISONING := y
# Perf : Using appropriate msmXXXX-perf_defconfig
#
# Shipment builds (user variants) should not have any debug feature
# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds
# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since
# there is no other way to identify defconfig builds, QTI internal
# representation of perf builds (identified using the string 'perf'),
# is used to identify if the build is a slub or defconfig one. This
# way no critical debug feature will be enabled for perf and shipment
# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT
# config.
############ UAPI ############
UAPI_DIR := uapi
UAPI_INC := -I$(AUDIO_ROOT)/include/$(UAPI_DIR)
############ COMMON ############
COMMON_DIR := include
COMMON_INC := -I$(AUDIO_ROOT)/$(COMMON_DIR)
############ WCD937X ############
# for WCD937X Codec
ifdef CONFIG_SND_SOC_WCD937X
WCD937X_OBJS += wcd937x.o
endif
ifdef CONFIG_SND_SOC_WCD937X_SLAVE
WCD937X_SLAVE_OBJS += wcd937x_slave.o
endif
LINUX_INC += -Iinclude/linux
INCS += $(COMMON_INC) \
$(UAPI_INC)
EXTRA_CFLAGS += $(INCS)
CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \
-DANI_LITTLE_BIT_ENDIAN \
-DDOT11F_LITTLE_ENDIAN_HOST \
-DANI_COMPILER_TYPE_GCC \
-DANI_OS_TYPE_ANDROID=6 \
-DPTT_SOCK_SVC_ENABLE \
-Wall\
-Werror\
-D__linux__
KBUILD_CPPFLAGS += $(CDEFINES)
# Currently, for versions of gcc which support it, the kernel Makefile
# is disabling the maybe-uninitialized warning. Re-enable it for the
# AUDIO driver. Note that we must use EXTRA_CFLAGS here so that it
# will override the kernel settings.
ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y)
EXTRA_CFLAGS += -Wmaybe-uninitialized
endif
#EXTRA_CFLAGS += -Wmissing-prototypes
ifeq ($(call cc-option-yn, -Wheader-guard),y)
EXTRA_CFLAGS += -Wheader-guard
endif
ifeq ($(KERNEL_BUILD), 0)
KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/ipc/Module.symvers
KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/dsp/Module.symvers
KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/Module.symvers
KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/Module.symvers
KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/soc/Module.symvers
endif
# Module information used by KBuild framework
obj-$(CONFIG_SND_SOC_WCD937X) += wcd937x_dlkm.o
wcd937x_dlkm-y := $(WCD937X_OBJS)
obj-$(CONFIG_SND_SOC_WCD937X_SLAVE) += wcd937x_slave_dlkm.o
wcd937x_slave_dlkm-y := $(WCD937X_SLAVE_OBJS)
# inject some build related information
DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\"

View file

@ -0,0 +1,310 @@
/*
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/component.h>
#include <sound/soc.h>
#include <soc/soundwire.h>
#include "../msm-cdc-pinctrl.h"
struct wcd937x_priv {
struct device *dev;
struct snd_soc_codec *codec;
struct device_node *rst_np;
struct swr_device *rx_swr_dev;
struct swr_device *tx_swr_dev;
};
struct wcd937x_pdata {
struct device_node *rst_np;
struct device_node *rx_slave;
struct device_node *tx_slave;
};
static int wcd937x_soc_codec_probe(struct snd_soc_codec *codec)
{
struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
if (!wcd937x)
return -EINVAL;
return 0;
}
static int wcd937x_soc_codec_remove(struct snd_soc_codec *codec)
{
struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
if (!wcd937x)
return -EINVAL;
return 0;
}
static struct regmap *wcd937x_get_regmap(struct device *dev)
{
struct wcd937x_priv *wcd937x = dev_get_drvdata(dev);
return wcd937x->regmap;
}
static const struct snd_kcontrol_new wcd937x_snd_controls[] = {
};
static const struct snd_soc_dapm_widget wcd937x_dapm_widgets[] = {
};
static const struct snd_soc_dapm_route wcd937x_audio_map[] = {
};
static struct snd_soc_codec_driver soc_codec_dev_wcd937x = {
.probe = wcd937x_soc_codec_probe,
.remove = wcd937x_soc_codec_remove,
.get_regmap = wcd937x_get_regmap,
.component_driver = {
.controls = wcd937x_snd_controls,
.num_controls = ARRAY_SIZE(wcd937x_snd_controls),
.dapm_widgets = wcd937x_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(wcd937x_dapm_widgets),
.dapm_routes = wcd937x_audio_map,
.num_dapm_routes = ARRAY_SIZE(wcd937x_audio_map),
},
};
int wcd937x_reset(struct device *dev)
{
struct wcd937x_priv *wcd937x = NULL;
int rc = 0;
int value = 0;
if (!dev)
return -ENODEV;
wcd937x = dev_get_drvdata(dev);
if (!wcd937x)
return -EINVAL;
if (!wcd937x->rst_np) {
dev_err(dev, "%s: reset gpio device node not specified\n",
__func__);
return -EINVAL;
}
value = msm_cdc_pinctrl_get_state(wcd937x->rst_np);
if (value > 0)
return 0;
rc = msm_cdc_pinctrl_select_sleep_state(wcd937x->rst_np);
if (rc) {
dev_err(dev, "%s: wcd sleep state request fail!\n",
__func__);
return rc;
}
/* 20ms sleep required after pulling the reset gpio to LOW */
msleep(20);
rc = msm_cdc_pinctrl_select_active_state(wcd937x->rst_np);
if (rc) {
dev_err(dev, "%s: wcd active state request fail!\n",
__func__);
return rc;
}
msleep(20);
return rc;
}
struct wcd937x_pdata *wcd937x_populate_dt_data(struct device *dev)
{
struct wcd937x_pdata *pdata = NULL;
pdata = devm_kzalloc(dev, sizeof(struct wcd937x_pdata),
GFP_KERNEL);
if (!pdata)
return NULL;
pdata->rst_np = of_parse_phandle(dev->of_node,
"qcom,wcd937x-reset-node", 0);
if (!pdata->rst_np) {
dev_err(dev, "%s: Looking up %s property in node %s failed\n",
__func__, "qcom,wcd937x-reset-node",
dev->of_node->full_name);
return NULL;
}
pdata->rx_slave = of_parse_phandle(dev->of_node, "qcom,rx-slave", 0);
pdata->tx_slave = of_parse_phandle(dev->of_node, "qcom,tx-slave", 0);
return pdata;
}
static int wcd937x_bind(struct device *dev)
{
int ret = 0;
struct wcd937x_priv *wcd937x = NULL;
struct wcd937x_pdata *pdata = NULL;
wcd937x = devm_kzalloc(dev, sizeof(struct wcd937x_priv), GFP_KERNEL);
if (!wcd937x)
return -ENOMEM;
dev_set_drvdata(dev, wcd937x);
pdata = wcd937x_populate_dt_data(dev);
if (!pdata) {
dev_err(dev, "%s: Fail to obtain platform data\n", __func__);
return -EINVAL;
}
wcd937x->rst_np = pdata->rst_np;
wcd937x_reset(dev);
/*
* Add 5msec delay to provide sufficient time for
* soundwire auto enumeration of slave devices as
* as per HW requirement.
*/
usleep_range(5000, 5010);
ret = component_bind_all(dev, wcd937x);
if (ret) {
dev_err(dev, "%s: Slave bind failed, ret = %d\n",
__func__, ret);
return ret;
}
wcd937x->rx_swr_dev = get_matching_swr_slave_device(pdata->rx_slave);
if (!wcd937x->rx_swr_dev) {
dev_err(dev, "%s: Could not find RX swr slave device\n",
__func__);
ret = -ENODEV;
goto err;
}
wcd937x->tx_swr_dev = get_matching_swr_slave_device(pdata->tx_slave);
if (!wcd937x->tx_swr_dev) {
dev_err(dev, "%s: Could not find TX swr slave device\n",
__func__);
ret = -ENODEV;
goto err;
}
ret = snd_soc_register_codec(dev, &soc_codec_dev_wcd937x,
NULL, 0);
if (ret) {
dev_err(dev, "%s: Codec registration failed\n",
__func__);
goto err;
}
return ret;
err:
component_unbind_all(dev, wcd937x);
return ret;
}
static void wcd937x_unbind(struct device *dev)
{
struct wcd937x_priv *wcd937x = dev_get_drvdata(dev);
snd_soc_unregister_codec(dev);
component_unbind_all(dev, wcd937x);
}
static const struct of_device_id wcd937x_dt_match[] = {
{ .compatible = "qcom,wcd937x-codec" },
{}
};
static const struct component_master_ops wcd937x_comp_ops = {
.bind = wcd937x_bind,
.unbind = wcd937x_unbind,
};
static int wcd937x_compare_of(struct device *dev, void *data)
{
return dev->of_node == data;
}
static void wcd937x_release_of(struct device *dev, void *data)
{
of_node_put(data);
}
static int wcd937x_add_slave_components(struct device *dev,
struct component_match **matchptr)
{
struct device_node *np, *rx_node, *tx_node;
np = dev->of_node;
rx_node = of_parse_phandle(np, "qcom,rx-slave", 0);
if (!rx_node) {
dev_err(dev, "%s: Rx-slave node not defined\n", __func__);
return -ENODEV;
}
of_node_get(rx_node);
component_match_add_release(dev, matchptr,
wcd937x_release_of,
wcd937x_compare_of,
rx_node);
tx_node = of_parse_phandle(np, "qcom,tx-slave", 0);
if (!tx_node) {
dev_err(dev, "%s: Tx-slave node not defined\n", __func__);
return -ENODEV;
}
of_node_get(tx_node);
component_match_add_release(dev, matchptr,
wcd937x_release_of,
wcd937x_compare_of,
tx_node);
return 0;
}
static int wcd937x_probe(struct platform_device *pdev)
{
struct component_match *match = NULL;
int ret;
ret = wcd937x_add_slave_components(&pdev->dev, &match);
if (ret)
return ret;
return component_master_add_with_match(&pdev->dev,
&wcd937x_comp_ops, match);
}
static int wcd937x_remove(struct platform_device *pdev)
{
component_master_del(&pdev->dev, &wcd937x_comp_ops);
return 0;
}
static struct platform_driver wcd937x_codec_driver = {
.probe = wcd937x_probe,
.remove = wcd937x_remove,
.driver = {
.name = "wcd937x_codec",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(wcd937x_dt_match),
},
};
module_platform_driver(wcd937x_codec_driver);
MODULE_DESCRIPTION("WCD937X Codec driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,124 @@
/*
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/component.h>
#include <soc/soundwire.h>
struct wcd937x_slave_priv {
struct swr_device *swr_slave;
};
static int wcd937x_slave_bind(struct device *dev,
struct device *master, void *data)
{
int ret = 0;
struct wcd937x_slave_priv *wcd937x_slave = NULL;
uint8_t devnum = 0;
struct swr_device *pdev = to_swr_device(dev);
wcd937x_slave = devm_kzalloc(&pdev->dev,
sizeof(struct wcd937x_slave_priv), GFP_KERNEL);
if (!wcd937x_slave)
return -ENOMEM;
swr_set_dev_data(pdev, wcd937x_slave);
wcd937x_slave->swr_slave = pdev;
ret = swr_get_logical_dev_num(pdev, pdev->addr, &devnum);
if (ret) {
dev_dbg(&pdev->dev,
"%s get devnum %d for dev addr %lx failed\n",
__func__, devnum, pdev->addr);
swr_remove_device(pdev);
return ret;
}
pdev->dev_num = devnum;
return ret;
}
static void wcd937x_slave_unbind(struct device *dev,
struct device *master, void *data)
{
struct wcd937x_slave_priv *wcd937x_slave = NULL;
struct swr_device *pdev = to_swr_device(dev);
wcd937x_slave = swr_get_dev_data(pdev);
if (!wcd937x_slave) {
dev_err(&pdev->dev, "%s: wcd937x_slave is NULL\n", __func__);
return;
}
swr_set_dev_data(pdev, NULL);
}
static const struct swr_device_id wcd937x_swr_id[] = {
{"wcd937x-slave", 0},
{}
};
static const struct of_device_id wcd937x_swr_dt_match[] = {
{
.compatible = "qcom,wcd937x-slave",
},
{}
};
static const struct component_ops wcd937x_slave_comp_ops = {
.bind = wcd937x_slave_bind,
.unbind = wcd937x_slave_unbind,
};
static int wcd937x_swr_probe(struct swr_device *pdev)
{
return component_add(&pdev->dev, &wcd937x_slave_comp_ops);
}
static int wcd937x_swr_remove(struct swr_device *pdev)
{
component_del(&pdev->dev, &wcd937x_slave_comp_ops);
return 0;
}
static struct swr_driver wcd937x_slave_driver = {
.driver = {
.name = "wcd937x-slave",
.owner = THIS_MODULE,
.of_match_table = wcd937x_swr_dt_match,
},
.probe = wcd937x_swr_probe,
.remove = wcd937x_swr_remove,
.id_table = wcd937x_swr_id,
};
static int __init wcd937x_slave_init(void)
{
return swr_driver_register(&wcd937x_slave_driver);
}
static void __exit wcd937x_slave_exit(void)
{
swr_driver_unregister(&wcd937x_slave_driver);
}
module_init(wcd937x_slave_init);
module_exit(wcd937x_slave_exit);
MODULE_DESCRIPTION("WCD937X Swr Slave driver");
MODULE_LICENSE("GPL v2");

View file

@ -334,4 +334,7 @@ extern int swr_slvdev_datapath_control(struct swr_device *swr_dev, u8 dev_num,
extern int swr_remove_from_group(struct swr_device *dev, u8 dev_num);
extern void swr_remove_device(struct swr_device *swr_dev);
extern struct swr_device *get_matching_swr_slave_device(struct device_node *np);
#endif /* _LINUX_SOUNDWIRE_H */

View file

@ -792,6 +792,26 @@ void swr_master_add_boarddevices(struct swr_master *master)
}
EXPORT_SYMBOL(swr_master_add_boarddevices);
struct swr_device *get_matching_swr_slave_device(struct device_node *np)
{
struct swr_device *swr = NULL;
struct swr_master *master;
mutex_lock(&board_lock);
list_for_each_entry(master, &swr_master_list, list) {
mutex_lock(&master->mlock);
list_for_each_entry(swr, &master->devices, dev_list) {
if (swr->dev.of_node == np)
break;
}
mutex_unlock(&master->mlock);
}
mutex_unlock(&board_lock);
return swr;
}
EXPORT_SYMBOL(get_matching_swr_slave_device);
static void swr_unregister_device(struct swr_device *swr)
{
if (swr)