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:
parent
2e628184b9
commit
759426ec4b
8 changed files with 627 additions and 2 deletions
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
57
asoc/codecs/wcd937x/Android.mk
Normal file
57
asoc/codecs/wcd937x/Android.mk
Normal 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
110
asoc/codecs/wcd937x/Kbuild
Normal 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')\"
|
310
asoc/codecs/wcd937x/wcd937x.c
Normal file
310
asoc/codecs/wcd937x/wcd937x.c
Normal 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");
|
124
asoc/codecs/wcd937x/wcd937x_slave.c
Normal file
124
asoc/codecs/wcd937x/wcd937x_slave.c
Normal 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");
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Reference in a new issue