Change-Id: Ic797bb237b29e0ddc5925e64191bad72d0ea3def
git-subtree-dir: techpack/audio
git-subtree-mainline: b0e9337da1
git-subtree-split: 4f7d3b88983d4ac4f5dd5425f0b08ef61d56e133
This commit is contained in:
Bruno Martins 2020-02-09 07:57:23 +00:00
commit bf2a78dd67
278 changed files with 263725 additions and 0 deletions

41
techpack/audio/Makefile Normal file
View file

@ -0,0 +1,41 @@
# auto-detect subdirs
ifeq ($(CONFIG_ARCH_SDM845), y)
include $(srctree)/techpack/audio/config/sdm845auto.conf
export
endif
ifeq ($(CONFIG_ARCH_SDM670), y)
include $(srctree)/techpack/audio/config/sdm670auto.conf
export
endif
ifeq ($(CONFIG_ARCH_SDXPOORWILLS), y)
include $(srctree)/techpack/audio/config/sdxpoorwillsauto.conf
export
endif
# Use USERINCLUDE when you must reference the UAPI directories only.
USERINCLUDE += \
-I$(srctree)/techpack/audio/include/uapi \
# Use LINUXINCLUDE when you must reference the include/ directory.
# Needed to be compatible with the O= option
LINUXINCLUDE += \
-I$(srctree)/techpack/audio/include/uapi \
-I$(srctree)/techpack/audio/include
ifeq ($(CONFIG_ARCH_SDM845), y)
LINUXINCLUDE += \
-include $(srctree)/techpack/audio/config/sdm845autoconf.h
endif
ifeq ($(CONFIG_ARCH_SDM670), y)
LINUXINCLUDE += \
-include $(srctree)/techpack/audio/config/sdm670autoconf.h
endif
ifeq ($(CONFIG_ARCH_SDXPOORWILLS), y)
LINUXINCLUDE += \
-include $(srctree)/techpack/audio/config/sdxpoorwillsautoconf.h
endif
obj-y += asoc/
obj-y += dsp/
obj-y += ipc/
obj-y += soc/

24
techpack/audio/NOTICE Normal file
View file

@ -0,0 +1,24 @@
Copyright (c) 2009-2017, 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.
________________________________________
Copyright (C) 2008 Google, Inc.
Copyright (C) 2008 HTC Corporation
Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
This software is licensed under the terms of the GNU General Public
License version 2, as published by the Free Software Foundation, and
may be copied, distributed, and modified under those terms.
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.

View file

@ -0,0 +1,45 @@
# MSM Machine Support
snd-soc-hostless-pcm-objs := msm-pcm-hostless.o
obj-$(CONFIG_SND_SOC_MSM_HOSTLESS_PCM) += snd-soc-hostless-pcm.o
snd-soc-qdsp6v2-objs := msm-dai-fe.o
obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o
#for CPE drivers
snd-soc-cpe-objs := msm-cpe-lsm.o
obj-$(CONFIG_SND_SOC_CPE) += snd-soc-cpe.o
# for MSM8998 sound card driver
snd-soc-msm8998-objs := msm8998.o
obj-$(CONFIG_SND_SOC_MACHINE_MSM8998) += snd-soc-msm8998.o
# for SDM670 sound card driver
snd-soc-sdm670-objs := sdm660-common.o sdm660-internal.o sdm660-external.o sdm660-ext-dai-links.o
obj-$(CONFIG_SND_SOC_SDM670) += snd-soc-sdm670.o
# for SDM845 sound card driver
snd-soc-sdm845-objs := sdm845.o
obj-$(CONFIG_SND_SOC_MACHINE_SDM845) += snd-soc-sdm845.o
# for SDXPOORWILLS sound card driver
snd-soc-sdxpoorwills-objs := sdxpoorwills.o
obj-$(CONFIG_SND_SOC_MACHINE_SDXPOORWILLS) += snd-soc-sdxpoorwills.o
snd-soc-qdsp6v2-objs += msm-dai-q6-v2.o msm-pcm-q6-v2.o \
msm-pcm-routing-v2.o msm-compress-q6-v2.o \
msm-pcm-afe-v2.o msm-pcm-voip-v2.o \
msm-pcm-voice-v2.o msm-dai-q6-hdmi-v2.o \
msm-lsm-client.o msm-pcm-host-voice-v2.o \
msm-audio-effects-q6-v2.o msm-pcm-loopback-v2.o \
msm-dai-slim.o msm-transcode-loopback-q6-v2.o msm-pcm-q6-noirq.o
obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o msm-pcm-dtmf-v2.o \
msm-dai-stub-v2.o
obj-$(CONFIG_SND_HWDEP_ROUTING) += msm-pcm-routing-devdep.o
obj-$(CONFIG_DOLBY_DAP) += msm-dolby-dap-config.o
obj-$(CONFIG_DOLBY_DS2) += msm-ds2-dap-config.o
obj-$(CONFIG_DOLBY_LICENSE) += msm-ds2-dap-config.o
obj-$(CONFIG_DTS_SRS_TM) += msm-dts-srs-tm-config.o
obj-$(CONFIG_QTI_PP) += msm-qti-pp-config.o
obj-y += codecs/

View file

@ -0,0 +1,40 @@
snd-soc-wcd9xxx-objs := wcd9xxx-common-v2.o wcd9xxx-resmgr-v2.o \
wcdcal-hwdep.o wcd9xxx-soc-init.o wcd-dsp-utils.o \
wcd-dsp-mgr.o audio-ext-clk-up.o
snd-soc-wcd-cpe-objs := wcd_cpe_services.o wcd_cpe_core.o
snd-soc-wsa881x-objs := wsa881x.o wsa881x-tables.o wsa881x-regmap.o wsa881x-temp-sensor.o
snd-soc-wcd-mbhc-objs := wcd-mbhc-v2.o
ifneq (,$(filter $(CONFIG_SND_SOC_WCD_MBHC_LEGACY),y m))
snd-soc-wcd-mbhc-objs += wcd-mbhc-legacy.o
endif
ifneq (,$(filter $(CONFIG_SND_SOC_WCD_MBHC_ADC),y m))
snd-soc-wcd-mbhc-objs += wcd-mbhc-adc.o
endif
snd-soc-wcd-spi-objs := wcd-spi.o
snd-soc-wcd9335-objs := wcd9335.o
snd-soc-wcd-cpe-objs := wcd_cpe_services.o wcd_cpe_core.o
obj-$(CONFIG_SND_SOC_WCD9335) += snd-soc-wcd9335.o
obj-$(CONFIG_SND_SOC_WCD_CPE) += snd-soc-wcd-cpe.o
obj-$(CONFIG_SND_SOC_WCD934X) += wcd934x/
obj-$(CONFIG_SND_SOC_SDM660_CDC) += sdm660_cdc/
obj-$(CONFIG_SND_SOC_MSM_SDW) += msm_sdw/
obj-$(CONFIG_SND_SOC_WCD9XXX_V2) += snd-soc-wcd9xxx.o
obj-$(CONFIG_SND_SOC_WCD_MBHC) += snd-soc-wcd-mbhc.o
obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o
obj-$(CONFIG_SND_SOC_WCD_SPI) += snd-soc-wcd-spi.o
snd-soc-msm-stub-objs := msm_stub.o
obj-$(CONFIG_SND_SOC_MSM_STUB) += snd-soc-msm-stub.o
wcd-core-objs := wcd9xxx-rst.o wcd9xxx-core-init.o \
wcd9xxx-core.o wcd9xxx-irq.o \
wcd9xxx-slimslave.o wcd9xxx-utils.o \
wcd9335-regmap.o wcd9335-tables.o \
msm-cdc-pinctrl.o msm-cdc-supply.o
wcd-core-objs += wcd934x/wcd934x-regmap.o
wcd-core-objs += wcd934x/wcd934x-tables.o
obj-$(CONFIG_WCD9XXX_CODEC_CORE) += wcd-core.o
obj-$(CONFIG_SND_SOC_MSM_HDMI_CODEC_RX) += msm_hdmi_codec_rx.o

View file

@ -0,0 +1,627 @@
/* Copyright (c) 2015-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/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include "../../../drivers/clk/qcom/common.h"
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <dt-bindings/clock/qcom,audio-ext-clk.h>
#include <dsp/q6afe-v2.h>
#include "audio-ext-clk-up.h"
enum audio_clk_mux {
AP_CLK2,
LPASS_MCLK,
LPASS_MCLK2,
};
struct pinctrl_info {
struct pinctrl *pinctrl;
struct pinctrl_state *sleep;
struct pinctrl_state *active;
char __iomem *base;
};
struct audio_ext_ap_clk {
bool enabled;
int gpio;
struct clk_fixed_factor fact;
};
struct audio_ext_pmi_clk {
int gpio;
struct clk_fixed_factor fact;
};
struct audio_ext_ap_clk2 {
bool enabled;
struct pinctrl_info pnctrl_info;
struct clk_fixed_factor fact;
};
struct audio_ext_lpass_mclk {
struct pinctrl_info pnctrl_info;
struct clk_fixed_factor fact;
};
static struct afe_clk_set clk2_config = {
Q6AFE_LPASS_CLK_CONFIG_API_VERSION,
Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR,
Q6AFE_LPASS_IBIT_CLK_11_P2896_MHZ,
Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
Q6AFE_LPASS_CLK_ROOT_DEFAULT,
0,
};
static struct afe_clk_set lpass_default = {
Q6AFE_LPASS_CLK_CONFIG_API_VERSION,
Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR,
Q6AFE_LPASS_IBIT_CLK_11_P2896_MHZ,
Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
Q6AFE_LPASS_CLK_ROOT_DEFAULT,
0,
};
static struct afe_clk_set lpass_mclk = {
Q6AFE_LPASS_CLK_CONFIG_API_VERSION,
Q6AFE_LPASS_CLK_ID_MCLK_3,
Q6AFE_LPASS_OSR_CLK_9_P600_MHZ,
Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
Q6AFE_LPASS_CLK_ROOT_DEFAULT,
0,
};
static inline struct audio_ext_ap_clk *to_audio_ap_clk(struct clk_hw *hw)
{
return container_of(hw, struct audio_ext_ap_clk, fact.hw);
}
static int audio_ext_clk_prepare(struct clk_hw *hw)
{
struct audio_ext_ap_clk *audio_clk = to_audio_ap_clk(hw);
pr_debug("%s: gpio: %d\n", __func__, audio_clk->gpio);
if (gpio_is_valid(audio_clk->gpio))
return gpio_direction_output(audio_clk->gpio, 1);
return 0;
}
static void audio_ext_clk_unprepare(struct clk_hw *hw)
{
struct audio_ext_ap_clk *audio_clk = to_audio_ap_clk(hw);
pr_debug("%s: gpio: %d\n", __func__, audio_clk->gpio);
if (gpio_is_valid(audio_clk->gpio))
gpio_direction_output(audio_clk->gpio, 0);
}
static inline struct audio_ext_ap_clk2 *to_audio_ap_clk2(struct clk_hw *hw)
{
return container_of(hw, struct audio_ext_ap_clk2, fact.hw);
}
static int audio_ext_clk2_prepare(struct clk_hw *hw)
{
struct audio_ext_ap_clk2 *audio_clk2 = to_audio_ap_clk2(hw);
struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info;
int ret;
if (!pnctrl_info->pinctrl || !pnctrl_info->active)
return 0;
ret = pinctrl_select_state(pnctrl_info->pinctrl,
pnctrl_info->active);
if (ret) {
pr_err("%s: active state select failed with %d\n",
__func__, ret);
return -EIO;
}
clk2_config.enable = 1;
ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk2_config);
if (ret < 0) {
pr_err("%s: failed to set clock, ret = %d\n", __func__, ret);
return -EINVAL;
}
return 0;
}
static void audio_ext_clk2_unprepare(struct clk_hw *hw)
{
struct audio_ext_ap_clk2 *audio_clk2 = to_audio_ap_clk2(hw);
struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info;
int ret;
if (!pnctrl_info->pinctrl || !pnctrl_info->sleep)
return;
ret = pinctrl_select_state(pnctrl_info->pinctrl,
pnctrl_info->sleep);
if (ret)
pr_err("%s: sleep state select failed with %d\n",
__func__, ret);
clk2_config.enable = 0;
ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk2_config);
if (ret < 0)
pr_err("%s: failed to reset clock, ret = %d\n", __func__, ret);
}
static inline struct audio_ext_lpass_mclk *to_audio_lpass_mclk(
struct clk_hw *hw)
{
return container_of(hw, struct audio_ext_lpass_mclk, fact.hw);
}
static int audio_ext_lpass_mclk_prepare(struct clk_hw *hw)
{
struct audio_ext_lpass_mclk *audio_lpass_mclk = to_audio_lpass_mclk(hw);
struct pinctrl_info *pnctrl_info = &audio_lpass_mclk->pnctrl_info;
int ret;
lpass_mclk.enable = 1;
ret = afe_set_lpass_clock_v2(AFE_PORT_ID_PRIMARY_MI2S_RX,
&lpass_mclk);
if (ret < 0) {
pr_err("%s afe_set_digital_codec_core_clock failed\n",
__func__);
return ret;
}
if (pnctrl_info->pinctrl) {
ret = pinctrl_select_state(pnctrl_info->pinctrl,
pnctrl_info->active);
if (ret) {
pr_err("%s: active state select failed with %d\n",
__func__, ret);
return -EIO;
}
}
if (pnctrl_info->base)
iowrite32(1, pnctrl_info->base);
return 0;
}
static void audio_ext_lpass_mclk_unprepare(struct clk_hw *hw)
{
struct audio_ext_lpass_mclk *audio_lpass_mclk = to_audio_lpass_mclk(hw);
struct pinctrl_info *pnctrl_info = &audio_lpass_mclk->pnctrl_info;
int ret;
if (pnctrl_info->pinctrl) {
ret = pinctrl_select_state(pnctrl_info->pinctrl,
pnctrl_info->sleep);
if (ret) {
pr_err("%s: active state select failed with %d\n",
__func__, ret);
return;
}
}
lpass_mclk.enable = 0;
ret = afe_set_lpass_clock_v2(AFE_PORT_ID_PRIMARY_MI2S_RX,
&lpass_mclk);
if (ret < 0)
pr_err("%s: afe_set_digital_codec_core_clock failed, ret = %d\n",
__func__, ret);
if (pnctrl_info->base)
iowrite32(0, pnctrl_info->base);
}
static int audio_ext_lpass_mclk2_prepare(struct clk_hw *hw)
{
struct audio_ext_lpass_mclk *audio_lpass_mclk2 =
to_audio_lpass_mclk(hw);
struct pinctrl_info *pnctrl_info = &audio_lpass_mclk2->pnctrl_info;
int ret;
if (pnctrl_info->pinctrl) {
ret = pinctrl_select_state(pnctrl_info->pinctrl,
pnctrl_info->active);
if (ret) {
pr_err("%s: active state select failed with %d\n",
__func__, ret);
return -EIO;
}
}
lpass_default.enable = 1;
ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &lpass_default);
if (ret < 0) {
pr_err("%s: failed to set clock, ret = %d\n", __func__, ret);
return -EINVAL;
}
return 0;
}
static void audio_ext_lpass_mclk2_unprepare(struct clk_hw *hw)
{
struct audio_ext_lpass_mclk *audio_lpass_mclk2 =
to_audio_lpass_mclk(hw);
struct pinctrl_info *pnctrl_info = &audio_lpass_mclk2->pnctrl_info;
int ret;
if (pnctrl_info->pinctrl) {
ret = pinctrl_select_state(pnctrl_info->pinctrl,
pnctrl_info->sleep);
if (ret)
pr_err("%s: sleep state select failed with %d\n",
__func__, ret);
}
lpass_default.enable = 0;
ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &lpass_default);
if (ret < 0)
pr_err("%s: failed to reset clock, ret = %d\n", __func__, ret);
}
static const struct clk_ops audio_ext_ap_clk_ops = {
.prepare = audio_ext_clk_prepare,
.unprepare = audio_ext_clk_unprepare,
};
static const struct clk_ops audio_ext_ap_clk2_ops = {
.prepare = audio_ext_clk2_prepare,
.unprepare = audio_ext_clk2_unprepare,
};
static const struct clk_ops audio_ext_lpass_mclk_ops = {
.prepare = audio_ext_lpass_mclk_prepare,
.unprepare = audio_ext_lpass_mclk_unprepare,
};
static const struct clk_ops audio_ext_lpass_mclk2_ops = {
.prepare = audio_ext_lpass_mclk2_prepare,
.unprepare = audio_ext_lpass_mclk2_unprepare,
};
static struct audio_ext_pmi_clk audio_pmi_clk = {
.gpio = -EINVAL,
.fact = {
.mult = 1,
.div = 1,
.hw.init = &(struct clk_init_data){
.name = "audio_ext_pmi_clk",
.parent_names = (const char *[]){ "div_clk1" },
.num_parents = 1,
.ops = &clk_dummy_ops,
},
},
};
static struct audio_ext_pmi_clk audio_pmi_lnbb_clk = {
.gpio = -EINVAL,
.fact = {
.mult = 1,
.div = 1,
.hw.init = &(struct clk_init_data){
.name = "audio_ext_pmi_lnbb_clk",
.parent_names = (const char *[]){ "ln_bb_clk2" },
.num_parents = 1,
.ops = &clk_dummy_ops,
},
},
};
static struct audio_ext_ap_clk audio_ap_clk = {
.gpio = -EINVAL,
.fact = {
.mult = 1,
.div = 1,
.hw.init = &(struct clk_init_data){
.name = "audio_ap_clk",
.ops = &audio_ext_ap_clk_ops,
},
},
};
static struct audio_ext_ap_clk2 audio_ap_clk2 = {
.enabled = false,
.pnctrl_info = {NULL},
.fact = {
.mult = 1,
.div = 1,
.hw.init = &(struct clk_init_data){
.name = "audio_ap_clk2",
.ops = &audio_ext_ap_clk2_ops,
},
},
};
static struct audio_ext_lpass_mclk audio_lpass_mclk = {
.pnctrl_info = {NULL},
.fact = {
.mult = 1,
.div = 1,
.hw.init = &(struct clk_init_data){
.name = "audio_lpass_mclk",
.ops = &audio_ext_lpass_mclk_ops,
},
},
};
static struct audio_ext_lpass_mclk audio_lpass_mclk2 = {
.pnctrl_info = {NULL},
.fact = {
.mult = 1,
.div = 1,
.hw.init = &(struct clk_init_data){
.name = "audio_lpass_mclk2",
.ops = &audio_ext_lpass_mclk2_ops,
},
},
};
static struct clk_hw *audio_msm_hws[] = {
&audio_pmi_clk.fact.hw,
&audio_ap_clk.fact.hw,
&audio_ap_clk2.fact.hw,
&audio_lpass_mclk.fact.hw,
&audio_lpass_mclk2.fact.hw,
};
static struct clk_hw *audio_msm_hws1[] = {
&audio_pmi_lnbb_clk.fact.hw,
};
static int audio_get_pinctrl(struct platform_device *pdev,
enum audio_clk_mux mux)
{
struct device *dev = &pdev->dev;
struct pinctrl_info *pnctrl_info;
struct pinctrl *pinctrl;
int ret;
u32 reg;
switch (mux) {
case AP_CLK2:
pnctrl_info = &audio_ap_clk2.pnctrl_info;
break;
case LPASS_MCLK:
pnctrl_info = &audio_lpass_mclk.pnctrl_info;
break;
case LPASS_MCLK2:
pnctrl_info = &audio_lpass_mclk2.pnctrl_info;
break;
default:
dev_err(dev, "%s Not a valid MUX ID: %d\n",
__func__, mux);
return -EINVAL;
}
if (pnctrl_info->pinctrl) {
dev_dbg(dev, "%s: already requested before\n",
__func__);
return -EINVAL;
}
pinctrl = devm_pinctrl_get(dev);
if (IS_ERR_OR_NULL(pinctrl)) {
dev_dbg(dev, "%s: Unable to get pinctrl handle\n",
__func__);
return -EINVAL;
}
pnctrl_info->pinctrl = pinctrl;
/* get all state handles from Device Tree */
pnctrl_info->sleep = pinctrl_lookup_state(pinctrl, "sleep");
if (IS_ERR(pnctrl_info->sleep)) {
dev_err(dev, "%s: could not get sleep pinstate\n",
__func__);
goto err;
}
pnctrl_info->active = pinctrl_lookup_state(pinctrl, "active");
if (IS_ERR(pnctrl_info->active)) {
dev_err(dev, "%s: could not get active pinstate\n",
__func__);
goto err;
}
/* Reset the TLMM pins to a default state */
ret = pinctrl_select_state(pnctrl_info->pinctrl,
pnctrl_info->sleep);
if (ret) {
dev_err(dev, "%s: Disable TLMM pins failed with %d\n",
__func__, ret);
goto err;
}
ret = of_property_read_u32(dev->of_node, "qcom,mclk-clk-reg", &reg);
if (ret < 0) {
dev_dbg(dev, "%s: miss mclk reg\n", __func__);
} else {
pnctrl_info->base = ioremap(reg, sizeof(u32));
if (pnctrl_info->base == NULL) {
dev_err(dev, "%s ioremap failed\n", __func__);
goto err;
}
}
return 0;
err:
devm_pinctrl_put(pnctrl_info->pinctrl);
return -EINVAL;
}
static int audio_ref_clk_probe(struct platform_device *pdev)
{
int clk_gpio;
int ret;
u32 mclk_freq;
struct clk *audio_clk;
struct device *dev = &pdev->dev;
int i;
struct clk_onecell_data *clk_data;
ret = of_property_read_u32(pdev->dev.of_node,
"qcom,codec-mclk-clk-freq",
&mclk_freq);
if (!ret) {
lpass_mclk.clk_freq_in_hz = mclk_freq;
ret = audio_get_pinctrl(pdev, LPASS_MCLK);
if (ret)
dev_err(&pdev->dev, "%s: Parsing pinctrl %s failed\n",
__func__, "LPASS_MCLK");
ret = audio_get_pinctrl(pdev, LPASS_MCLK2);
if (ret)
dev_dbg(&pdev->dev, "%s: Parsing pinctrl %s failed\n",
__func__, "LPASS_MCLK2");
}
clk_gpio = of_get_named_gpio(pdev->dev.of_node,
"qcom,audio-ref-clk-gpio", 0);
if (clk_gpio > 0) {
ret = gpio_request(clk_gpio, "EXT_CLK");
if (ret) {
dev_err(&pdev->dev,
"Request ext clk gpio failed %d, err:%d\n",
clk_gpio, ret);
goto err;
}
if (of_property_read_bool(pdev->dev.of_node,
"qcom,node_has_rpm_clock")) {
audio_pmi_clk.gpio = clk_gpio;
} else
audio_ap_clk.gpio = clk_gpio;
}
ret = audio_get_pinctrl(pdev, AP_CLK2);
if (ret)
dev_dbg(&pdev->dev, "%s: Parsing pinctrl failed\n",
__func__);
clk_data = devm_kzalloc(&pdev->dev, sizeof(*clk_data), GFP_KERNEL);
if (!clk_data)
goto err_gpio;
clk_gpio = of_get_named_gpio(pdev->dev.of_node,
"qcom,audio-ref-clk-gpio", 0);
if (clk_gpio > 0) {
clk_data->clk_num = ARRAY_SIZE(audio_msm_hws);
clk_data->clks = devm_kzalloc(&pdev->dev,
clk_data->clk_num *
sizeof(struct clk *),
GFP_KERNEL);
if (!clk_data->clks)
goto err_clk;
for (i = 0; i < ARRAY_SIZE(audio_msm_hws); i++) {
audio_clk = devm_clk_register(dev, audio_msm_hws[i]);
if (IS_ERR(audio_clk)) {
dev_err(&pdev->dev,
"%s: ref clock: %d register failed\n",
__func__, i);
return PTR_ERR(audio_clk);
}
clk_data->clks[i] = audio_clk;
}
} else {
clk_data->clk_num = ARRAY_SIZE(audio_msm_hws1);
clk_data->clks = devm_kzalloc(&pdev->dev,
clk_data->clk_num *
sizeof(struct clk *),
GFP_KERNEL);
if (!clk_data->clks)
goto err_clk;
for (i = 0; i < ARRAY_SIZE(audio_msm_hws1); i++) {
audio_clk = devm_clk_register(dev, audio_msm_hws1[i]);
if (IS_ERR(audio_clk)) {
dev_err(&pdev->dev,
"%s: ref clock: %d register failed\n",
__func__, i);
return PTR_ERR(audio_clk);
}
clk_data->clks[i] = audio_clk;
}
}
ret = of_clk_add_provider(pdev->dev.of_node,
of_clk_src_onecell_get, clk_data);
if (ret) {
dev_err(&pdev->dev, "%s: audio ref clock register failed\n",
__func__);
goto err_gpio;
}
return 0;
err_clk:
if (clk_data)
devm_kfree(&pdev->dev, clk_data->clks);
devm_kfree(&pdev->dev, clk_data);
err_gpio:
gpio_free(clk_gpio);
err:
return ret;
}
static int audio_ref_clk_remove(struct platform_device *pdev)
{
struct pinctrl_info *pnctrl_info = &audio_ap_clk2.pnctrl_info;
if (audio_pmi_clk.gpio > 0)
gpio_free(audio_pmi_clk.gpio);
else if (audio_ap_clk.gpio > 0)
gpio_free(audio_ap_clk.gpio);
if (pnctrl_info->pinctrl) {
devm_pinctrl_put(pnctrl_info->pinctrl);
pnctrl_info->pinctrl = NULL;
}
return 0;
}
static const struct of_device_id audio_ref_clk_match[] = {
{.compatible = "qcom,audio-ref-clk"},
{}
};
MODULE_DEVICE_TABLE(of, audio_ref_clk_match);
static struct platform_driver audio_ref_clk_driver = {
.driver = {
.name = "audio-ref-clk",
.owner = THIS_MODULE,
.of_match_table = audio_ref_clk_match,
.suppress_bind_attrs = true,
},
.probe = audio_ref_clk_probe,
.remove = audio_ref_clk_remove,
};
int audio_ref_clk_platform_init(void)
{
return platform_driver_register(&audio_ref_clk_driver);
}
void audio_ref_clk_platform_exit(void)
{
platform_driver_unregister(&audio_ref_clk_driver);
}
MODULE_DESCRIPTION("Audio Ref Up Clock module platform driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,20 @@
/*
* Copyright (c) 2017, 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.
*/
#ifndef __AUDIO_EXT_CLK_UP_H_
#define __AUDIO_EXT_CLK_UP_H_
int audio_ref_clk_platform_init(void);
void audio_ref_clk_platform_exit(void);
#endif

View file

@ -0,0 +1,349 @@
/* Copyright (c) 2015-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/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/clk/msm-clk-provider.h>
#include <linux/clk/msm-clk.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <dt-bindings/clock/audio-ext-clk.h>
#include <sound/q6afe-v2.h>
#include "audio-ext-clk-up.h"
struct pinctrl_info {
struct pinctrl *pinctrl;
struct pinctrl_state *sleep;
struct pinctrl_state *active;
};
struct audio_ext_ap_clk {
bool enabled;
int gpio;
struct clk c;
};
struct audio_ext_pmi_clk {
int gpio;
struct clk c;
};
struct audio_ext_ap_clk2 {
bool enabled;
struct pinctrl_info pnctrl_info;
struct clk c;
};
static struct afe_clk_set clk2_config = {
Q6AFE_LPASS_CLK_CONFIG_API_VERSION,
Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR,
Q6AFE_LPASS_IBIT_CLK_11_P2896_MHZ,
Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
Q6AFE_LPASS_CLK_ROOT_DEFAULT,
0,
};
static inline struct audio_ext_ap_clk *to_audio_ap_clk(struct clk *clk)
{
return container_of(clk, struct audio_ext_ap_clk, c);
}
static int audio_ext_clk_prepare(struct clk *clk)
{
struct audio_ext_ap_clk *audio_clk = to_audio_ap_clk(clk);
pr_debug("%s: gpio: %d\n", __func__, audio_clk->gpio);
if (gpio_is_valid(audio_clk->gpio))
return gpio_direction_output(audio_clk->gpio, 1);
return 0;
}
static void audio_ext_clk_unprepare(struct clk *clk)
{
struct audio_ext_ap_clk *audio_clk = to_audio_ap_clk(clk);
pr_debug("%s: gpio: %d\n", __func__, audio_clk->gpio);
if (gpio_is_valid(audio_clk->gpio))
gpio_direction_output(audio_clk->gpio, 0);
}
static inline struct audio_ext_ap_clk2 *to_audio_ap_clk2(struct clk *clk)
{
return container_of(clk, struct audio_ext_ap_clk2, c);
}
static int audio_ext_clk2_prepare(struct clk *clk)
{
struct audio_ext_ap_clk2 *audio_clk2 = to_audio_ap_clk2(clk);
struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info;
int ret;
if (!pnctrl_info->pinctrl || !pnctrl_info->active)
return 0;
ret = pinctrl_select_state(pnctrl_info->pinctrl,
pnctrl_info->active);
if (ret) {
pr_err("%s: active state select failed with %d\n",
__func__, ret);
return -EIO;
}
clk2_config.enable = 1;
ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk2_config);
if (ret < 0) {
pr_err("%s: failed to set clock, ret = %d\n", __func__, ret);
return -EINVAL;
}
return 0;
}
static void audio_ext_clk2_unprepare(struct clk *clk)
{
struct audio_ext_ap_clk2 *audio_clk2 = to_audio_ap_clk2(clk);
struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info;
int ret;
if (!pnctrl_info->pinctrl || !pnctrl_info->sleep)
return;
ret = pinctrl_select_state(pnctrl_info->pinctrl,
pnctrl_info->sleep);
if (ret)
pr_err("%s: sleep state select failed with %d\n",
__func__, ret);
clk2_config.enable = 0;
ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk2_config);
if (ret < 0)
pr_err("%s: failed to reset clock, ret = %d\n", __func__, ret);
}
static const struct clk_ops audio_ext_ap_clk_ops = {
.prepare = audio_ext_clk_prepare,
.unprepare = audio_ext_clk_unprepare,
};
static const struct clk_ops audio_ext_ap_clk2_ops = {
.prepare = audio_ext_clk2_prepare,
.unprepare = audio_ext_clk2_unprepare,
};
static struct audio_ext_pmi_clk audio_pmi_clk = {
.gpio = -EINVAL,
.c = {
.dbg_name = "audio_ext_pmi_clk",
.ops = &clk_ops_dummy,
CLK_INIT(audio_pmi_clk.c),
},
};
static struct audio_ext_pmi_clk audio_pmi_lnbb_clk = {
.gpio = -EINVAL,
.c = {
.dbg_name = "audio_ext_pmi_lnbb_clk",
.ops = &clk_ops_dummy,
CLK_INIT(audio_pmi_lnbb_clk.c),
},
};
static struct audio_ext_ap_clk audio_ap_clk = {
.gpio = -EINVAL,
.c = {
.dbg_name = "audio_ext_ap_clk",
.ops = &audio_ext_ap_clk_ops,
CLK_INIT(audio_ap_clk.c),
},
};
static struct audio_ext_ap_clk2 audio_ap_clk2 = {
.c = {
.dbg_name = "audio_ext_ap_clk2",
.ops = &audio_ext_ap_clk2_ops,
CLK_INIT(audio_ap_clk2.c),
},
};
static struct clk_lookup audio_ref_clock[] = {
CLK_LIST(audio_ap_clk),
CLK_LIST(audio_pmi_clk),
CLK_LIST(audio_pmi_lnbb_clk),
CLK_LIST(audio_ap_clk2),
};
static int audio_get_pinctrl(struct platform_device *pdev)
{
struct pinctrl_info *pnctrl_info;
struct pinctrl *pinctrl;
int ret;
pnctrl_info = &audio_ap_clk2.pnctrl_info;
if (pnctrl_info->pinctrl) {
dev_dbg(&pdev->dev, "%s: already requested before\n",
__func__);
return -EINVAL;
}
pinctrl = devm_pinctrl_get(&pdev->dev);
if (IS_ERR_OR_NULL(pinctrl)) {
dev_dbg(&pdev->dev, "%s: Unable to get pinctrl handle\n",
__func__);
return -EINVAL;
}
pnctrl_info->pinctrl = pinctrl;
/* get all state handles from Device Tree */
pnctrl_info->sleep = pinctrl_lookup_state(pinctrl, "sleep");
if (IS_ERR(pnctrl_info->sleep)) {
dev_err(&pdev->dev, "%s: could not get sleep pinstate\n",
__func__);
goto err;
}
pnctrl_info->active = pinctrl_lookup_state(pinctrl, "active");
if (IS_ERR(pnctrl_info->active)) {
dev_err(&pdev->dev, "%s: could not get active pinstate\n",
__func__);
goto err;
}
/* Reset the TLMM pins to a default state */
ret = pinctrl_select_state(pnctrl_info->pinctrl,
pnctrl_info->sleep);
if (ret) {
dev_err(&pdev->dev, "%s: Disable TLMM pins failed with %d\n",
__func__, ret);
goto err;
}
return 0;
err:
devm_pinctrl_put(pnctrl_info->pinctrl);
return -EINVAL;
}
static int audio_ref_clk_probe(struct platform_device *pdev)
{
int clk_gpio;
int ret;
struct clk *audio_clk;
clk_gpio = of_get_named_gpio(pdev->dev.of_node,
"qcom,audio-ref-clk-gpio", 0);
if (clk_gpio > 0) {
ret = gpio_request(clk_gpio, "EXT_CLK");
if (ret) {
dev_err(&pdev->dev,
"Request ext clk gpio failed %d, err:%d\n",
clk_gpio, ret);
goto err;
}
if (of_property_read_bool(pdev->dev.of_node,
"qcom,node_has_rpm_clock")) {
audio_clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(audio_clk)) {
dev_err(&pdev->dev, "Failed to get RPM div clk\n");
ret = PTR_ERR(audio_clk);
goto err_gpio;
}
audio_pmi_clk.c.parent = audio_clk;
audio_pmi_clk.gpio = clk_gpio;
} else
audio_ap_clk.gpio = clk_gpio;
} else {
if (of_property_read_bool(pdev->dev.of_node,
"qcom,node_has_rpm_clock")) {
audio_clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(audio_clk)) {
dev_err(&pdev->dev, "Failed to get lnbbclk2\n");
ret = PTR_ERR(audio_clk);
goto err;
}
audio_pmi_lnbb_clk.c.parent = audio_clk;
audio_pmi_lnbb_clk.gpio = -EINVAL;
}
}
ret = audio_get_pinctrl(pdev);
if (ret)
dev_dbg(&pdev->dev, "%s: Parsing pinctrl failed\n",
__func__);
ret = of_msm_clock_register(pdev->dev.of_node, audio_ref_clock,
ARRAY_SIZE(audio_ref_clock));
if (ret) {
dev_err(&pdev->dev, "%s: audio ref clock register failed\n",
__func__);
goto err_gpio;
}
return 0;
err_gpio:
gpio_free(clk_gpio);
err:
return ret;
}
static int audio_ref_clk_remove(struct platform_device *pdev)
{
struct pinctrl_info *pnctrl_info = &audio_ap_clk2.pnctrl_info;
if (audio_pmi_clk.gpio > 0)
gpio_free(audio_pmi_clk.gpio);
else if (audio_ap_clk.gpio > 0)
gpio_free(audio_ap_clk.gpio);
if (pnctrl_info->pinctrl) {
devm_pinctrl_put(pnctrl_info->pinctrl);
pnctrl_info->pinctrl = NULL;
}
return 0;
}
static const struct of_device_id audio_ref_clk_match[] = {
{.compatible = "qcom,audio-ref-clk"},
{}
};
MODULE_DEVICE_TABLE(of, audio_ref_clk_match);
static struct platform_driver audio_ref_clk_driver = {
.driver = {
.name = "audio-ref-clk",
.owner = THIS_MODULE,
.of_match_table = audio_ref_clk_match,
.suppress_bind_attrs = true,
},
.probe = audio_ref_clk_probe,
.remove = audio_ref_clk_remove,
};
int audio_ref_clk_platform_init(void)
{
return platform_driver_register(&audio_ref_clk_driver);
}
void audio_ref_clk_platform_exit(void)
{
platform_driver_unregister(&audio_ref_clk_driver);
}
MODULE_DESCRIPTION("Audio Ref Clock module platform driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,440 @@
/* Copyright (c) 2011-2017, 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.
*/
#ifndef __MFD_TABLA_CORE_H__
#define __MFD_TABLA_CORE_H__
#include <linux/types.h>
#include <linux/platform_device.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/pm_qos.h>
#define WCD9XXX_MAX_IRQ_REGS 4
#define WCD9XXX_MAX_NUM_IRQS (WCD9XXX_MAX_IRQ_REGS * 8)
#define WCD9XXX_SLIM_NUM_PORT_REG 3
#define TABLA_VERSION_1_0 0
#define TABLA_VERSION_1_1 1
#define TABLA_VERSION_2_0 2
#define TABLA_IS_1_X(ver) \
(((ver == TABLA_VERSION_1_0) || (ver == TABLA_VERSION_1_1)) ? 1 : 0)
#define TABLA_IS_2_0(ver) ((ver == TABLA_VERSION_2_0) ? 1 : 0)
#define WCD9XXX_SUPPLY_BUCK_NAME "cdc-vdd-buck"
#define SITAR_VERSION_1P0 0
#define SITAR_VERSION_1P1 1
#define SITAR_IS_1P0(ver) \
((ver == SITAR_VERSION_1P0) ? 1 : 0)
#define SITAR_IS_1P1(ver) \
((ver == SITAR_VERSION_1P1) ? 1 : 0)
#define TAIKO_VERSION_1_0 1
#define TAIKO_IS_1_0(ver) \
((ver == TAIKO_VERSION_1_0) ? 1 : 0)
#define TAPAN_VERSION_1_0 0
#define TAPAN_IS_1_0(ver) \
((ver == TAPAN_VERSION_1_0) ? 1 : 0)
#define TOMTOM_VERSION_1_0 1
#define TOMTOM_IS_1_0(ver) \
((ver == TOMTOM_VERSION_1_0) ? 1 : 0)
#define TASHA_VERSION_1_0 0
#define TASHA_VERSION_1_1 1
#define TASHA_VERSION_2_0 2
#define TASHA_IS_1_0(wcd) \
((wcd->type == WCD9335 || wcd->type == WCD9326) ? \
((wcd->version == TASHA_VERSION_1_0) ? 1 : 0) : 0)
#define TASHA_IS_1_1(wcd) \
((wcd->type == WCD9335 || wcd->type == WCD9326) ? \
((wcd->version == TASHA_VERSION_1_1) ? 1 : 0) : 0)
#define TASHA_IS_2_0(wcd) \
((wcd->type == WCD9335 || wcd->type == WCD9326) ? \
((wcd->version == TASHA_VERSION_2_0) ? 1 : 0) : 0)
/*
* As fine version info cannot be retrieved before tavil probe.
* Define three coarse versions for possible future use before tavil probe.
*/
#define TAVIL_VERSION_1_0 0
#define TAVIL_VERSION_1_1 1
#define TAVIL_VERSION_WCD9340_1_0 2
#define TAVIL_VERSION_WCD9341_1_0 3
#define TAVIL_VERSION_WCD9340_1_1 4
#define TAVIL_VERSION_WCD9341_1_1 5
#define TAVIL_IS_1_0(wcd) \
((wcd->type == WCD934X) ? \
((wcd->version == TAVIL_VERSION_1_0 || \
wcd->version == TAVIL_VERSION_WCD9340_1_0 || \
wcd->version == TAVIL_VERSION_WCD9341_1_0) ? 1 : 0) : 0)
#define TAVIL_IS_1_1(wcd) \
((wcd->type == WCD934X) ? \
((wcd->version == TAVIL_VERSION_1_1 || \
wcd->version == TAVIL_VERSION_WCD9340_1_1 || \
wcd->version == TAVIL_VERSION_WCD9341_1_1) ? 1 : 0) : 0)
#define TAVIL_IS_WCD9340_1_0(wcd) \
((wcd->type == WCD934X) ? \
((wcd->version == TAVIL_VERSION_WCD9340_1_0) ? 1 : 0) : 0)
#define TAVIL_IS_WCD9341_1_0(wcd) \
((wcd->type == WCD934X) ? \
((wcd->version == TAVIL_VERSION_WCD9341_1_0) ? 1 : 0) : 0)
#define TAVIL_IS_WCD9340_1_1(wcd) \
((wcd->type == WCD934X) ? \
((wcd->version == TAVIL_VERSION_WCD9340_1_1) ? 1 : 0) : 0)
#define TAVIL_IS_WCD9341_1_1(wcd) \
((wcd->type == WCD934X) ? \
((wcd->version == TAVIL_VERSION_WCD9341_1_1) ? 1 : 0) : 0)
#define IS_CODEC_TYPE(wcd, wcdtype) \
((wcd->type == wcdtype) ? true : false)
#define IS_CODEC_VERSION(wcd, wcdversion) \
((wcd->version == wcdversion) ? true : false)
enum {
CDC_V_1_0,
CDC_V_1_1,
CDC_V_2_0,
};
enum codec_variant {
WCD9XXX,
WCD9330,
WCD9335,
WCD9326,
WCD934X,
};
enum wcd9xxx_slim_slave_addr_type {
WCD9XXX_SLIM_SLAVE_ADDR_TYPE_0,
WCD9XXX_SLIM_SLAVE_ADDR_TYPE_1,
};
enum wcd9xxx_pm_state {
WCD9XXX_PM_SLEEPABLE,
WCD9XXX_PM_AWAKE,
WCD9XXX_PM_ASLEEP,
};
enum {
WCD9XXX_INTR_STATUS_BASE = 0,
WCD9XXX_INTR_CLEAR_BASE,
WCD9XXX_INTR_MASK_BASE,
WCD9XXX_INTR_LEVEL_BASE,
WCD9XXX_INTR_CLR_COMMIT,
WCD9XXX_INTR_REG_MAX,
};
enum wcd9xxx_intf_status {
WCD9XXX_INTERFACE_TYPE_PROBING,
WCD9XXX_INTERFACE_TYPE_SLIMBUS,
WCD9XXX_INTERFACE_TYPE_I2C,
};
enum {
/* INTR_REG 0 */
WCD9XXX_IRQ_SLIMBUS = 0,
WCD9XXX_IRQ_MBHC_REMOVAL,
WCD9XXX_IRQ_MBHC_SHORT_TERM,
WCD9XXX_IRQ_MBHC_PRESS,
WCD9XXX_IRQ_MBHC_RELEASE,
WCD9XXX_IRQ_MBHC_POTENTIAL,
WCD9XXX_IRQ_MBHC_INSERTION,
WCD9XXX_IRQ_BG_PRECHARGE,
/* INTR_REG 1 */
WCD9XXX_IRQ_PA1_STARTUP,
WCD9XXX_IRQ_PA2_STARTUP,
WCD9XXX_IRQ_PA3_STARTUP,
WCD9XXX_IRQ_PA4_STARTUP,
WCD9306_IRQ_HPH_PA_OCPR_FAULT = WCD9XXX_IRQ_PA4_STARTUP,
WCD9XXX_IRQ_PA5_STARTUP,
WCD9XXX_IRQ_MICBIAS1_PRECHARGE,
WCD9306_IRQ_HPH_PA_OCPL_FAULT = WCD9XXX_IRQ_MICBIAS1_PRECHARGE,
WCD9XXX_IRQ_MICBIAS2_PRECHARGE,
WCD9XXX_IRQ_MICBIAS3_PRECHARGE,
/* INTR_REG 2 */
WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
WCD9XXX_IRQ_EAR_PA_OCPL_FAULT,
WCD9XXX_IRQ_HPH_L_PA_STARTUP,
WCD9XXX_IRQ_HPH_R_PA_STARTUP,
WCD9320_IRQ_EAR_PA_STARTUP,
WCD9306_IRQ_MBHC_JACK_SWITCH = WCD9320_IRQ_EAR_PA_STARTUP,
WCD9310_NUM_IRQS,
WCD9XXX_IRQ_RESERVED_0 = WCD9310_NUM_IRQS,
WCD9XXX_IRQ_RESERVED_1,
WCD9330_IRQ_SVASS_ERR_EXCEPTION = WCD9310_NUM_IRQS,
WCD9330_IRQ_MBHC_JACK_SWITCH,
/* INTR_REG 3 */
WCD9XXX_IRQ_MAD_AUDIO,
WCD9XXX_IRQ_MAD_ULTRASOUND,
WCD9XXX_IRQ_MAD_BEACON,
WCD9XXX_IRQ_SPEAKER_CLIPPING,
WCD9320_IRQ_MBHC_JACK_SWITCH,
WCD9306_NUM_IRQS,
WCD9XXX_IRQ_VBAT_MONITOR_ATTACK = WCD9306_NUM_IRQS,
WCD9XXX_IRQ_VBAT_MONITOR_RELEASE,
WCD9XXX_NUM_IRQS,
/* WCD9330 INTR1_REG 3*/
WCD9330_IRQ_SVASS_ENGINE = WCD9XXX_IRQ_MAD_AUDIO,
WCD9330_IRQ_MAD_AUDIO,
WCD9330_IRQ_MAD_ULTRASOUND,
WCD9330_IRQ_MAD_BEACON,
WCD9330_IRQ_SPEAKER1_CLIPPING,
WCD9330_IRQ_SPEAKER2_CLIPPING,
WCD9330_IRQ_VBAT_MONITOR_ATTACK,
WCD9330_IRQ_VBAT_MONITOR_RELEASE,
WCD9330_NUM_IRQS,
WCD9XXX_IRQ_RESERVED_2 = WCD9330_NUM_IRQS,
};
enum {
TABLA_NUM_IRQS = WCD9310_NUM_IRQS,
SITAR_NUM_IRQS = WCD9310_NUM_IRQS,
TAIKO_NUM_IRQS = WCD9XXX_NUM_IRQS,
TAPAN_NUM_IRQS = WCD9306_NUM_IRQS,
TOMTOM_NUM_IRQS = WCD9330_NUM_IRQS,
};
struct intr_data {
int intr_num;
bool clear_first;
};
struct wcd9xxx_core_resource {
struct mutex irq_lock;
struct mutex nested_irq_lock;
enum wcd9xxx_pm_state pm_state;
struct mutex pm_lock;
/* pm_wq notifies change of pm_state */
wait_queue_head_t pm_wq;
struct pm_qos_request pm_qos_req;
int wlock_holders;
/* holds the table of interrupts per codec */
const struct intr_data *intr_table;
int intr_table_size;
unsigned int irq_base;
unsigned int irq;
u8 irq_masks_cur[WCD9XXX_MAX_IRQ_REGS];
u8 irq_masks_cache[WCD9XXX_MAX_IRQ_REGS];
bool irq_level_high[WCD9XXX_MAX_NUM_IRQS];
int num_irqs;
int num_irq_regs;
u16 intr_reg[WCD9XXX_INTR_REG_MAX];
struct regmap *wcd_core_regmap;
/* Pointer to parent container data structure */
void *parent;
struct device *dev;
struct irq_domain *domain;
};
/*
* data structure for Slimbus and I2S channel.
* Some of fields are only used in smilbus mode
*/
struct wcd9xxx_ch {
u32 sph; /* share channel handle - slimbus only */
u32 ch_num; /*
* vitrual channel number, such as 128 -144.
* apply for slimbus only
*/
u16 ch_h; /* chanel handle - slimbus only */
u16 port; /*
* tabla port for RX and TX
* such as 0-9 for TX and 10 -16 for RX
* apply for both i2s and slimbus
*/
u16 shift; /*
* shift bit for RX and TX
* apply for both i2s and slimbus
*/
struct list_head list; /*
* channel link list
* apply for both i2s and slimbus
*/
};
struct wcd9xxx_codec_dai_data {
u32 rate; /* sample rate */
u32 bit_width; /* sit width 16,24,32 */
struct list_head wcd9xxx_ch_list; /* channel list */
u16 grph; /* slimbus group handle */
unsigned long ch_mask;
wait_queue_head_t dai_wait;
bool bus_down_in_recovery;
};
#define WCD9XXX_CH(xport, xshift) \
{.port = xport, .shift = xshift}
enum wcd9xxx_chipid_major {
TABLA_MAJOR = cpu_to_le16(0x100),
SITAR_MAJOR = cpu_to_le16(0x101),
TAIKO_MAJOR = cpu_to_le16(0x102),
TAPAN_MAJOR = cpu_to_le16(0x103),
TOMTOM_MAJOR = cpu_to_le16(0x105),
TASHA_MAJOR = cpu_to_le16(0x0),
TASHA2P0_MAJOR = cpu_to_le16(0x107),
TAVIL_MAJOR = cpu_to_le16(0x108),
};
enum codec_power_states {
WCD_REGION_POWER_COLLAPSE_REMOVE,
WCD_REGION_POWER_COLLAPSE_BEGIN,
WCD_REGION_POWER_DOWN,
};
enum wcd_power_regions {
WCD9XXX_DIG_CORE_REGION_1,
WCD9XXX_MAX_PWR_REGIONS,
};
struct wcd9xxx_codec_type {
u16 id_major;
u16 id_minor;
struct mfd_cell *dev;
int size;
int num_irqs;
int version; /* -1 to retrieve version from chip version register */
enum wcd9xxx_slim_slave_addr_type slim_slave_type;
u16 i2c_chip_status;
const struct intr_data *intr_tbl;
int intr_tbl_size;
u16 intr_reg[WCD9XXX_INTR_REG_MAX];
};
struct wcd9xxx_power_region {
enum codec_power_states power_state;
u16 pwr_collapse_reg_min;
u16 pwr_collapse_reg_max;
};
struct wcd9xxx {
struct device *dev;
struct slim_device *slim;
struct slim_device *slim_slave;
struct mutex io_lock;
struct mutex xfer_lock;
struct mutex reset_lock;
u8 version;
int reset_gpio;
struct device_node *wcd_rst_np;
int (*read_dev)(struct wcd9xxx *wcd9xxx, unsigned short reg,
int bytes, void *dest, bool interface_reg);
int (*write_dev)(struct wcd9xxx *wcd9xxx, unsigned short reg,
int bytes, void *src, bool interface_reg);
int (*multi_reg_write)(struct wcd9xxx *wcd9xxx, const void *data,
size_t count);
int (*dev_down)(struct wcd9xxx *wcd9xxx);
int (*post_reset)(struct wcd9xxx *wcd9xxx);
void *ssr_priv;
bool dev_up;
u32 num_of_supplies;
struct regulator_bulk_data *supplies;
struct wcd9xxx_core_resource core_res;
u16 id_minor;
u16 id_major;
/* Slimbus or I2S port */
u32 num_rx_port;
u32 num_tx_port;
struct wcd9xxx_ch *rx_chs;
struct wcd9xxx_ch *tx_chs;
u32 mclk_rate;
enum codec_variant type;
struct regmap *regmap;
struct wcd9xxx_codec_type *codec_type;
bool prev_pg_valid;
u8 prev_pg;
u8 avoid_cdc_rstlow;
struct wcd9xxx_power_region *wcd9xxx_pwr[WCD9XXX_MAX_PWR_REGIONS];
};
struct wcd9xxx_reg_val {
unsigned short reg; /* register address */
u8 *buf; /* buffer to be written to reg. addr */
int bytes; /* number of bytes to be written */
};
int wcd9xxx_interface_reg_read(struct wcd9xxx *wcd9xxx, unsigned short reg);
int wcd9xxx_interface_reg_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
u8 val);
int wcd9xxx_get_logical_addresses(u8 *pgd_la, u8 *inf_la);
int wcd9xxx_slim_write_repeat(struct wcd9xxx *wcd9xxx, unsigned short reg,
int bytes, void *src);
int wcd9xxx_slim_reserve_bw(struct wcd9xxx *wcd9xxx,
u32 bw_ops, bool commit);
int wcd9xxx_set_power_state(struct wcd9xxx *wcd9xxx, enum codec_power_states,
enum wcd_power_regions);
int wcd9xxx_get_current_power_state(struct wcd9xxx *wcd9xxx,
enum wcd_power_regions);
int wcd9xxx_page_write(struct wcd9xxx *wcd9xxx, unsigned short *reg);
int wcd9xxx_slim_bulk_write(struct wcd9xxx *wcd9xxx,
struct wcd9xxx_reg_val *bulk_reg,
unsigned int size, bool interface);
extern int wcd9xxx_core_res_init(
struct wcd9xxx_core_resource *wcd9xxx_core_res,
int num_irqs, int num_irq_regs, struct regmap *wcd_regmap);
extern void wcd9xxx_core_res_deinit(
struct wcd9xxx_core_resource *wcd9xxx_core_res);
extern int wcd9xxx_core_res_suspend(
struct wcd9xxx_core_resource *wcd9xxx_core_res,
pm_message_t pmesg);
extern int wcd9xxx_core_res_resume(
struct wcd9xxx_core_resource *wcd9xxx_core_res);
extern int wcd9xxx_core_irq_init(
struct wcd9xxx_core_resource *wcd9xxx_core_res);
extern int wcd9xxx_assign_irq(struct wcd9xxx_core_resource *wcd9xxx_core_res,
unsigned int irq,
unsigned int irq_base);
extern enum wcd9xxx_intf_status wcd9xxx_get_intf_type(void);
extern void wcd9xxx_set_intf_type(enum wcd9xxx_intf_status);
extern enum wcd9xxx_pm_state wcd9xxx_pm_cmpxchg(
struct wcd9xxx_core_resource *wcd9xxx_core_res,
enum wcd9xxx_pm_state o,
enum wcd9xxx_pm_state n);
static inline int __init wcd9xxx_irq_of_init(struct device_node *node,
struct device_node *parent)
{
return 0;
}
int wcd9xxx_init(void);
void wcd9xxx_exit(void);
#endif

View file

@ -0,0 +1,492 @@
/*
* Copyright (c) 2014-2016, 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.
*/
#ifndef __CPE_CMI_H__
#define __CPE_CMI_H__
#include <linux/types.h>
#define CPE_AFE_PORT_1_TX 1
#define CPE_AFE_PORT_3_TX 3
#define CPE_AFE_PORT_ID_2_OUT 0x02
#define CMI_INBAND_MESSAGE_SIZE 127
/*
* Multiple mad types can be supported at once.
* these values can be OR'ed to form the set of
* supported mad types
*/
#define MAD_TYPE_AUDIO (1 << 0)
#define MAD_TYPE_BEACON (1 << 1)
#define MAD_TYPE_ULTRASND (1 << 2)
/* Core service command opcodes */
#define CPE_CORE_SVC_CMD_SHARED_MEM_ALLOC (0x3001)
#define CPE_CORE_SVC_CMDRSP_SHARED_MEM_ALLOC (0x3002)
#define CPE_CORE_SVC_CMD_SHARED_MEM_DEALLOC (0x3003)
#define CPE_CORE_SVC_CMD_DRAM_ACCESS_REQ (0x3004)
#define CPE_CORE_SVC_EVENT_SYSTEM_BOOT (0x3005)
/* core service command opcodes for WCD9335 */
#define CPE_CORE_SVC_CMD_CFG_CLK_PLAN (0x3006)
#define CPE_CORE_SVC_CMD_CLK_FREQ_REQUEST (0x3007)
#define CPE_BOOT_SUCCESS 0x00
#define CPE_BOOT_FAILED 0x01
#define CPE_CORE_VERSION_SYSTEM_BOOT_EVENT 0x01
/* LSM Service command opcodes */
#define CPE_LSM_SESSION_CMD_OPEN_TX (0x2000)
#define CPE_LSM_SESSION_CMD_SET_PARAMS (0x2001)
#define CPE_LSM_SESSION_CMD_REGISTER_SOUND_MODEL (0x2002)
#define CPE_LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL (0x2003)
#define CPE_LSM_SESSION_CMD_START (0x2004)
#define CPE_LSM_SESSION_CMD_STOP (0x2005)
#define CPE_LSM_SESSION_EVENT_DETECTION_STATUS_V2 (0x2006)
#define CPE_LSM_SESSION_CMD_CLOSE_TX (0x2007)
#define CPE_LSM_SESSION_CMD_SHARED_MEM_ALLOC (0x2008)
#define CPE_LSM_SESSION_CMDRSP_SHARED_MEM_ALLOC (0x2009)
#define CPE_LSM_SESSION_CMD_SHARED_MEM_DEALLOC (0x200A)
#define CPE_LSM_SESSION_CMD_TX_BUFF_OUTPUT_CONFIG (0x200f)
#define CPE_LSM_SESSION_CMD_OPEN_TX_V2 (0x200D)
#define CPE_LSM_SESSION_CMD_SET_PARAMS_V2 (0x200E)
/* LSM Service module and param IDs */
#define CPE_LSM_MODULE_ID_VOICE_WAKEUP (0x00012C00)
#define CPE_LSM_MODULE_ID_VOICE_WAKEUP_V2 (0x00012C0D)
#define CPE_LSM_MODULE_FRAMEWORK (0x00012C0E)
#define CPE_LSM_PARAM_ID_ENDPOINT_DETECT_THRESHOLD (0x00012C01)
#define CPE_LSM_PARAM_ID_OPERATION_MODE (0x00012C02)
#define CPE_LSM_PARAM_ID_GAIN (0x00012C03)
#define CPE_LSM_PARAM_ID_CONNECT_TO_PORT (0x00012C04)
#define CPE_LSM_PARAM_ID_MIN_CONFIDENCE_LEVELS (0x00012C07)
/* LSM LAB command opcodes */
#define CPE_LSM_SESSION_CMD_EOB 0x0000200B
#define CPE_LSM_MODULE_ID_LAB 0x00012C08
/* used for enable/disable lab*/
#define CPE_LSM_PARAM_ID_LAB_ENABLE 0x00012C09
/* used for T in LAB config DSP internal buffer*/
#define CPE_LSM_PARAM_ID_LAB_CONFIG 0x00012C0A
#define CPE_LSM_PARAM_ID_REGISTER_SOUND_MODEL (0x00012C14)
#define CPE_LSM_PARAM_ID_DEREGISTER_SOUND_MODEL (0x00012C15)
#define CPE_LSM_PARAM_ID_MEDIA_FMT (0x00012C1E)
/* AFE Service command opcodes */
#define CPE_AFE_PORT_CMD_START (0x1001)
#define CPE_AFE_PORT_CMD_STOP (0x1002)
#define CPE_AFE_PORT_CMD_SUSPEND (0x1003)
#define CPE_AFE_PORT_CMD_RESUME (0x1004)
#define CPE_AFE_PORT_CMD_SHARED_MEM_ALLOC (0x1005)
#define CPE_AFE_PORT_CMDRSP_SHARED_MEM_ALLOC (0x1006)
#define CPE_AFE_PORT_CMD_SHARED_MEM_DEALLOC (0x1007)
#define CPE_AFE_PORT_CMD_GENERIC_CONFIG (0x1008)
#define CPE_AFE_SVC_CMD_LAB_MODE (0x1009)
/* AFE Service module and param IDs */
#define CPE_AFE_CMD_SET_PARAM (0x1000)
#define CPE_AFE_MODULE_ID_SW_MAD (0x0001022D)
#define CPE_AFE_PARAM_ID_SW_MAD_CFG (0x0001022E)
#define CPE_AFE_PARAM_ID_SVM_MODEL (0x0001022F)
#define CPE_AFE_MODULE_HW_MAD (0x00010230)
#define CPE_AFE_PARAM_ID_HW_MAD_CTL (0x00010232)
#define CPE_AFE_PARAM_ID_HW_MAD_CFG (0x00010231)
#define CPE_AFE_MODULE_AUDIO_DEV_INTERFACE (0x0001020C)
#define CPE_AFE_PARAM_ID_GENERIC_PORT_CONFIG (0x00010253)
#define CPE_CMI_BASIC_RSP_OPCODE (0x0001)
#define CPE_HDR_MAX_PLD_SIZE (0x7F)
#define CMI_OBM_FLAG_IN_BAND 0
#define CMI_OBM_FLAG_OUT_BAND 1
#define CMI_SHMEM_ALLOC_FAILED 0xff
/*
* Future Service ID's can be added one line
* before the CMI_CPE_SERVICE_ID_MAX
*/
enum {
CMI_CPE_SERVICE_ID_MIN = 0,
CMI_CPE_CORE_SERVICE_ID,
CMI_CPE_AFE_SERVICE_ID,
CMI_CPE_LSM_SERVICE_ID,
CMI_CPE_SERVICE_ID_MAX,
};
#define CPE_LSM_SESSION_ID_MAX 2
#define IS_VALID_SESSION_ID(s_id) \
(s_id <= CPE_LSM_SESSION_ID_MAX)
#define IS_VALID_SERVICE_ID(s_id) \
(s_id > CMI_CPE_SERVICE_ID_MIN && \
s_id < CMI_CPE_SERVICE_ID_MAX)
#define IS_VALID_PLD_SIZE(p_size) \
(p_size <= CPE_HDR_MAX_PLD_SIZE)
#define CMI_HDR_SET_OPCODE(hdr, cmd) (hdr->opcode = cmd)
#define CMI_HDR_SET(hdr_info, mask, shift, value) \
(hdr_info = (((hdr_info) & ~(mask)) | \
((value << shift) & mask)))
#define SVC_ID_SHIFT 4
#define SVC_ID_MASK (0x07 << SVC_ID_SHIFT)
#define SESSION_ID_SHIFT 0
#define SESSION_ID_MASK (0x0F << SESSION_ID_SHIFT)
#define PAYLD_SIZE_SHIFT 0
#define PAYLD_SIZE_MASK (0x7F << PAYLD_SIZE_SHIFT)
#define OBM_FLAG_SHIFT 7
#define OBM_FLAG_MASK (1 << OBM_FLAG_SHIFT)
#define VERSION_SHIFT 7
#define VERSION_MASK (1 << VERSION_SHIFT)
#define CMI_HDR_SET_SERVICE(hdr, s_id) \
CMI_HDR_SET(hdr->hdr_info, SVC_ID_MASK,\
SVC_ID_SHIFT, s_id)
#define CMI_HDR_GET_SERVICE(hdr) \
((hdr->hdr_info >> SVC_ID_SHIFT) & \
(SVC_ID_MASK >> SVC_ID_SHIFT))
#define CMI_HDR_SET_SESSION(hdr, s_id) \
CMI_HDR_SET(hdr->hdr_info, SESSION_ID_MASK,\
SESSION_ID_SHIFT, s_id)
#define CMI_HDR_GET_SESSION_ID(hdr) \
((hdr->hdr_info >> SESSION_ID_SHIFT) & \
(SESSION_ID_MASK >> SESSION_ID_SHIFT))
#define CMI_GET_HEADER(msg) ((struct cmi_hdr *)(msg))
#define CMI_GET_PAYLOAD(msg) ((void *)(CMI_GET_HEADER(msg) + 1))
#define CMI_GET_OPCODE(msg) (CMI_GET_HEADER(msg)->opcode)
#define CMI_HDR_SET_VERSION(hdr, ver) \
CMI_HDR_SET(hdr->hdr_info, VERSION_MASK, \
VERSION_SHIFT, ver)
#define CMI_HDR_SET_PAYLOAD_SIZE(hdr, p_size) \
CMI_HDR_SET(hdr->pld_info, PAYLD_SIZE_MASK, \
PAYLD_SIZE_SHIFT, p_size)
#define CMI_HDR_GET_PAYLOAD_SIZE(hdr) \
((hdr->pld_info >> PAYLD_SIZE_SHIFT) & \
(PAYLD_SIZE_MASK >> PAYLD_SIZE_SHIFT))
#define CMI_HDR_SET_OBM(hdr, obm_flag) \
CMI_HDR_SET(hdr->pld_info, OBM_FLAG_MASK, \
OBM_FLAG_SHIFT, obm_flag)
#define CMI_HDR_GET_OBM_FLAG(hdr) \
((hdr->pld_info >> OBM_FLAG_SHIFT) & \
(OBM_FLAG_MASK >> OBM_FLAG_SHIFT))
struct cmi_hdr {
/*
* bits 0:3 is session id
* bits 4:6 is service id
* bit 7 is the version flag
*/
u8 hdr_info;
/*
* bits 0:6 is payload size in case of in-band message
* bits 0:6 is size (OBM message size)
* bit 7 is the OBM flag
*/
u8 pld_info;
/* 16 bit command opcode */
u16 opcode;
} __packed;
union cpe_addr {
u64 msw_lsw;
void *kvaddr;
} __packed;
struct cmi_obm {
u32 version;
u32 size;
union cpe_addr data_ptr;
u32 mem_handle;
} __packed;
struct cmi_obm_msg {
struct cmi_hdr hdr;
struct cmi_obm pld;
} __packed;
struct cmi_core_svc_event_system_boot {
u8 status;
u8 version;
u16 sfr_buff_size;
u32 sfr_buff_address;
} __packed;
struct cmi_core_svc_cmd_shared_mem_alloc {
u32 size;
} __packed;
struct cmi_core_svc_cmdrsp_shared_mem_alloc {
u32 addr;
} __packed;
struct cmi_core_svc_cmd_clk_freq_request {
u32 clk_freq;
} __packed;
struct cmi_msg_transport {
u32 size;
u32 addr;
} __packed;
struct cmi_basic_rsp_result {
u8 status;
} __packed;
struct cpe_lsm_cmd_open_tx {
struct cmi_hdr hdr;
u16 app_id;
u16 reserved;
u32 sampling_rate;
} __packed;
struct cpe_lsm_cmd_open_tx_v2 {
struct cmi_hdr hdr;
u32 topology_id;
} __packed;
struct cpe_cmd_shmem_alloc {
struct cmi_hdr hdr;
u32 size;
} __packed;
struct cpe_cmdrsp_shmem_alloc {
struct cmi_hdr hdr;
u32 addr;
} __packed;
struct cpe_cmd_shmem_dealloc {
struct cmi_hdr hdr;
u32 addr;
} __packed;
struct cpe_lsm_event_detect_v2 {
struct cmi_hdr hdr;
u8 detection_status;
u8 size;
u8 payload[0];
} __packed;
struct cpe_lsm_psize_res {
u16 param_size;
u16 reserved;
} __packed;
union cpe_lsm_param_size {
u32 param_size;
struct cpe_lsm_psize_res sr;
} __packed;
struct cpe_param_data {
u32 module_id;
u32 param_id;
union cpe_lsm_param_size p_size;
} __packed;
struct cpe_lsm_param_epd_thres {
struct cmi_hdr hdr;
struct cpe_param_data param;
u32 minor_version;
u32 epd_begin;
u32 epd_end;
} __packed;
struct cpe_lsm_param_gain {
struct cmi_hdr hdr;
struct cpe_param_data param;
u32 minor_version;
u16 gain;
u16 reserved;
} __packed;
struct cpe_afe_hw_mad_ctrl {
struct cpe_param_data param;
u32 minor_version;
u16 mad_type;
u16 mad_enable;
} __packed;
struct cpe_afe_port_cfg {
struct cpe_param_data param;
u32 minor_version;
u16 bit_width;
u16 num_channels;
u32 sample_rate;
} __packed;
struct cpe_afe_cmd_port_cfg {
struct cmi_hdr hdr;
u8 bit_width;
u8 num_channels;
u16 buffer_size;
u32 sample_rate;
} __packed;
struct cpe_afe_params {
struct cmi_hdr hdr;
struct cpe_afe_hw_mad_ctrl hw_mad_ctrl;
struct cpe_afe_port_cfg port_cfg;
} __packed;
struct cpe_afe_svc_cmd_mode {
struct cmi_hdr hdr;
u8 mode;
} __packed;
struct cpe_lsm_param_opmode {
struct cmi_hdr hdr;
struct cpe_param_data param;
u32 minor_version;
u16 mode;
u16 reserved;
} __packed;
struct cpe_lsm_param_connectport {
struct cmi_hdr hdr;
struct cpe_param_data param;
u32 minor_version;
u16 afe_port_id;
u16 reserved;
} __packed;
/*
* This cannot be sent to CPE as is,
* need to append the conf_levels dynamically
*/
struct cpe_lsm_conf_level {
struct cmi_hdr hdr;
struct cpe_param_data param;
u8 num_active_models;
} __packed;
struct cpe_lsm_output_format_cfg {
struct cmi_hdr hdr;
u8 format;
u8 packing;
u8 data_path_events;
} __packed;
struct cpe_lsm_lab_enable {
struct cpe_param_data param;
u16 enable;
u16 reserved;
} __packed;
struct cpe_lsm_control_lab {
struct cmi_hdr hdr;
struct cpe_lsm_lab_enable lab_enable;
} __packed;
struct cpe_lsm_lab_config {
struct cpe_param_data param;
u32 minor_ver;
u32 latency;
} __packed;
struct cpe_lsm_lab_latency_config {
struct cmi_hdr hdr;
struct cpe_lsm_lab_config latency_cfg;
} __packed;
struct cpe_lsm_media_fmt_param {
struct cmi_hdr hdr;
struct cpe_param_data param;
u32 minor_version;
u32 sample_rate;
u16 num_channels;
u16 bit_width;
} __packed;
#define CPE_PARAM_LSM_LAB_LATENCY_SIZE (\
sizeof(struct cpe_lsm_lab_latency_config) - \
sizeof(struct cmi_hdr))
#define PARAM_SIZE_LSM_LATENCY_SIZE (\
sizeof(struct cpe_lsm_lab_config) - \
sizeof(struct cpe_param_data))
#define CPE_PARAM_SIZE_LSM_LAB_CONTROL (\
sizeof(struct cpe_lsm_control_lab) - \
sizeof(struct cmi_hdr))
#define PARAM_SIZE_LSM_CONTROL_SIZE (sizeof(struct cpe_lsm_lab_enable) - \
sizeof(struct cpe_param_data))
#define PARAM_SIZE_AFE_HW_MAD_CTRL (sizeof(struct cpe_afe_hw_mad_ctrl) - \
sizeof(struct cpe_param_data))
#define PARAM_SIZE_AFE_PORT_CFG (sizeof(struct cpe_afe_port_cfg) - \
sizeof(struct cpe_param_data))
#define CPE_AFE_PARAM_PAYLOAD_SIZE (sizeof(struct cpe_afe_params) - \
sizeof(struct cmi_hdr))
#define OPEN_CMD_PAYLOAD_SIZE (sizeof(struct cpe_lsm_cmd_open_tx) - \
sizeof(struct cmi_hdr))
#define OPEN_V2_CMD_PAYLOAD_SIZE (sizeof(struct cpe_lsm_cmd_open_tx_v2) - \
sizeof(struct cmi_hdr))
#define SHMEM_ALLOC_CMD_PLD_SIZE (sizeof(struct cpe_cmd_shmem_alloc) - \
sizeof(struct cmi_hdr))
#define SHMEM_DEALLOC_CMD_PLD_SIZE (sizeof(struct cpe_cmd_shmem_dealloc) - \
sizeof(struct cmi_hdr))
#define OUT_FMT_CFG_CMD_PAYLOAD_SIZE ( \
sizeof(struct cpe_lsm_output_format_cfg) - \
sizeof(struct cmi_hdr))
#define CPE_AFE_CMD_PORT_CFG_PAYLOAD_SIZE \
(sizeof(struct cpe_afe_cmd_port_cfg) - \
sizeof(struct cmi_hdr))
#define CPE_AFE_CMD_MODE_PAYLOAD_SIZE \
(sizeof(struct cpe_afe_svc_cmd_mode) - \
sizeof(struct cmi_hdr))
#define CPE_CMD_EPD_THRES_PLD_SIZE (sizeof(struct cpe_lsm_param_epd_thres) - \
sizeof(struct cmi_hdr))
#define CPE_EPD_THRES_PARAM_SIZE ((CPE_CMD_EPD_THRES_PLD_SIZE) - \
sizeof(struct cpe_param_data))
#define CPE_CMD_OPMODE_PLD_SIZE (sizeof(struct cpe_lsm_param_opmode) - \
sizeof(struct cmi_hdr))
#define CPE_OPMODE_PARAM_SIZE ((CPE_CMD_OPMODE_PLD_SIZE) -\
sizeof(struct cpe_param_data))
#define CPE_CMD_CONNECTPORT_PLD_SIZE \
(sizeof(struct cpe_lsm_param_connectport) - \
sizeof(struct cmi_hdr))
#define CPE_CONNECTPORT_PARAM_SIZE ((CPE_CMD_CONNECTPORT_PLD_SIZE) - \
sizeof(struct cpe_param_data))
#define CPE_CMD_GAIN_PLD_SIZE (sizeof(struct cpe_lsm_param_gain) - \
sizeof(struct cmi_hdr))
#define CPE_GAIN_PARAM_SIZE ((CPE_CMD_GAIN_PLD_SIZE) - \
sizeof(struct cpe_param_data))
#define CPE_MEDIA_FMT_PLD_SIZE (sizeof(struct cpe_lsm_media_fmt_param) - \
sizeof(struct cmi_hdr))
#define CPE_MEDIA_FMT_PARAM_SIZE ((CPE_MEDIA_FMT_PLD_SIZE) - \
sizeof(struct cpe_param_data))
#endif /* __CPE_CMI_H__ */

View file

@ -0,0 +1,179 @@
/*
* Copyright (c) 2013-2017, 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.
*/
#ifndef __CPE_CORE_H__
#define __CPE_CORE_H__
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/dma-mapping.h>
#include <sound/lsm_params.h>
enum {
CMD_INIT_STATE = 0,
CMD_SENT,
CMD_RESP_RCVD,
};
enum wcd_cpe_event {
WCD_CPE_PRE_ENABLE = 1,
WCD_CPE_POST_ENABLE,
WCD_CPE_PRE_DISABLE,
WCD_CPE_POST_DISABLE,
};
struct wcd_cpe_afe_port_cfg {
u8 port_id;
u16 bit_width;
u16 num_channels;
u32 sample_rate;
};
struct lsm_out_fmt_cfg {
u8 format;
u8 pack_mode;
u8 data_path_events;
u8 transfer_mode;
};
struct lsm_hw_params {
u32 sample_rate;
u16 num_chs;
u16 bit_width;
};
struct cpe_lsm_session {
/* sound model related */
void *snd_model_data;
u8 *conf_levels;
void *cmi_reg_handle;
/* Clients private data */
void *priv_d;
void (*event_cb)(void *priv_data,
u8 detect_status,
u8 size, u8 *payload);
struct completion cmd_comp;
struct wcd_cpe_afe_port_cfg afe_port_cfg;
struct wcd_cpe_afe_port_cfg afe_out_port_cfg;
struct mutex lsm_lock;
u32 snd_model_size;
u32 lsm_mem_handle;
u16 cmd_err_code;
u8 id;
u8 num_confidence_levels;
u16 afe_out_port_id;
struct task_struct *lsm_lab_thread;
bool started;
u32 lab_enable;
struct lsm_out_fmt_cfg out_fmt_cfg;
bool is_topology_used;
};
struct wcd_cpe_afe_ops {
int (*afe_set_params)(void *core_handle,
struct wcd_cpe_afe_port_cfg *cfg,
bool afe_mad_ctl);
int (*afe_port_start)(void *core_handle,
struct wcd_cpe_afe_port_cfg *cfg);
int (*afe_port_stop)(void *core_handle,
struct wcd_cpe_afe_port_cfg *cfg);
int (*afe_port_suspend)(void *core_handle,
struct wcd_cpe_afe_port_cfg *cfg);
int (*afe_port_resume)(void *core_handle,
struct wcd_cpe_afe_port_cfg *cfg);
int (*afe_port_cmd_cfg)(void *core_handle,
struct wcd_cpe_afe_port_cfg *cfg);
};
struct wcd_cpe_lsm_ops {
struct cpe_lsm_session *(*lsm_alloc_session)
(void *core_handle, void *lsm_priv_d,
void (*event_cb)(void *priv_data,
u8 detect_status,
u8 size, u8 *payload));
int (*lsm_dealloc_session)
(void *core_handle, struct cpe_lsm_session *);
int (*lsm_open_tx)(void *core_handle,
struct cpe_lsm_session *, u16, u16);
int (*lsm_close_tx)(void *core_handle,
struct cpe_lsm_session *);
int (*lsm_shmem_alloc)(void *core_handle,
struct cpe_lsm_session *, u32 size);
int (*lsm_shmem_dealloc)(void *core_handle,
struct cpe_lsm_session *);
int (*lsm_register_snd_model)(void *core_handle,
struct cpe_lsm_session *,
enum lsm_detection_mode, bool);
int (*lsm_deregister_snd_model)(void *core_handle,
struct cpe_lsm_session *);
int (*lsm_get_afe_out_port_id)(void *core_handle,
struct cpe_lsm_session *session);
int (*lsm_start)(void *core_handle,
struct cpe_lsm_session *);
int (*lsm_stop)(void *core_handle,
struct cpe_lsm_session *);
int (*lsm_lab_control)(void *core_handle,
struct cpe_lsm_session *session,
bool enable);
int (*lab_ch_setup)(void *core_handle,
struct cpe_lsm_session *session,
enum wcd_cpe_event event);
int (*lsm_set_data)(void *core_handle,
struct cpe_lsm_session *session,
enum lsm_detection_mode detect_mode,
bool detect_failure);
int (*lsm_set_fmt_cfg)(void *core_handle,
struct cpe_lsm_session *session);
int (*lsm_set_one_param)(void *core_handle,
struct cpe_lsm_session *session,
struct lsm_params_info *p_info,
void *data, uint32_t param_type);
void (*lsm_get_snd_model_offset)
(void *core_handle, struct cpe_lsm_session *,
size_t *offset);
int (*lsm_set_media_fmt_params)(void *core_handle,
struct cpe_lsm_session *session,
struct lsm_hw_params *param);
int (*lsm_set_port)(void *core_handle,
struct cpe_lsm_session *session, void *data);
};
int wcd_cpe_get_lsm_ops(struct wcd_cpe_lsm_ops *lsm_ops);
int wcd_cpe_get_afe_ops(struct wcd_cpe_afe_ops *afe_ops);
void *wcd_cpe_get_core_handle(struct snd_soc_codec *codec);
#endif

View file

@ -0,0 +1,166 @@
/*
* Copyright (c) 2015, 2017, 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.
*/
#ifndef __CPE_ERR__
#define __CPE_ERR__
#include <linux/errno.h>
/* ERROR CODES */
/* Success. The operation completed with no errors. */
#define CPE_EOK 0x00000000
/* General failure. */
#define CPE_EFAILED 0x00000001
/* Bad operation parameter. */
#define CPE_EBADPARAM 0x00000002
/* Unsupported routine or operation. */
#define CPE_EUNSUPPORTED 0x00000003
/* Unsupported version. */
#define CPE_EVERSION 0x00000004
/* Unexpected problem encountered. */
#define CPE_EUNEXPECTED 0x00000005
/* Unhandled problem occurred. */
#define CPE_EPANIC 0x00000006
/* Unable to allocate resource. */
#define CPE_ENORESOURCE 0x00000007
/* Invalid handle. */
#define CPE_EHANDLE 0x00000008
/* Operation is already processed. */
#define CPE_EALREADY 0x00000009
/* Operation is not ready to be processed. */
#define CPE_ENOTREADY 0x0000000A
/* Operation is pending completion. */
#define CPE_EPENDING 0x0000000B
/* Operation could not be accepted or processed. */
#define CPE_EBUSY 0x0000000C
/* Operation aborted due to an error. */
#define CPE_EABORTED 0x0000000D
/* Operation preempted by a higher priority. */
#define CPE_EPREEMPTED 0x0000000E
/* Operation requests intervention to complete. */
#define CPE_ECONTINUE 0x0000000F
/* Operation requests immediate intervention to complete. */
#define CPE_EIMMEDIATE 0x00000010
/* Operation is not implemented. */
#define CPE_ENOTIMPL 0x00000011
/* Operation needs more data or resources. */
#define CPE_ENEEDMORE 0x00000012
/* Operation does not have memory. */
#define CPE_ENOMEMORY 0x00000014
/* Item does not exist. */
#define CPE_ENOTEXIST 0x00000015
/* Operation is finished. */
#define CPE_ETERMINATED 0x00000016
/* Max count for adsp error code sent to HLOS*/
#define CPE_ERR_MAX (CPE_ETERMINATED + 1)
/* ERROR STRING */
/* Success. The operation completed with no errors. */
#define CPE_EOK_STR "CPE_EOK"
/* General failure. */
#define CPE_EFAILED_STR "CPE_EFAILED"
/* Bad operation parameter. */
#define CPE_EBADPARAM_STR "CPE_EBADPARAM"
/* Unsupported routine or operation. */
#define CPE_EUNSUPPORTED_STR "CPE_EUNSUPPORTED"
/* Unsupported version. */
#define CPE_EVERSION_STR "CPE_EVERSION"
/* Unexpected problem encountered. */
#define CPE_EUNEXPECTED_STR "CPE_EUNEXPECTED"
/* Unhandled problem occurred. */
#define CPE_EPANIC_STR "CPE_EPANIC"
/* Unable to allocate resource. */
#define CPE_ENORESOURCE_STR "CPE_ENORESOURCE"
/* Invalid handle. */
#define CPE_EHANDLE_STR "CPE_EHANDLE"
/* Operation is already processed. */
#define CPE_EALREADY_STR "CPE_EALREADY"
/* Operation is not ready to be processed. */
#define CPE_ENOTREADY_STR "CPE_ENOTREADY"
/* Operation is pending completion. */
#define CPE_EPENDING_STR "CPE_EPENDING"
/* Operation could not be accepted or processed. */
#define CPE_EBUSY_STR "CPE_EBUSY"
/* Operation aborted due to an error. */
#define CPE_EABORTED_STR "CPE_EABORTED"
/* Operation preempted by a higher priority. */
#define CPE_EPREEMPTED_STR "CPE_EPREEMPTED"
/* Operation requests intervention to complete. */
#define CPE_ECONTINUE_STR "CPE_ECONTINUE"
/* Operation requests immediate intervention to complete. */
#define CPE_EIMMEDIATE_STR "CPE_EIMMEDIATE"
/* Operation is not implemented. */
#define CPE_ENOTIMPL_STR "CPE_ENOTIMPL"
/* Operation needs more data or resources. */
#define CPE_ENEEDMORE_STR "CPE_ENEEDMORE"
/* Operation does not have memory. */
#define CPE_ENOMEMORY_STR "CPE_ENOMEMORY"
/* Item does not exist. */
#define CPE_ENOTEXIST_STR "CPE_ENOTEXIST"
/* Operation is finished. */
#define CPE_ETERMINATED_STR "CPE_ETERMINATED"
/* Unexpected error code. */
#define CPE_ERR_MAX_STR "CPE_ERR_MAX"
struct cpe_err_code {
int lnx_err_code;
char *cpe_err_str;
};
static struct cpe_err_code cpe_err_code_info[CPE_ERR_MAX+1] = {
{ 0, CPE_EOK_STR},
{ -ENOTRECOVERABLE, CPE_EFAILED_STR},
{ -EINVAL, CPE_EBADPARAM_STR},
{ -EOPNOTSUPP, CPE_EUNSUPPORTED_STR},
{ -ENOPROTOOPT, CPE_EVERSION_STR},
{ -ENOTRECOVERABLE, CPE_EUNEXPECTED_STR},
{ -ENOTRECOVERABLE, CPE_EPANIC_STR},
{ -ENOSPC, CPE_ENORESOURCE_STR},
{ -EBADR, CPE_EHANDLE_STR},
{ -EALREADY, CPE_EALREADY_STR},
{ -EPERM, CPE_ENOTREADY_STR},
{ -EINPROGRESS, CPE_EPENDING_STR},
{ -EBUSY, CPE_EBUSY_STR},
{ -ECANCELED, CPE_EABORTED_STR},
{ -EAGAIN, CPE_EPREEMPTED_STR},
{ -EAGAIN, CPE_ECONTINUE_STR},
{ -EAGAIN, CPE_EIMMEDIATE_STR},
{ -EAGAIN, CPE_ENOTIMPL_STR},
{ -ENODATA, CPE_ENEEDMORE_STR},
{ -EADV, CPE_ERR_MAX_STR},
{ -ENOMEM, CPE_ENOMEMORY_STR},
{ -ENODEV, CPE_ENOTEXIST_STR},
{ -EADV, CPE_ETERMINATED_STR},
{ -EADV, CPE_ERR_MAX_STR},
};
static inline int cpe_err_get_lnx_err_code(u32 cpe_error)
{
if (cpe_error > CPE_ERR_MAX)
return cpe_err_code_info[CPE_ERR_MAX].lnx_err_code;
else
return cpe_err_code_info[cpe_error].lnx_err_code;
}
static inline char *cpe_err_get_err_str(u32 cpe_error)
{
if (cpe_error > CPE_ERR_MAX)
return cpe_err_code_info[CPE_ERR_MAX].cpe_err_str;
else
return cpe_err_code_info[cpe_error].cpe_err_str;
}
#endif

View file

@ -0,0 +1,260 @@
/* Copyright (c) 2016-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/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include "msm-cdc-pinctrl.h"
struct msm_cdc_pinctrl_info {
struct pinctrl *pinctrl;
struct pinctrl_state *pinctrl_active;
struct pinctrl_state *pinctrl_sleep;
int gpio;
bool state;
};
static struct msm_cdc_pinctrl_info *msm_cdc_pinctrl_get_gpiodata(
struct device_node *np)
{
struct platform_device *pdev;
struct msm_cdc_pinctrl_info *gpio_data;
if (!np) {
pr_err("%s: device node is null\n", __func__);
return NULL;
}
pdev = of_find_device_by_node(np);
if (!pdev) {
pr_err("%s: platform device not found!\n", __func__);
return NULL;
}
gpio_data = dev_get_drvdata(&pdev->dev);
if (!gpio_data)
dev_err(&pdev->dev, "%s: cannot find cdc gpio info\n",
__func__);
return gpio_data;
}
/*
* msm_cdc_get_gpio_state: select pinctrl sleep state
* @np: pointer to struct device_node
*
* Returns error code for failure and GPIO value on success
*/
int msm_cdc_get_gpio_state(struct device_node *np)
{
struct msm_cdc_pinctrl_info *gpio_data;
int value = -EINVAL;
gpio_data = msm_cdc_pinctrl_get_gpiodata(np);
if (!gpio_data)
return value;
if (gpio_is_valid(gpio_data->gpio))
value = gpio_get_value_cansleep(gpio_data->gpio);
return value;
}
EXPORT_SYMBOL(msm_cdc_get_gpio_state);
/*
* msm_cdc_pinctrl_select_sleep_state: select pinctrl sleep state
* @np: pointer to struct device_node
*
* Returns error code for failure
*/
int msm_cdc_pinctrl_select_sleep_state(struct device_node *np)
{
struct msm_cdc_pinctrl_info *gpio_data;
gpio_data = msm_cdc_pinctrl_get_gpiodata(np);
if (!gpio_data)
return -EINVAL;
if (!gpio_data->pinctrl_sleep) {
pr_err("%s: pinctrl sleep state is null\n", __func__);
return -EINVAL;
}
gpio_data->state = false;
return pinctrl_select_state(gpio_data->pinctrl,
gpio_data->pinctrl_sleep);
}
EXPORT_SYMBOL(msm_cdc_pinctrl_select_sleep_state);
/*
* msm_cdc_pinctrl_select_active_state: select pinctrl active state
* @np: pointer to struct device_node
*
* Returns error code for failure
*/
int msm_cdc_pinctrl_select_active_state(struct device_node *np)
{
struct msm_cdc_pinctrl_info *gpio_data;
gpio_data = msm_cdc_pinctrl_get_gpiodata(np);
if (!gpio_data)
return -EINVAL;
if (!gpio_data->pinctrl_active) {
pr_err("%s: pinctrl active state is null\n", __func__);
return -EINVAL;
}
gpio_data->state = true;
return pinctrl_select_state(gpio_data->pinctrl,
gpio_data->pinctrl_active);
}
EXPORT_SYMBOL(msm_cdc_pinctrl_select_active_state);
/*
* msm_cdc_pinctrl_get_state: get curren pinctrl state
* @np: pointer to struct device_node
*
* Returns 0 for sleep state, 1 for active state
*/
bool msm_cdc_pinctrl_get_state(struct device_node *np)
{
struct msm_cdc_pinctrl_info *gpio_data;
gpio_data = msm_cdc_pinctrl_get_gpiodata(np);
if (!gpio_data)
return -EINVAL;
return gpio_data->state;
}
EXPORT_SYMBOL(msm_cdc_pinctrl_get_state);
static int msm_cdc_pinctrl_probe(struct platform_device *pdev)
{
int ret = 0;
struct msm_cdc_pinctrl_info *gpio_data;
gpio_data = devm_kzalloc(&pdev->dev,
sizeof(struct msm_cdc_pinctrl_info),
GFP_KERNEL);
if (!gpio_data)
return -ENOMEM;
gpio_data->pinctrl = devm_pinctrl_get(&pdev->dev);
if (IS_ERR_OR_NULL(gpio_data->pinctrl)) {
dev_err(&pdev->dev, "%s: Cannot get cdc gpio pinctrl:%ld\n",
__func__, PTR_ERR(gpio_data->pinctrl));
ret = PTR_ERR(gpio_data->pinctrl);
goto err_pctrl_get;
}
gpio_data->pinctrl_active = pinctrl_lookup_state(
gpio_data->pinctrl, "aud_active");
if (IS_ERR_OR_NULL(gpio_data->pinctrl_active)) {
dev_err(&pdev->dev, "%s: Cannot get aud_active pinctrl state:%ld\n",
__func__, PTR_ERR(gpio_data->pinctrl_active));
ret = PTR_ERR(gpio_data->pinctrl_active);
goto err_lookup_state;
}
gpio_data->pinctrl_sleep = pinctrl_lookup_state(
gpio_data->pinctrl, "aud_sleep");
if (IS_ERR_OR_NULL(gpio_data->pinctrl_sleep)) {
dev_err(&pdev->dev, "%s: Cannot get aud_sleep pinctrl state:%ld\n",
__func__, PTR_ERR(gpio_data->pinctrl_sleep));
ret = PTR_ERR(gpio_data->pinctrl_sleep);
goto err_lookup_state;
}
/* skip setting to sleep state for LPI_TLMM GPIOs */
if (!of_property_read_bool(pdev->dev.of_node, "qcom,lpi-gpios")) {
/* Set pinctrl state to aud_sleep by default */
ret = pinctrl_select_state(gpio_data->pinctrl,
gpio_data->pinctrl_sleep);
if (ret)
dev_err(&pdev->dev, "%s: set cdc gpio sleep state fail: %d\n",
__func__, ret);
}
gpio_data->gpio = of_get_named_gpio(pdev->dev.of_node,
"qcom,cdc-rst-n-gpio", 0);
if (gpio_is_valid(gpio_data->gpio)) {
ret = gpio_request(gpio_data->gpio, "MSM_CDC_RESET");
if (ret) {
dev_err(&pdev->dev, "%s: Failed to request gpio %d\n",
__func__, gpio_data->gpio);
goto err_lookup_state;
}
}
dev_set_drvdata(&pdev->dev, gpio_data);
return 0;
err_lookup_state:
devm_pinctrl_put(gpio_data->pinctrl);
err_pctrl_get:
devm_kfree(&pdev->dev, gpio_data);
return ret;
}
static int msm_cdc_pinctrl_remove(struct platform_device *pdev)
{
struct msm_cdc_pinctrl_info *gpio_data;
gpio_data = dev_get_drvdata(&pdev->dev);
/* to free the requested gpio before exiting */
if (gpio_data) {
if (gpio_is_valid(gpio_data->gpio))
gpio_free(gpio_data->gpio);
if (gpio_data->pinctrl)
devm_pinctrl_put(gpio_data->pinctrl);
}
devm_kfree(&pdev->dev, gpio_data);
return 0;
}
static const struct of_device_id msm_cdc_pinctrl_match[] = {
{.compatible = "qcom,msm-cdc-pinctrl"},
{}
};
static struct platform_driver msm_cdc_pinctrl_driver = {
.driver = {
.name = "msm-cdc-pinctrl",
.owner = THIS_MODULE,
.of_match_table = msm_cdc_pinctrl_match,
.suppress_bind_attrs = true,
},
.probe = msm_cdc_pinctrl_probe,
.remove = msm_cdc_pinctrl_remove,
};
int msm_cdc_pinctrl_drv_init(void)
{
return platform_driver_register(&msm_cdc_pinctrl_driver);
}
void msm_cdc_pinctrl_drv_exit(void)
{
platform_driver_unregister(&msm_cdc_pinctrl_driver);
}
MODULE_DESCRIPTION("MSM CODEC pin control platform driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,49 @@
/* Copyright (c) 2016-2017, 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.
*/
#ifndef __MFD_CDC_PINCTRL_H_
#define __MFD_CDC_PINCTRL_H_
#include <linux/types.h>
#include <linux/of.h>
#if IS_ENABLED(CONFIG_MSM_CDC_PINCTRL)
extern int msm_cdc_pinctrl_select_sleep_state(struct device_node *np);
extern int msm_cdc_pinctrl_select_active_state(struct device_node *np);
extern bool msm_cdc_pinctrl_get_state(struct device_node *np);
extern int msm_cdc_get_gpio_state(struct device_node *np);
int msm_cdc_pinctrl_drv_init(void);
void msm_cdc_pinctrl_drv_exit(void);
#else
int msm_cdc_pinctrl_select_sleep_state(struct device_node *np)
{
return 0;
}
int msm_cdc_pinctrl_select_active_state(struct device_node *np)
{
return 0;
}
int msm_cdc_get_gpio_state(struct device_node *np)
{
return 0;
}
int msm_cdc_pinctrl_drv_init(void)
{
return 0;
}
void msm_cdc_pinctrl_drv_exit(void)
{
}
#endif
#endif

View file

@ -0,0 +1,456 @@
/*
* Copyright (c) 2016-2017, 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/kernel.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/of_device.h>
#include <linux/slab.h>
#include "msm-cdc-supply.h"
#include <linux/regulator/consumer.h>
#define CODEC_DT_MAX_PROP_SIZE 40
static int msm_cdc_dt_parse_vreg_info(struct device *dev,
struct cdc_regulator *cdc_vreg,
const char *name, bool is_ond)
{
char prop_name[CODEC_DT_MAX_PROP_SIZE];
struct device_node *regulator_node = NULL;
const __be32 *prop;
int len, rc;
u32 prop_val;
/* Parse supply name */
snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "%s-supply", name);
regulator_node = of_parse_phandle(dev->of_node, prop_name, 0);
if (!regulator_node) {
dev_err(dev, "%s: Looking up %s property in node %s failed",
__func__, prop_name, dev->of_node->full_name);
rc = -EINVAL;
goto done;
}
cdc_vreg->name = name;
cdc_vreg->ondemand = is_ond;
/* Parse supply - voltage */
snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "qcom,%s-voltage", name);
prop = of_get_property(dev->of_node, prop_name, &len);
if (!prop || (len != (2 * sizeof(__be32)))) {
dev_err(dev, "%s: %s %s property\n", __func__,
prop ? "invalid format" : "no", prop_name);
rc = -EINVAL;
goto done;
} else {
cdc_vreg->min_uV = be32_to_cpup(&prop[0]);
cdc_vreg->max_uV = be32_to_cpup(&prop[1]);
}
/* Parse supply - current */
snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "qcom,%s-current", name);
rc = of_property_read_u32(dev->of_node, prop_name, &prop_val);
if (rc) {
dev_err(dev, "%s: Looking up %s property in node %s failed",
__func__, prop_name, dev->of_node->full_name);
goto done;
}
cdc_vreg->optimum_uA = prop_val;
dev_info(dev, "%s: %s: vol=[%d %d]uV, curr=[%d]uA, ond %d\n",
__func__, cdc_vreg->name, cdc_vreg->min_uV, cdc_vreg->max_uV,
cdc_vreg->optimum_uA, cdc_vreg->ondemand);
done:
return rc;
}
static int msm_cdc_parse_supplies(struct device *dev,
struct cdc_regulator *cdc_reg,
const char *sup_list, int sup_cnt,
bool is_ond)
{
int idx, rc = 0;
const char *name = NULL;
for (idx = 0; idx < sup_cnt; idx++) {
rc = of_property_read_string_index(dev->of_node, sup_list, idx,
&name);
if (rc) {
dev_err(dev, "%s: read string %s[%d] error (%d)\n",
__func__, sup_list, idx, rc);
goto done;
}
dev_dbg(dev, "%s: Found cdc supply %s as part of %s\n",
__func__, name, sup_list);
rc = msm_cdc_dt_parse_vreg_info(dev, &cdc_reg[idx], name,
is_ond);
if (rc) {
dev_err(dev, "%s: parse %s vreg info failed (%d)\n",
__func__, name, rc);
goto done;
}
}
done:
return rc;
}
static int msm_cdc_check_supply_param(struct device *dev,
struct cdc_regulator *cdc_vreg,
int num_supplies)
{
if (!dev) {
pr_err("%s: device is NULL\n", __func__);
return -ENODEV;
}
if (!cdc_vreg || (num_supplies <= 0)) {
dev_err(dev, "%s: supply check failed: vreg: %pK, num_supplies: %d\n",
__func__, cdc_vreg, num_supplies);
return -EINVAL;
}
return 0;
}
/*
* msm_cdc_disable_static_supplies:
* Disable codec static supplies
*
* @dev: pointer to codec device
* @supplies: pointer to regulator bulk data
* @cdc_vreg: pointer to platform regulator data
* @num_supplies: number of supplies
*
* Return error code if supply disable is failed
*/
int msm_cdc_disable_static_supplies(struct device *dev,
struct regulator_bulk_data *supplies,
struct cdc_regulator *cdc_vreg,
int num_supplies)
{
int rc, i;
if ((!dev) || (!supplies) || (!cdc_vreg)) {
pr_err("%s: either dev or supplies or cdc_vreg is NULL\n",
__func__);
return -EINVAL;
}
/* input parameter validation */
rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies);
if (rc)
return rc;
for (i = 0; i < num_supplies; i++) {
if (cdc_vreg[i].ondemand)
continue;
rc = regulator_disable(supplies[i].consumer);
if (rc)
dev_err(dev, "%s: failed to disable supply %s, err:%d\n",
__func__, supplies[i].supply, rc);
else
dev_dbg(dev, "%s: disabled regulator %s\n",
__func__, supplies[i].supply);
}
return rc;
}
EXPORT_SYMBOL(msm_cdc_disable_static_supplies);
/*
* msm_cdc_release_supplies:
* Release codec power supplies
*
* @dev: pointer to codec device
* @supplies: pointer to regulator bulk data
* @cdc_vreg: pointer to platform regulator data
* @num_supplies: number of supplies
*
* Return error code if supply disable is failed
*/
int msm_cdc_release_supplies(struct device *dev,
struct regulator_bulk_data *supplies,
struct cdc_regulator *cdc_vreg,
int num_supplies)
{
int rc = 0;
int i;
if ((!dev) || (!supplies) || (!cdc_vreg)) {
pr_err("%s: either dev or supplies or cdc_vreg is NULL\n",
__func__);
return -EINVAL;
}
/* input parameter validation */
rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies);
if (rc)
return rc;
msm_cdc_disable_static_supplies(dev, supplies, cdc_vreg,
num_supplies);
for (i = 0; i < num_supplies; i++) {
if (regulator_count_voltages(supplies[i].consumer) < 0)
continue;
regulator_set_voltage(supplies[i].consumer, 0,
cdc_vreg[i].max_uV);
regulator_set_load(supplies[i].consumer, 0);
devm_regulator_put(supplies[i].consumer);
supplies[i].consumer = NULL;
}
devm_kfree(dev, supplies);
return rc;
}
EXPORT_SYMBOL(msm_cdc_release_supplies);
/*
* msm_cdc_enable_static_supplies:
* Enable codec static supplies
*
* @dev: pointer to codec device
* @supplies: pointer to regulator bulk data
* @cdc_vreg: pointer to platform regulator data
* @num_supplies: number of supplies
*
* Return error code if supply enable is failed
*/
int msm_cdc_enable_static_supplies(struct device *dev,
struct regulator_bulk_data *supplies,
struct cdc_regulator *cdc_vreg,
int num_supplies)
{
int rc, i;
if ((!dev) || (!supplies) || (!cdc_vreg)) {
pr_err("%s: either dev or supplies or cdc_vreg is NULL\n",
__func__);
return -EINVAL;
}
/* input parameter validation */
rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies);
if (rc)
return rc;
for (i = 0; i < num_supplies; i++) {
if (cdc_vreg[i].ondemand)
continue;
rc = regulator_enable(supplies[i].consumer);
if (rc) {
dev_err(dev, "%s: failed to enable supply %s, rc: %d\n",
__func__, supplies[i].supply, rc);
break;
}
}
while (rc && i--)
if (!cdc_vreg[i].ondemand)
regulator_disable(supplies[i].consumer);
return rc;
}
EXPORT_SYMBOL(msm_cdc_enable_static_supplies);
/*
* msm_cdc_init_supplies:
* Initialize codec static supplies with regulator get
*
* @dev: pointer to codec device
* @supplies: pointer to regulator bulk data
* @cdc_vreg: pointer to platform regulator data
* @num_supplies: number of supplies
*
* Return error code if supply init is failed
*/
int msm_cdc_init_supplies(struct device *dev,
struct regulator_bulk_data **supplies,
struct cdc_regulator *cdc_vreg,
int num_supplies)
{
struct regulator_bulk_data *vsup;
int rc;
int i;
if (!dev || !cdc_vreg) {
pr_err("%s: device pointer or dce_vreg is NULL\n",
__func__);
return -EINVAL;
}
/* input parameter validation */
rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies);
if (rc)
return rc;
vsup = devm_kcalloc(dev, num_supplies,
sizeof(struct regulator_bulk_data),
GFP_KERNEL);
if (!vsup)
return -ENOMEM;
for (i = 0; i < num_supplies; i++) {
if (!cdc_vreg[i].name) {
dev_err(dev, "%s: supply name not defined\n",
__func__);
rc = -EINVAL;
goto err_supply;
}
vsup[i].supply = cdc_vreg[i].name;
}
rc = devm_regulator_bulk_get(dev, num_supplies, vsup);
if (rc) {
dev_err(dev, "%s: failed to get supplies (%d)\n",
__func__, rc);
goto err_supply;
}
/* Set voltage and current on regulators */
for (i = 0; i < num_supplies; i++) {
if (regulator_count_voltages(vsup[i].consumer) < 0)
continue;
rc = regulator_set_voltage(vsup[i].consumer,
cdc_vreg[i].min_uV,
cdc_vreg[i].max_uV);
if (rc) {
dev_err(dev, "%s: set regulator voltage failed for %s, err:%d\n",
__func__, vsup[i].supply, rc);
goto err_set_supply;
}
rc = regulator_set_load(vsup[i].consumer,
cdc_vreg[i].optimum_uA);
if (rc < 0) {
dev_err(dev, "%s: set regulator optimum mode failed for %s, err:%d\n",
__func__, vsup[i].supply, rc);
goto err_set_supply;
}
}
*supplies = vsup;
return 0;
err_set_supply:
for (i = 0; i < num_supplies; i++)
devm_regulator_put(vsup[i].consumer);
err_supply:
devm_kfree(dev, vsup);
return rc;
}
EXPORT_SYMBOL(msm_cdc_init_supplies);
/*
* msm_cdc_get_power_supplies:
* Get codec power supplies from device tree.
* Allocate memory to hold regulator data for
* all power supplies.
*
* @dev: pointer to codec device
* @cdc_vreg: pointer to codec regulator
* @total_num_supplies: total number of supplies read from DT
*
* Return error code if supply disable is failed
*/
int msm_cdc_get_power_supplies(struct device *dev,
struct cdc_regulator **cdc_vreg,
int *total_num_supplies)
{
const char *static_prop_name = "qcom,cdc-static-supplies";
const char *ond_prop_name = "qcom,cdc-on-demand-supplies";
const char *cp_prop_name = "qcom,cdc-cp-supplies";
int static_sup_cnt = 0;
int ond_sup_cnt = 0;
int cp_sup_cnt = 0;
int num_supplies = 0;
struct cdc_regulator *cdc_reg;
int rc;
if (!dev) {
pr_err("%s: device pointer is NULL\n", __func__);
return -EINVAL;
}
static_sup_cnt = of_property_count_strings(dev->of_node,
static_prop_name);
if (static_sup_cnt < 0) {
dev_err(dev, "%s: Failed to get static supplies(%d)\n",
__func__, static_sup_cnt);
rc = static_sup_cnt;
goto err_supply_cnt;
}
ond_sup_cnt = of_property_count_strings(dev->of_node, ond_prop_name);
if (ond_sup_cnt < 0)
ond_sup_cnt = 0;
cp_sup_cnt = of_property_count_strings(dev->of_node,
cp_prop_name);
if (cp_sup_cnt < 0)
cp_sup_cnt = 0;
num_supplies = static_sup_cnt + ond_sup_cnt + cp_sup_cnt;
if (num_supplies <= 0) {
dev_err(dev, "%s: supply count is 0 or negative\n", __func__);
rc = -EINVAL;
goto err_supply_cnt;
}
cdc_reg = devm_kcalloc(dev, num_supplies,
sizeof(struct cdc_regulator),
GFP_KERNEL);
if (!cdc_reg) {
rc = -ENOMEM;
goto err_mem_alloc;
}
rc = msm_cdc_parse_supplies(dev, cdc_reg, static_prop_name,
static_sup_cnt, false);
if (rc) {
dev_err(dev, "%s: failed to parse static supplies(%d)\n",
__func__, rc);
goto err_sup;
}
rc = msm_cdc_parse_supplies(dev, &cdc_reg[static_sup_cnt],
ond_prop_name, ond_sup_cnt,
true);
if (rc) {
dev_err(dev, "%s: failed to parse demand supplies(%d)\n",
__func__, rc);
goto err_sup;
}
rc = msm_cdc_parse_supplies(dev,
&cdc_reg[static_sup_cnt + ond_sup_cnt],
cp_prop_name, cp_sup_cnt, true);
if (rc) {
dev_err(dev, "%s: failed to parse cp supplies(%d)\n",
__func__, rc);
goto err_sup;
}
*cdc_vreg = cdc_reg;
*total_num_supplies = num_supplies;
return 0;
err_sup:
devm_kfree(dev, cdc_reg);
err_supply_cnt:
err_mem_alloc:
return rc;
}
EXPORT_SYMBOL(msm_cdc_get_power_supplies);

View file

@ -0,0 +1,48 @@
/* Copyright (c) 2016, 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.
*/
#ifndef __CODEC_POWER_SUPPLY_H__
#define __CODEC_POWER_SUPPLY_H__
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/regulator/consumer.h>
struct cdc_regulator {
const char *name;
int min_uV;
int max_uV;
int optimum_uA;
bool ondemand;
struct regulator *regulator;
};
extern int msm_cdc_get_power_supplies(struct device *dev,
struct cdc_regulator **cdc_vreg,
int *total_num_supplies);
extern int msm_cdc_disable_static_supplies(struct device *dev,
struct regulator_bulk_data *supplies,
struct cdc_regulator *cdc_vreg,
int num_supplies);
extern int msm_cdc_release_supplies(struct device *dev,
struct regulator_bulk_data *supplies,
struct cdc_regulator *cdc_vreg,
int num_supplies);
extern int msm_cdc_enable_static_supplies(struct device *dev,
struct regulator_bulk_data *supplies,
struct cdc_regulator *cdc_vreg,
int num_supplies);
extern int msm_cdc_init_supplies(struct device *dev,
struct regulator_bulk_data **supplies,
struct cdc_regulator *cdc_vreg,
int num_supplies);
#endif

View file

@ -0,0 +1,564 @@
/* Copyright (c) 2012-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/platform_device.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/err.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <linux/msm_ext_display.h>
#define MSM_EXT_DISP_PCM_RATES SNDRV_PCM_RATE_48000
#define AUD_EXT_DISP_ACK_DISCONNECT (AUDIO_ACK_CONNECT ^ AUDIO_ACK_CONNECT)
#define AUD_EXT_DISP_ACK_CONNECT (AUDIO_ACK_CONNECT)
#define AUD_EXT_DISP_ACK_ENABLE (AUDIO_ACK_SET_ENABLE | AUDIO_ACK_ENABLE)
static const char *const ext_disp_audio_type_text[] = {"None", "HDMI", "DP"};
static const char *const ext_disp_audio_ack_text[] = {"Disconnect", "Connect",
"Ack_Enable"};
static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_audio_type, ext_disp_audio_type_text);
static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_audio_ack_state,
ext_disp_audio_ack_text);
struct msm_ext_disp_audio_codec_rx_data {
struct platform_device *ext_disp_core_pdev;
struct msm_ext_disp_audio_codec_ops ext_disp_ops;
int cable_status;
};
static int msm_ext_disp_edid_ctl_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct msm_ext_disp_audio_codec_rx_data *codec_data;
struct msm_ext_disp_audio_edid_blk edid_blk;
int rc;
codec_data = snd_soc_codec_get_drvdata(codec);
if (!codec_data) {
dev_err(codec->dev, "%s: codec_data is NULL\n", __func__);
return -EINVAL;
}
if (!codec_data->ext_disp_ops.get_audio_edid_blk) {
dev_dbg(codec->dev, "%s: get_audio_edid_blk() is NULL\n",
__func__);
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
uinfo->count = 0;
return 0;
}
rc = codec_data->ext_disp_ops.get_audio_edid_blk(
codec_data->ext_disp_core_pdev, &edid_blk);
if (rc >= 0) {
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
uinfo->count = edid_blk.audio_data_blk_size +
edid_blk.spk_alloc_data_blk_size;
}
dev_dbg(codec->dev, "%s: count: %d\n", __func__, uinfo->count);
return rc;
}
static int msm_ext_disp_edid_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) {
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct msm_ext_disp_audio_codec_rx_data *codec_data;
struct msm_ext_disp_audio_edid_blk edid_blk;
int rc;
codec_data = snd_soc_codec_get_drvdata(codec);
if (!codec_data || !codec_data->ext_disp_ops.get_audio_edid_blk) {
dev_err(codec->dev, "%s: codec_data or get_audio_edid_blk() is NULL\n",
__func__);
return -EINVAL;
}
rc = codec_data->ext_disp_ops.get_audio_edid_blk(
codec_data->ext_disp_core_pdev, &edid_blk);
if (rc >= 0) {
if (sizeof(ucontrol->value.bytes.data) <
(edid_blk.audio_data_blk_size +
edid_blk.spk_alloc_data_blk_size)) {
dev_err(codec->dev,
"%s: Not enough memory to copy EDID data\n",
__func__);
return -ENOMEM;
}
memcpy(ucontrol->value.bytes.data,
edid_blk.audio_data_blk,
edid_blk.audio_data_blk_size);
memcpy((ucontrol->value.bytes.data +
edid_blk.audio_data_blk_size),
edid_blk.spk_alloc_data_blk,
edid_blk.spk_alloc_data_blk_size);
dev_dbg(codec->dev, "%s: data_blk_size:%d, spk_alloc_data_blk_size:%d\n",
__func__, edid_blk.audio_data_blk_size,
edid_blk.spk_alloc_data_blk_size);
}
return rc;
}
static int msm_ext_disp_audio_type_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct msm_ext_disp_audio_codec_rx_data *codec_data;
enum msm_ext_disp_cable_state cable_state;
enum msm_ext_disp_type disp_type;
int rc;
codec_data = snd_soc_codec_get_drvdata(codec);
if (!codec_data ||
!codec_data->ext_disp_ops.get_audio_edid_blk ||
!codec_data->ext_disp_ops.get_intf_id) {
dev_err(codec->dev, "%s: codec_data, get_audio_edid_blk() or get_intf_id is NULL\n",
__func__);
return -EINVAL;
}
cable_state = codec_data->ext_disp_ops.cable_status(
codec_data->ext_disp_core_pdev, 1);
if (cable_state < 0) {
dev_err(codec->dev, "%s: Error retrieving cable state from ext_disp, err:%d\n",
__func__, cable_state);
rc = cable_state;
goto done;
}
codec_data->cable_status = cable_state;
if (cable_state == EXT_DISPLAY_CABLE_DISCONNECT) {
dev_err(codec->dev, "%s: Display cable disconnected\n",
__func__);
ucontrol->value.integer.value[0] = 0;
rc = 0;
goto done;
}
disp_type = codec_data->ext_disp_ops.get_intf_id(
codec_data->ext_disp_core_pdev);
if (disp_type >= 0) {
switch (disp_type) {
case EXT_DISPLAY_TYPE_DP:
ucontrol->value.integer.value[0] = 2;
rc = 0;
break;
case EXT_DISPLAY_TYPE_HDMI:
ucontrol->value.integer.value[0] = 1;
rc = 0;
break;
default:
rc = -EINVAL;
dev_err(codec->dev, "%s: Invalid disp_type:%d\n",
__func__, disp_type);
goto done;
}
dev_dbg(codec->dev, "%s: Display type: %d\n",
__func__, disp_type);
} else {
dev_err(codec->dev, "%s: Error retrieving disp_type from ext_disp, err:%d\n",
__func__, disp_type);
rc = disp_type;
}
done:
return rc;
}
static int msm_ext_disp_audio_ack_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct msm_ext_disp_audio_codec_rx_data *codec_data;
u32 ack_state = 0;
int rc;
codec_data = snd_soc_codec_get_drvdata(codec);
if (!codec_data ||
!codec_data->ext_disp_ops.acknowledge) {
dev_err(codec->dev,
"%s: codec_data or ops acknowledge() is NULL\n",
__func__);
rc = -EINVAL;
goto done;
}
switch (ucontrol->value.enumerated.item[0]) {
case 0:
ack_state = AUD_EXT_DISP_ACK_DISCONNECT;
break;
case 1:
ack_state = AUD_EXT_DISP_ACK_CONNECT;
break;
case 2:
ack_state = AUD_EXT_DISP_ACK_ENABLE;
break;
default:
rc = -EINVAL;
dev_err(codec->dev,
"%s: invalid value %d for mixer ctl\n",
__func__, ucontrol->value.enumerated.item[0]);
goto done;
}
dev_dbg(codec->dev, "%s: control %d, ack set value 0x%x\n",
__func__, ucontrol->value.enumerated.item[0], ack_state);
rc = codec_data->ext_disp_ops.acknowledge(
codec_data->ext_disp_core_pdev, ack_state);
if (rc < 0) {
dev_err(codec->dev, "%s: error from acknowledge(), err:%d\n",
__func__, rc);
}
done:
return rc;
}
static const struct snd_kcontrol_new msm_ext_disp_codec_rx_controls[] = {
{
.access = SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "HDMI EDID",
.info = msm_ext_disp_edid_ctl_info,
.get = msm_ext_disp_edid_get,
},
{
.access = SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "Display Port EDID",
.info = msm_ext_disp_edid_ctl_info,
.get = msm_ext_disp_edid_get,
},
SOC_ENUM_EXT("External Display Type", ext_disp_audio_type,
msm_ext_disp_audio_type_get, NULL),
SOC_ENUM_EXT("External Display Audio Ack", ext_disp_audio_ack_state,
NULL, msm_ext_disp_audio_ack_set),
};
static int msm_ext_disp_audio_codec_rx_dai_startup(
struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int ret = 0;
struct msm_ext_disp_audio_codec_rx_data *codec_data =
dev_get_drvdata(dai->codec->dev);
if (!codec_data || !codec_data->ext_disp_ops.cable_status) {
dev_err(dai->dev, "%s() codec_data or cable_status is null\n",
__func__);
return -EINVAL;
}
codec_data->cable_status =
codec_data->ext_disp_ops.cable_status(
codec_data->ext_disp_core_pdev, 1);
if (codec_data->cable_status < 0) {
dev_err(dai->dev,
"%s() ext disp core is not ready (ret val = %d)\n",
__func__, codec_data->cable_status);
ret = codec_data->cable_status;
} else if (!codec_data->cable_status) {
dev_err(dai->dev,
"%s() ext disp cable is not connected (ret val = %d)\n",
__func__, codec_data->cable_status);
ret = -ENODEV;
}
return ret;
}
static int msm_ext_disp_audio_codec_rx_dai_hw_params(
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
u32 channel_allocation = 0;
u32 level_shift = 0; /* 0dB */
bool down_mix = 0;
u32 num_channels = params_channels(params);
int rc = 0;
struct msm_ext_disp_audio_setup_params audio_setup_params = {0};
struct msm_ext_disp_audio_codec_rx_data *codec_data =
dev_get_drvdata(dai->codec->dev);
if (!codec_data || !codec_data->ext_disp_ops.audio_info_setup) {
dev_err(dai->dev, "%s: codec_data or audio_info_setup is null\n",
__func__);
return -EINVAL;
}
if (codec_data->cable_status < 0) {
dev_err_ratelimited(dai->dev,
"%s() ext disp core is not ready (ret val = %d)\n",
__func__, codec_data->cable_status);
return codec_data->cable_status;
} else if (!codec_data->cable_status) {
dev_err_ratelimited(dai->dev,
"%s() ext disp cable is not connected (ret val = %d)\n",
__func__, codec_data->cable_status);
return -ENODEV;
}
/*refer to HDMI spec CEA-861-E: Table 28 Audio InfoFrame Data Byte 4*/
switch (num_channels) {
case 2:
channel_allocation = 0;
break;
case 3:
channel_allocation = 0x02;/*default to FL/FR/FC*/
audio_setup_params.sample_present = 0x3;
break;
case 4:
channel_allocation = 0x06;/*default to FL/FR/FC/RC*/
audio_setup_params.sample_present = 0x7;
break;
case 5:
channel_allocation = 0x0A;/*default to FL/FR/FC/RR/RL*/
audio_setup_params.sample_present = 0x7;
break;
case 6:
channel_allocation = 0x0B;
audio_setup_params.sample_present = 0x7;
break;
case 7:
channel_allocation = 0x12;/*default to FL/FR/FC/RL/RR/RRC/RLC*/
audio_setup_params.sample_present = 0xf;
break;
case 8:
channel_allocation = 0x13;
audio_setup_params.sample_present = 0xf;
break;
default:
dev_err(dai->dev, "invalid Channels = %u\n", num_channels);
return -EINVAL;
}
dev_dbg(dai->dev,
"%s() num_ch %u samplerate %u channel_allocation = %u\n",
__func__, num_channels, params_rate(params),
channel_allocation);
audio_setup_params.sample_rate_hz = params_rate(params);
audio_setup_params.num_of_channels = num_channels;
audio_setup_params.channel_allocation = channel_allocation;
audio_setup_params.level_shift = level_shift;
audio_setup_params.down_mix = down_mix;
rc = codec_data->ext_disp_ops.audio_info_setup(
codec_data->ext_disp_core_pdev, &audio_setup_params);
if (rc < 0) {
dev_err_ratelimited(dai->dev,
"%s() ext disp core is not ready, rc: %d\n",
__func__, rc);
}
return rc;
}
static void msm_ext_disp_audio_codec_rx_dai_shutdown(
struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int rc;
struct msm_ext_disp_audio_codec_rx_data *codec_data =
dev_get_drvdata(dai->codec->dev);
if (!codec_data || !codec_data->ext_disp_ops.teardown_done ||
!codec_data->ext_disp_ops.cable_status) {
dev_err(dai->dev, "%s: codec data or teardown_done or cable_status is null\n",
__func__);
return;
}
rc = codec_data->ext_disp_ops.cable_status(
codec_data->ext_disp_core_pdev, 0);
if (rc < 0) {
dev_err(dai->dev,
"%s: ext disp core had problems releasing audio flag\n",
__func__);
}
codec_data->ext_disp_ops.teardown_done(
codec_data->ext_disp_core_pdev);
}
static int msm_ext_disp_audio_codec_rx_probe(struct snd_soc_codec *codec)
{
struct msm_ext_disp_audio_codec_rx_data *codec_data;
struct device_node *of_node_parent = NULL;
codec_data = kzalloc(sizeof(struct msm_ext_disp_audio_codec_rx_data),
GFP_KERNEL);
if (!codec_data) {
dev_err(codec->dev, "%s(): fail to allocate dai data\n",
__func__);
return -ENOMEM;
}
of_node_parent = of_get_parent(codec->dev->of_node);
if (!of_node_parent) {
dev_err(codec->dev, "%s(): Parent device tree node not found\n",
__func__);
kfree(codec_data);
return -ENODEV;
}
codec_data->ext_disp_core_pdev = of_find_device_by_node(of_node_parent);
if (!codec_data->ext_disp_core_pdev) {
dev_err(codec->dev, "%s(): can't get parent pdev\n", __func__);
kfree(codec_data);
return -ENODEV;
}
if (msm_ext_disp_register_audio_codec(codec_data->ext_disp_core_pdev,
&codec_data->ext_disp_ops)) {
dev_err(codec->dev, "%s(): can't register with ext disp core",
__func__);
kfree(codec_data);
return -ENODEV;
}
dev_set_drvdata(codec->dev, codec_data);
dev_dbg(codec->dev, "%s(): registered %s with ext disp core\n",
__func__, codec->component.name);
return 0;
}
static int msm_ext_disp_audio_codec_rx_remove(struct snd_soc_codec *codec)
{
struct msm_ext_disp_audio_codec_rx_data *codec_data;
codec_data = dev_get_drvdata(codec->dev);
kfree(codec_data);
return 0;
}
static struct snd_soc_dai_ops msm_ext_disp_audio_codec_rx_dai_ops = {
.startup = msm_ext_disp_audio_codec_rx_dai_startup,
.hw_params = msm_ext_disp_audio_codec_rx_dai_hw_params,
.shutdown = msm_ext_disp_audio_codec_rx_dai_shutdown
};
static struct snd_soc_dai_driver msm_ext_disp_audio_codec_rx_dais[] = {
{
.name = "msm_hdmi_audio_codec_rx_dai",
.playback = {
.stream_name = "HDMI Playback",
.channels_min = 1,
.channels_max = 8,
.rate_min = 48000,
.rate_max = 48000,
.rates = MSM_EXT_DISP_PCM_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.ops = &msm_ext_disp_audio_codec_rx_dai_ops,
},
{
.name = "msm_dp_audio_codec_rx_dai",
.playback = {
.stream_name = "Display Port Playback",
.channels_min = 1,
.channels_max = 8,
.rate_min = 48000,
.rate_max = 192000,
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S24_3LE,
},
.ops = &msm_ext_disp_audio_codec_rx_dai_ops,
},
};
static struct snd_soc_codec_driver msm_ext_disp_audio_codec_rx_soc_driver = {
.probe = msm_ext_disp_audio_codec_rx_probe,
.remove = msm_ext_disp_audio_codec_rx_remove,
.component_driver = {
.controls = msm_ext_disp_codec_rx_controls,
.num_controls = ARRAY_SIZE(msm_ext_disp_codec_rx_controls),
},
};
static int msm_ext_disp_audio_codec_rx_plat_probe(
struct platform_device *pdev)
{
dev_dbg(&pdev->dev, "%s(): dev name %s\n", __func__,
dev_name(&pdev->dev));
return snd_soc_register_codec(&pdev->dev,
&msm_ext_disp_audio_codec_rx_soc_driver,
msm_ext_disp_audio_codec_rx_dais,
ARRAY_SIZE(msm_ext_disp_audio_codec_rx_dais));
}
static int msm_ext_disp_audio_codec_rx_plat_remove(
struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
static const struct of_device_id msm_ext_disp_audio_codec_rx_dt_match[] = {
{ .compatible = "qcom,msm-ext-disp-audio-codec-rx", },
{}
};
MODULE_DEVICE_TABLE(of, msm_ext_disp_audio_codec_rx_dt_match);
static struct platform_driver msm_ext_disp_audio_codec_rx_driver = {
.driver = {
.name = "msm-ext-disp-audio-codec-rx",
.owner = THIS_MODULE,
.of_match_table = msm_ext_disp_audio_codec_rx_dt_match,
.suppress_bind_attrs = true,
},
.probe = msm_ext_disp_audio_codec_rx_plat_probe,
.remove = msm_ext_disp_audio_codec_rx_plat_remove,
};
static int __init msm_ext_disp_audio_codec_rx_init(void)
{
int rc;
rc = platform_driver_register(&msm_ext_disp_audio_codec_rx_driver);
if (rc) {
pr_err("%s: failed to register ext disp codec driver err:%d\n",
__func__, rc);
}
return rc;
}
module_init(msm_ext_disp_audio_codec_rx_init);
static void __exit msm_ext_disp_audio_codec_rx_exit(void)
{
platform_driver_unregister(&msm_ext_disp_audio_codec_rx_driver);
}
module_exit(msm_ext_disp_audio_codec_rx_exit);
MODULE_DESCRIPTION("MSM External Display Audio CODEC Driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,2 @@
snd-soc-msm-sdw-objs := msm_sdw_cdc.o msm_sdw_regmap.o msm-sdw-tables.o msm_sdw_cdc_utils.o
obj-$(CONFIG_SND_SOC_MSM_SDW) += snd-soc-msm-sdw.o

View file

@ -0,0 +1,319 @@
/* Copyright (c) 2016-2017, 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/types.h>
#include "msm_sdw.h"
const u8 msm_sdw_page_map[MSM_SDW_MAX_REGISTER] = {
[MSM_SDW_TX9_SPKR_PROT_PATH_CTL] = 0xa,
[MSM_SDW_TX9_SPKR_PROT_PATH_CFG0] = 0xa,
[MSM_SDW_TX10_SPKR_PROT_PATH_CTL] = 0xa,
[MSM_SDW_TX10_SPKR_PROT_PATH_CFG0] = 0xa,
[MSM_SDW_TX11_SPKR_PROT_PATH_CTL] = 0xa,
[MSM_SDW_TX11_SPKR_PROT_PATH_CFG0] = 0xa,
[MSM_SDW_TX12_SPKR_PROT_PATH_CTL] = 0xa,
[MSM_SDW_TX12_SPKR_PROT_PATH_CFG0] = 0xa,
[MSM_SDW_COMPANDER7_CTL0] = 0xb,
[MSM_SDW_COMPANDER7_CTL1] = 0xb,
[MSM_SDW_COMPANDER7_CTL2] = 0xb,
[MSM_SDW_COMPANDER7_CTL3] = 0xb,
[MSM_SDW_COMPANDER7_CTL4] = 0xb,
[MSM_SDW_COMPANDER7_CTL5] = 0xb,
[MSM_SDW_COMPANDER7_CTL6] = 0xb,
[MSM_SDW_COMPANDER7_CTL7] = 0xb,
[MSM_SDW_COMPANDER8_CTL0] = 0xb,
[MSM_SDW_COMPANDER8_CTL1] = 0xb,
[MSM_SDW_COMPANDER8_CTL2] = 0xb,
[MSM_SDW_COMPANDER8_CTL3] = 0xb,
[MSM_SDW_COMPANDER8_CTL4] = 0xb,
[MSM_SDW_COMPANDER8_CTL5] = 0xb,
[MSM_SDW_COMPANDER8_CTL6] = 0xb,
[MSM_SDW_COMPANDER8_CTL7] = 0xb,
[MSM_SDW_RX7_RX_PATH_CTL] = 0xb,
[MSM_SDW_RX7_RX_PATH_CFG0] = 0xb,
[MSM_SDW_RX7_RX_PATH_CFG1] = 0xb,
[MSM_SDW_RX7_RX_PATH_CFG2] = 0xb,
[MSM_SDW_RX7_RX_VOL_CTL] = 0xb,
[MSM_SDW_RX7_RX_PATH_MIX_CTL] = 0xb,
[MSM_SDW_RX7_RX_PATH_MIX_CFG] = 0xb,
[MSM_SDW_RX7_RX_VOL_MIX_CTL] = 0xb,
[MSM_SDW_RX7_RX_PATH_SEC0] = 0xb,
[MSM_SDW_RX7_RX_PATH_SEC1] = 0xb,
[MSM_SDW_RX7_RX_PATH_SEC2] = 0xb,
[MSM_SDW_RX7_RX_PATH_SEC3] = 0xb,
[MSM_SDW_RX7_RX_PATH_SEC5] = 0xb,
[MSM_SDW_RX7_RX_PATH_SEC6] = 0xb,
[MSM_SDW_RX7_RX_PATH_SEC7] = 0xb,
[MSM_SDW_RX7_RX_PATH_MIX_SEC0] = 0xb,
[MSM_SDW_RX7_RX_PATH_MIX_SEC1] = 0xb,
[MSM_SDW_RX8_RX_PATH_CTL] = 0xb,
[MSM_SDW_RX8_RX_PATH_CFG0] = 0xb,
[MSM_SDW_RX8_RX_PATH_CFG1] = 0xb,
[MSM_SDW_RX8_RX_PATH_CFG2] = 0xb,
[MSM_SDW_RX8_RX_VOL_CTL] = 0xb,
[MSM_SDW_RX8_RX_PATH_MIX_CTL] = 0xb,
[MSM_SDW_RX8_RX_PATH_MIX_CFG] = 0xb,
[MSM_SDW_RX8_RX_VOL_MIX_CTL] = 0xb,
[MSM_SDW_RX8_RX_PATH_SEC0] = 0xb,
[MSM_SDW_RX8_RX_PATH_SEC1] = 0xb,
[MSM_SDW_RX8_RX_PATH_SEC2] = 0xb,
[MSM_SDW_RX8_RX_PATH_SEC3] = 0xb,
[MSM_SDW_RX8_RX_PATH_SEC5] = 0xb,
[MSM_SDW_RX8_RX_PATH_SEC6] = 0xb,
[MSM_SDW_RX8_RX_PATH_SEC7] = 0xb,
[MSM_SDW_RX8_RX_PATH_MIX_SEC0] = 0xb,
[MSM_SDW_RX8_RX_PATH_MIX_SEC1] = 0xb,
[MSM_SDW_BOOST0_BOOST_PATH_CTL] = 0xc,
[MSM_SDW_BOOST0_BOOST_CTL] = 0xc,
[MSM_SDW_BOOST0_BOOST_CFG1] = 0xc,
[MSM_SDW_BOOST0_BOOST_CFG2] = 0xc,
[MSM_SDW_BOOST1_BOOST_PATH_CTL] = 0xc,
[MSM_SDW_BOOST1_BOOST_CTL] = 0xc,
[MSM_SDW_BOOST1_BOOST_CFG1] = 0xc,
[MSM_SDW_BOOST1_BOOST_CFG2] = 0xc,
[MSM_SDW_AHB_BRIDGE_WR_DATA_0] = 0xc,
[MSM_SDW_AHB_BRIDGE_WR_DATA_1] = 0xc,
[MSM_SDW_AHB_BRIDGE_WR_DATA_2] = 0xc,
[MSM_SDW_AHB_BRIDGE_WR_DATA_3] = 0xc,
[MSM_SDW_AHB_BRIDGE_WR_ADDR_0] = 0xc,
[MSM_SDW_AHB_BRIDGE_WR_ADDR_1] = 0xc,
[MSM_SDW_AHB_BRIDGE_WR_ADDR_2] = 0xc,
[MSM_SDW_AHB_BRIDGE_WR_ADDR_3] = 0xc,
[MSM_SDW_AHB_BRIDGE_RD_ADDR_0] = 0xc,
[MSM_SDW_AHB_BRIDGE_RD_ADDR_1] = 0xc,
[MSM_SDW_AHB_BRIDGE_RD_ADDR_2] = 0xc,
[MSM_SDW_AHB_BRIDGE_RD_ADDR_3] = 0xc,
[MSM_SDW_AHB_BRIDGE_RD_DATA_0] = 0xc,
[MSM_SDW_AHB_BRIDGE_RD_DATA_1] = 0xc,
[MSM_SDW_AHB_BRIDGE_RD_DATA_2] = 0xc,
[MSM_SDW_AHB_BRIDGE_RD_DATA_3] = 0xc,
[MSM_SDW_AHB_BRIDGE_ACCESS_CFG] = 0xc,
[MSM_SDW_AHB_BRIDGE_ACCESS_STATUS] = 0xc,
[MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL] = 0xd,
[MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL] = 0xd,
[MSM_SDW_CLK_RST_CTRL_SWR_CONTROL] = 0xd,
[MSM_SDW_TOP_TOP_CFG0] = 0xd,
[MSM_SDW_TOP_TOP_CFG1] = 0xd,
[MSM_SDW_TOP_RX_I2S_CTL] = 0xd,
[MSM_SDW_TOP_TX_I2S_CTL] = 0xd,
[MSM_SDW_TOP_I2S_CLK] = 0xd,
[MSM_SDW_TOP_RX7_PATH_INPUT0_MUX] = 0xd,
[MSM_SDW_TOP_RX7_PATH_INPUT1_MUX] = 0xd,
[MSM_SDW_TOP_RX8_PATH_INPUT0_MUX] = 0xd,
[MSM_SDW_TOP_RX8_PATH_INPUT1_MUX] = 0xd,
[MSM_SDW_TOP_FREQ_MCLK] = 0xd,
[MSM_SDW_TOP_DEBUG_BUS_SEL] = 0xd,
[MSM_SDW_TOP_DEBUG_EN] = 0xd,
[MSM_SDW_TOP_I2S_RESET] = 0xd,
[MSM_SDW_TOP_BLOCKS_RESET] = 0xd,
};
const u8 msm_sdw_reg_readable[MSM_SDW_MAX_REGISTER] = {
[MSM_SDW_PAGE_REGISTER] = 1,
[MSM_SDW_TX9_SPKR_PROT_PATH_CTL] = 1,
[MSM_SDW_TX9_SPKR_PROT_PATH_CFG0] = 1,
[MSM_SDW_TX10_SPKR_PROT_PATH_CTL] = 1,
[MSM_SDW_TX10_SPKR_PROT_PATH_CFG0] = 1,
[MSM_SDW_TX11_SPKR_PROT_PATH_CTL] = 1,
[MSM_SDW_TX11_SPKR_PROT_PATH_CFG0] = 1,
[MSM_SDW_TX12_SPKR_PROT_PATH_CTL] = 1,
[MSM_SDW_TX12_SPKR_PROT_PATH_CFG0] = 1,
[MSM_SDW_COMPANDER7_CTL0] = 1,
[MSM_SDW_COMPANDER7_CTL1] = 1,
[MSM_SDW_COMPANDER7_CTL2] = 1,
[MSM_SDW_COMPANDER7_CTL3] = 1,
[MSM_SDW_COMPANDER7_CTL4] = 1,
[MSM_SDW_COMPANDER7_CTL5] = 1,
[MSM_SDW_COMPANDER7_CTL6] = 1,
[MSM_SDW_COMPANDER7_CTL7] = 1,
[MSM_SDW_COMPANDER8_CTL0] = 1,
[MSM_SDW_COMPANDER8_CTL1] = 1,
[MSM_SDW_COMPANDER8_CTL2] = 1,
[MSM_SDW_COMPANDER8_CTL3] = 1,
[MSM_SDW_COMPANDER8_CTL4] = 1,
[MSM_SDW_COMPANDER8_CTL5] = 1,
[MSM_SDW_COMPANDER8_CTL6] = 1,
[MSM_SDW_COMPANDER8_CTL7] = 1,
[MSM_SDW_RX7_RX_PATH_CTL] = 1,
[MSM_SDW_RX7_RX_PATH_CFG0] = 1,
[MSM_SDW_RX7_RX_PATH_CFG1] = 1,
[MSM_SDW_RX7_RX_PATH_CFG2] = 1,
[MSM_SDW_RX7_RX_VOL_CTL] = 1,
[MSM_SDW_RX7_RX_PATH_MIX_CTL] = 1,
[MSM_SDW_RX7_RX_PATH_MIX_CFG] = 1,
[MSM_SDW_RX7_RX_VOL_MIX_CTL] = 1,
[MSM_SDW_RX7_RX_PATH_SEC0] = 1,
[MSM_SDW_RX7_RX_PATH_SEC1] = 1,
[MSM_SDW_RX7_RX_PATH_SEC2] = 1,
[MSM_SDW_RX7_RX_PATH_SEC3] = 1,
[MSM_SDW_RX7_RX_PATH_SEC5] = 1,
[MSM_SDW_RX7_RX_PATH_SEC6] = 1,
[MSM_SDW_RX7_RX_PATH_SEC7] = 1,
[MSM_SDW_RX7_RX_PATH_MIX_SEC0] = 1,
[MSM_SDW_RX7_RX_PATH_MIX_SEC1] = 1,
[MSM_SDW_RX8_RX_PATH_CTL] = 1,
[MSM_SDW_RX8_RX_PATH_CFG0] = 1,
[MSM_SDW_RX8_RX_PATH_CFG1] = 1,
[MSM_SDW_RX8_RX_PATH_CFG2] = 1,
[MSM_SDW_RX8_RX_VOL_CTL] = 1,
[MSM_SDW_RX8_RX_PATH_MIX_CTL] = 1,
[MSM_SDW_RX8_RX_PATH_MIX_CFG] = 1,
[MSM_SDW_RX8_RX_VOL_MIX_CTL] = 1,
[MSM_SDW_RX8_RX_PATH_SEC0] = 1,
[MSM_SDW_RX8_RX_PATH_SEC1] = 1,
[MSM_SDW_RX8_RX_PATH_SEC2] = 1,
[MSM_SDW_RX8_RX_PATH_SEC3] = 1,
[MSM_SDW_RX8_RX_PATH_SEC5] = 1,
[MSM_SDW_RX8_RX_PATH_SEC6] = 1,
[MSM_SDW_RX8_RX_PATH_SEC7] = 1,
[MSM_SDW_RX8_RX_PATH_MIX_SEC0] = 1,
[MSM_SDW_RX8_RX_PATH_MIX_SEC1] = 1,
[MSM_SDW_BOOST0_BOOST_PATH_CTL] = 1,
[MSM_SDW_BOOST0_BOOST_CTL] = 1,
[MSM_SDW_BOOST0_BOOST_CFG1] = 1,
[MSM_SDW_BOOST0_BOOST_CFG2] = 1,
[MSM_SDW_BOOST1_BOOST_PATH_CTL] = 1,
[MSM_SDW_BOOST1_BOOST_CTL] = 1,
[MSM_SDW_BOOST1_BOOST_CFG1] = 1,
[MSM_SDW_BOOST1_BOOST_CFG2] = 1,
[MSM_SDW_AHB_BRIDGE_WR_DATA_0] = 1,
[MSM_SDW_AHB_BRIDGE_WR_DATA_1] = 1,
[MSM_SDW_AHB_BRIDGE_WR_DATA_2] = 1,
[MSM_SDW_AHB_BRIDGE_WR_DATA_3] = 1,
[MSM_SDW_AHB_BRIDGE_WR_ADDR_0] = 1,
[MSM_SDW_AHB_BRIDGE_WR_ADDR_1] = 1,
[MSM_SDW_AHB_BRIDGE_WR_ADDR_2] = 1,
[MSM_SDW_AHB_BRIDGE_WR_ADDR_3] = 1,
[MSM_SDW_AHB_BRIDGE_RD_ADDR_0] = 1,
[MSM_SDW_AHB_BRIDGE_RD_ADDR_1] = 1,
[MSM_SDW_AHB_BRIDGE_RD_ADDR_2] = 1,
[MSM_SDW_AHB_BRIDGE_RD_ADDR_3] = 1,
[MSM_SDW_AHB_BRIDGE_RD_DATA_0] = 1,
[MSM_SDW_AHB_BRIDGE_RD_DATA_1] = 1,
[MSM_SDW_AHB_BRIDGE_RD_DATA_2] = 1,
[MSM_SDW_AHB_BRIDGE_RD_DATA_3] = 1,
[MSM_SDW_AHB_BRIDGE_ACCESS_CFG] = 1,
[MSM_SDW_AHB_BRIDGE_ACCESS_STATUS] = 1,
[MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL] = 1,
[MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL] = 1,
[MSM_SDW_CLK_RST_CTRL_SWR_CONTROL] = 1,
[MSM_SDW_TOP_TOP_CFG0] = 1,
[MSM_SDW_TOP_TOP_CFG1] = 1,
[MSM_SDW_TOP_RX_I2S_CTL] = 1,
[MSM_SDW_TOP_TX_I2S_CTL] = 1,
[MSM_SDW_TOP_RX7_PATH_INPUT0_MUX] = 1,
[MSM_SDW_TOP_RX7_PATH_INPUT1_MUX] = 1,
[MSM_SDW_TOP_RX8_PATH_INPUT0_MUX] = 1,
[MSM_SDW_TOP_RX8_PATH_INPUT1_MUX] = 1,
[MSM_SDW_TOP_FREQ_MCLK] = 1,
[MSM_SDW_TOP_DEBUG_BUS_SEL] = 1,
[MSM_SDW_TOP_DEBUG_EN] = 1,
[MSM_SDW_TOP_I2S_RESET] = 1,
[MSM_SDW_TOP_BLOCKS_RESET] = 1,
};
const u8 msm_sdw_reg_writeable[MSM_SDW_MAX_REGISTER] = {
[MSM_SDW_PAGE_REGISTER] = 1,
[MSM_SDW_TX9_SPKR_PROT_PATH_CTL] = 1,
[MSM_SDW_TX9_SPKR_PROT_PATH_CFG0] = 1,
[MSM_SDW_TX10_SPKR_PROT_PATH_CTL] = 1,
[MSM_SDW_TX10_SPKR_PROT_PATH_CFG0] = 1,
[MSM_SDW_TX11_SPKR_PROT_PATH_CTL] = 1,
[MSM_SDW_TX11_SPKR_PROT_PATH_CFG0] = 1,
[MSM_SDW_TX12_SPKR_PROT_PATH_CTL] = 1,
[MSM_SDW_TX12_SPKR_PROT_PATH_CFG0] = 1,
[MSM_SDW_COMPANDER7_CTL0] = 1,
[MSM_SDW_COMPANDER7_CTL1] = 1,
[MSM_SDW_COMPANDER7_CTL2] = 1,
[MSM_SDW_COMPANDER7_CTL3] = 1,
[MSM_SDW_COMPANDER7_CTL4] = 1,
[MSM_SDW_COMPANDER7_CTL5] = 1,
[MSM_SDW_COMPANDER7_CTL7] = 1,
[MSM_SDW_COMPANDER8_CTL0] = 1,
[MSM_SDW_COMPANDER8_CTL1] = 1,
[MSM_SDW_COMPANDER8_CTL2] = 1,
[MSM_SDW_COMPANDER8_CTL3] = 1,
[MSM_SDW_COMPANDER8_CTL4] = 1,
[MSM_SDW_COMPANDER8_CTL5] = 1,
[MSM_SDW_COMPANDER8_CTL7] = 1,
[MSM_SDW_RX7_RX_PATH_CTL] = 1,
[MSM_SDW_RX7_RX_PATH_CFG0] = 1,
[MSM_SDW_RX7_RX_PATH_CFG1] = 1,
[MSM_SDW_RX7_RX_PATH_CFG2] = 1,
[MSM_SDW_RX7_RX_VOL_CTL] = 1,
[MSM_SDW_RX7_RX_PATH_MIX_CTL] = 1,
[MSM_SDW_RX7_RX_PATH_MIX_CFG] = 1,
[MSM_SDW_RX7_RX_VOL_MIX_CTL] = 1,
[MSM_SDW_RX7_RX_PATH_SEC0] = 1,
[MSM_SDW_RX7_RX_PATH_SEC1] = 1,
[MSM_SDW_RX7_RX_PATH_SEC2] = 1,
[MSM_SDW_RX7_RX_PATH_SEC3] = 1,
[MSM_SDW_RX7_RX_PATH_SEC5] = 1,
[MSM_SDW_RX7_RX_PATH_SEC6] = 1,
[MSM_SDW_RX7_RX_PATH_SEC7] = 1,
[MSM_SDW_RX7_RX_PATH_MIX_SEC0] = 1,
[MSM_SDW_RX7_RX_PATH_MIX_SEC1] = 1,
[MSM_SDW_RX8_RX_PATH_CTL] = 1,
[MSM_SDW_RX8_RX_PATH_CFG0] = 1,
[MSM_SDW_RX8_RX_PATH_CFG1] = 1,
[MSM_SDW_RX8_RX_PATH_CFG2] = 1,
[MSM_SDW_RX8_RX_VOL_CTL] = 1,
[MSM_SDW_RX8_RX_PATH_MIX_CTL] = 1,
[MSM_SDW_RX8_RX_PATH_MIX_CFG] = 1,
[MSM_SDW_RX8_RX_VOL_MIX_CTL] = 1,
[MSM_SDW_RX8_RX_PATH_SEC0] = 1,
[MSM_SDW_RX8_RX_PATH_SEC1] = 1,
[MSM_SDW_RX8_RX_PATH_SEC2] = 1,
[MSM_SDW_RX8_RX_PATH_SEC3] = 1,
[MSM_SDW_RX8_RX_PATH_SEC5] = 1,
[MSM_SDW_RX8_RX_PATH_SEC6] = 1,
[MSM_SDW_RX8_RX_PATH_SEC7] = 1,
[MSM_SDW_RX8_RX_PATH_MIX_SEC0] = 1,
[MSM_SDW_RX8_RX_PATH_MIX_SEC1] = 1,
[MSM_SDW_BOOST0_BOOST_PATH_CTL] = 1,
[MSM_SDW_BOOST0_BOOST_CTL] = 1,
[MSM_SDW_BOOST0_BOOST_CFG1] = 1,
[MSM_SDW_BOOST0_BOOST_CFG2] = 1,
[MSM_SDW_BOOST1_BOOST_PATH_CTL] = 1,
[MSM_SDW_BOOST1_BOOST_CTL] = 1,
[MSM_SDW_BOOST1_BOOST_CFG1] = 1,
[MSM_SDW_BOOST1_BOOST_CFG2] = 1,
[MSM_SDW_AHB_BRIDGE_WR_DATA_0] = 1,
[MSM_SDW_AHB_BRIDGE_WR_DATA_1] = 1,
[MSM_SDW_AHB_BRIDGE_WR_DATA_2] = 1,
[MSM_SDW_AHB_BRIDGE_WR_DATA_3] = 1,
[MSM_SDW_AHB_BRIDGE_WR_ADDR_0] = 1,
[MSM_SDW_AHB_BRIDGE_WR_ADDR_1] = 1,
[MSM_SDW_AHB_BRIDGE_WR_ADDR_2] = 1,
[MSM_SDW_AHB_BRIDGE_WR_ADDR_3] = 1,
[MSM_SDW_AHB_BRIDGE_RD_ADDR_0] = 1,
[MSM_SDW_AHB_BRIDGE_RD_ADDR_1] = 1,
[MSM_SDW_AHB_BRIDGE_RD_ADDR_2] = 1,
[MSM_SDW_AHB_BRIDGE_RD_ADDR_3] = 1,
[MSM_SDW_AHB_BRIDGE_ACCESS_CFG] = 1,
[MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL] = 1,
[MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL] = 1,
[MSM_SDW_CLK_RST_CTRL_SWR_CONTROL] = 1,
[MSM_SDW_TOP_TOP_CFG0] = 1,
[MSM_SDW_TOP_TOP_CFG1] = 1,
[MSM_SDW_TOP_RX_I2S_CTL] = 1,
[MSM_SDW_TOP_TX_I2S_CTL] = 1,
[MSM_SDW_TOP_RX7_PATH_INPUT0_MUX] = 1,
[MSM_SDW_TOP_RX7_PATH_INPUT1_MUX] = 1,
[MSM_SDW_TOP_RX8_PATH_INPUT0_MUX] = 1,
[MSM_SDW_TOP_RX8_PATH_INPUT1_MUX] = 1,
[MSM_SDW_TOP_FREQ_MCLK] = 1,
[MSM_SDW_TOP_DEBUG_BUS_SEL] = 1,
[MSM_SDW_TOP_DEBUG_EN] = 1,
[MSM_SDW_TOP_I2S_RESET] = 1,
[MSM_SDW_TOP_BLOCKS_RESET] = 1,
};

View file

@ -0,0 +1,170 @@
/* Copyright (c) 2016-2017, 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.
*/
#ifndef MSM_SDW_H
#define MSM_SDW_H
#include <sound/soc.h>
#include <dsp/q6afe-v2.h>
#include "msm_sdw_registers.h"
#define MSM_SDW_MAX_REGISTER 0x400
extern const struct regmap_config msm_sdw_regmap_config;
extern const u8 msm_sdw_page_map[MSM_SDW_MAX_REGISTER];
extern const u8 msm_sdw_reg_readable[MSM_SDW_MAX_REGISTER];
extern const u8 msm_sdw_reg_writeable[MSM_SDW_MAX_REGISTER];
enum {
MSM_SDW_RX4 = 0,
MSM_SDW_RX5,
MSM_SDW_RX_MAX,
};
enum {
MSM_SDW_TX0 = 0,
MSM_SDW_TX1,
MSM_SDW_TX_MAX,
};
enum {
COMP1, /* SPK_L */
COMP2, /* SPK_R */
COMP_MAX
};
/*
* Structure used to update codec
* register defaults after reset
*/
struct msm_sdw_reg_mask_val {
u16 reg;
u8 mask;
u8 val;
};
/*
* Selects compander and smart boost settings
* for a given speaker mode
*/
enum {
SPKR_MODE_DEFAULT,
SPKR_MODE_1, /* COMP Gain = 12dB, Smartboost Max = 5.5V */
};
/* Rx path gain offsets */
enum {
RX_GAIN_OFFSET_M1P5_DB,
RX_GAIN_OFFSET_0_DB,
};
struct msm_sdw_reg_val {
unsigned short reg; /* register address */
u8 *buf; /* buffer to be written to reg. addr */
int bytes; /* number of bytes to be written */
};
/* Hold instance to soundwire platform device */
struct msm_sdw_ctrl_data {
struct platform_device *sdw_pdev;
};
struct wcd_sdw_ctrl_platform_data {
void *handle; /* holds codec private data */
int (*read)(void *handle, int reg);
int (*write)(void *handle, int reg, int val);
int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len);
int (*clk)(void *handle, bool enable);
int (*handle_irq)(void *handle,
irqreturn_t (*swrm_irq_handler)(int irq,
void *data),
void *swrm_handle,
int action);
};
struct msm_sdw_priv {
struct device *dev;
struct mutex io_lock;
int (*read_dev)(struct msm_sdw_priv *msm_sdw, unsigned short reg,
int bytes, void *dest);
int (*write_dev)(struct msm_sdw_priv *msm_sdw, unsigned short reg,
int bytes, void *src);
int (*multi_reg_write)(struct msm_sdw_priv *msm_sdw, const void *data,
size_t count);
struct snd_soc_codec *codec;
struct device_node *sdw_gpio_p; /* used by pinctrl API */
/* SoundWire data structure */
struct msm_sdw_ctrl_data *sdw_ctrl_data;
int nr;
/* compander */
int comp_enabled[COMP_MAX];
int ear_spkr_gain;
/* to track the status */
unsigned long status_mask;
struct work_struct msm_sdw_add_child_devices_work;
struct wcd_sdw_ctrl_platform_data sdw_plat_data;
unsigned int vi_feed_value;
struct mutex sdw_read_lock;
struct mutex sdw_write_lock;
struct mutex sdw_clk_lock;
int sdw_clk_users;
int sdw_mclk_users;
int sdw_irq;
int int_mclk1_rsc_ref;
bool int_mclk1_enabled;
bool sdw_npl_clk_enabled;
struct mutex cdc_int_mclk1_mutex;
struct mutex sdw_npl_clk_mutex;
struct delayed_work disable_int_mclk1_work;
struct afe_clk_set sdw_cdc_core_clk;
struct afe_clk_set sdw_npl_clk;
struct notifier_block service_nb;
int (*sdw_cdc_gpio_fn)(bool enable, struct snd_soc_codec *codec);
bool dev_up;
int spkr_gain_offset;
int spkr_mode;
struct mutex codec_mutex;
int rx_4_count;
int rx_5_count;
u32 mclk_rate;
struct regmap *regmap;
bool prev_pg_valid;
u8 prev_pg;
u32 sdw_base_addr;
char __iomem *sdw_base;
u32 version;
/* Entry for version info */
struct snd_info_entry *entry;
struct snd_info_entry *version_entry;
};
extern int msm_sdw_set_spkr_mode(struct snd_soc_codec *codec, int mode);
extern int msm_sdw_set_spkr_gain_offset(struct snd_soc_codec *codec,
int offset);
extern void msm_sdw_gpio_cb(
int (*sdw_cdc_gpio_fn)(bool enable, struct snd_soc_codec *codec),
struct snd_soc_codec *codec);
extern struct regmap *msm_sdw_regmap_init(struct device *dev,
const struct regmap_config *config);
extern int msm_sdw_codec_info_create_codec_entry(
struct snd_info_entry *codec_root,
struct snd_soc_codec *codec);
#endif

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more