blob: ba08c366595213ee690370c585f8f0e852c2ae5f [file] [log] [blame] [edit]
// SPDX-License-Identifier: GPL-2.0
/*
* resample Asoc driver
*
* Copyright (C) 2019 Amlogic, Inc. All rights reserved.
*
*/
#define DEBUG
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include "regs.h"
#include "iomap.h"
#include "resample.h"
#include "resample_hw.h"
#define DRV_NAME "audioresample"
#define CLK_RATIO 256
/*#define __PTM_RESAMPLE_CLK__*/
struct resample_chipinfo {
int num; /* support resample a/b */
enum resample_idx id;
bool dividor_fn;
int resample_version;
bool chnum_sync;
};
struct audioresample {
struct device *dev;
/* mpll0~3, hifi pll, div3~4, gp0 */
struct clk *pll;
/* mst_mclk_a~f, slv_sclk_a~j */
struct clk *sclk;
/* resample clk */
struct clk *clk;
struct resample_chipinfo *chipinfo;
enum resample_idx id;
/* which module should be resampled */
enum toddr_src resample_module;
/* resample to the rate */
int out_rate;
/* sync with auge_resample_texts */
enum samplerate_index asrc_in_sr_idx;
int capture_sample_rate;
bool enable;
unsigned int src_clk_rate;
};
struct audioresample *s_resample_a;
struct audioresample *s_resample_b;
struct audioresample *get_audioresample(enum resample_idx id)
{
struct audioresample *p_resample;
p_resample = ((id == RESAMPLE_A) ? s_resample_a : s_resample_b);
if (!p_resample)
return NULL;
return p_resample;
}
int get_resample_module_num(void)
{
struct audioresample *p_resample = get_audioresample(RESAMPLE_A);
if (p_resample && p_resample->chipinfo)
return p_resample->chipinfo->num;
p_resample = get_audioresample(RESAMPLE_B);
if (p_resample && p_resample->chipinfo)
return p_resample->chipinfo->num;
return 1;
}
int get_resample_version_id(enum resample_idx id)
{
struct audioresample *p_resample;
p_resample = ((id == RESAMPLE_A) ? s_resample_a : s_resample_b);
if (!p_resample || !p_resample->chipinfo) {
pr_debug("Not init audio resample\n");
return -1;
}
return p_resample->chipinfo->resample_version;
}
bool get_resample_enable(enum resample_idx id)
{
struct audioresample *p_resample;
p_resample = ((id == RESAMPLE_A) ? s_resample_a : s_resample_b);
if (!p_resample) {
pr_debug("Not init audio resample\n");
return 0;
}
return p_resample->enable;
}
bool get_resample_enable_chnum_sync(enum resample_idx id)
{
struct audioresample *p_resample;
p_resample = ((id == RESAMPLE_A) ? s_resample_a : s_resample_b);
if (!p_resample || !p_resample->chipinfo) {
pr_debug("Not init audio resample\n");
return false;
}
return p_resample->chipinfo->chnum_sync;
}
int set_resample_source(enum resample_idx id, enum toddr_src src)
{
struct audioresample *p_resample = get_audioresample(id);
(void)p_resample;
(void)src;
//p_resample->resample_module = src;
return 0;
}
static int resample_clk_set(struct audioresample *p_resample, int output_sr)
{
int ret = 0;
/* defaule tdm out mclk to resample clk */
if (p_resample->src_clk_rate)
clk_set_rate(p_resample->pll, p_resample->src_clk_rate);
else
clk_set_rate(p_resample->pll, output_sr * CLK_RATIO * 2);
clk_set_rate(p_resample->sclk, output_sr * CLK_RATIO);
clk_set_rate(p_resample->clk, output_sr * CLK_RATIO);
pr_info("%s, resample_pll:%lu, sclk:%lu, clk:%lu\n",
__func__,
clk_get_rate(p_resample->pll),
clk_get_rate(p_resample->sclk),
clk_get_rate(p_resample->clk));
return ret;
}
static int audio_resample_set(
struct audioresample *p_resample,
bool enable, int rate)
{
if (!p_resample)
return 0;
p_resample->enable = enable;
p_resample->out_rate = rate;
aml_set_resample(p_resample->id,
p_resample->enable,
p_resample->resample_module);
return 0;
}
static const char *const auge_resample_texts[] = {
"Disable",
"Enable:32K",
"Enable:44K",
"Enable:48K",
"Enable:88K",
"Enable:96K",
"Enable:176K",
"Enable:192K",
"Enable:16K",
};
static int resample_idx2rate(enum samplerate_index index)
{
int rate = 0;
if (index == RATE_OFF)
rate = 0;
else if (index == RATE_32K)
rate = 32000;
else if (index == RATE_44K)
rate = 44100;
else if (index == RATE_48K)
rate = 48000;
else if (index == RATE_88K)
rate = 88200;
else if (index == RATE_96K)
rate = 96000;
else if (index == RATE_176K)
rate = 176400;
else if (index == RATE_192K)
rate = 192000;
else if (index == RATE_16K)
rate = 16000;
return rate;
}
static const struct soc_enum auge_resample_enum =
SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(auge_resample_texts),
auge_resample_texts);
static int resample_get_enum(
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct audioresample *p_resample = snd_kcontrol_chip(kcontrol);
int resample_enable = 0;
if (!p_resample || !p_resample->chipinfo) {
pr_debug("audio resample is not init\n");
return 0;
}
if (p_resample->chipinfo->resample_version == 1)
resample_enable = new_resample_get_status(p_resample->id);
else if (p_resample->chipinfo->resample_version == 0)
resample_enable = resample_get_status(p_resample->id);
if (resample_enable)
ucontrol->value.enumerated.item[0] = p_resample->asrc_in_sr_idx;
else
ucontrol->value.enumerated.item[0] = RATE_OFF;
return 0;
}
/* force set to new rate index whatever the resampler holds */
int resample_set(enum resample_idx id, enum samplerate_index index)
{
int resample_rate = 0;
struct audioresample *p_resample = get_audioresample(id);
int ret = 0;
if (!p_resample || !p_resample->chipinfo)
return 0;
if (index < RATE_OFF || index >= RATE_MAX) {
pr_err("%s(), invalid index %d\n", __func__, index);
return 0;
}
p_resample->asrc_in_sr_idx = index;
pr_info("%s resample_%c to %s, last %s\n",
__func__,
(id == RESAMPLE_A) ? 'a' : 'b',
auge_resample_texts[index],
auge_resample_texts[p_resample->asrc_in_sr_idx]);
resample_rate = resample_idx2rate(index);
ret = audio_resample_set(p_resample, index, resample_rate);
if (ret)
return ret;
if (index == RATE_OFF) {
if (p_resample->chipinfo->resample_version == 1)
new_resample_enable(p_resample->id, false);
else if (p_resample->chipinfo->resample_version == 0)
resample_enable(p_resample->id, false);
} else {
if (p_resample->chipinfo->resample_version == 1) {
new_resample_set_ratio(id, resample_rate,
DEFAULT_SPK_SAMPLERATE);
} else if (p_resample->chipinfo->resample_version == 0) {
resample_init(p_resample->id, resample_rate);
resample_set_hw_param(p_resample->id, index);
}
}
return 0;
}
static int resample_set_enum(
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct audioresample *p_resample = snd_kcontrol_chip(kcontrol);
int index = ucontrol->value.enumerated.item[0];
if (!p_resample) {
pr_debug("audio resample is not init\n");
return 0;
}
if (index == p_resample->asrc_in_sr_idx)
return 0;
resample_set(p_resample->id, index);
return 0;
}
static int mixer_audiobus_read(
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
unsigned int shift = mc->shift;
unsigned int max = mc->max;
unsigned int invert = mc->invert;
unsigned int value =
(((unsigned int)audiobus_read(reg)) >> shift) & max;
if (invert)
value = (~value) & max;
ucontrol->value.integer.value[0] = value;
return 0;
}
static int mixer_audiobus_write(
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
unsigned int shift = mc->shift;
unsigned int max = mc->max;
unsigned int invert = mc->invert;
unsigned int value = ucontrol->value.integer.value[0];
unsigned int reg_value = (unsigned int)audiobus_read(reg);
if (invert)
value = (~value) & max;
max = ~(max << shift);
reg_value &= max;
reg_value |= (value << shift);
audiobus_write(reg, reg_value);
return 0;
}
/* resample module
* keep sync with enum toddr_src in ddr_mngr.h
*/
static const char *const auge_resample_module_texts[] = {
"TDMIN_A",
"TDMIN_B",
"TDMIN_C",
"SPDIFIN",
"PDMIN",
"FRATV", /* NONE for axg, g12a, g12b */
"TDMIN_LB",
"LOOPBACK_A",
"FRHDMIRX", /* from tl1 chipset*/
"LOOPBACK_B",
"SPDIFIN_LB",
"RESERVED_0",
"RESERVED_1",
"RESERVED_2",
"RESERVED_3",
"VAD",
};
static const struct soc_enum auge_resample_module_enum =
SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(auge_resample_module_texts),
auge_resample_module_texts);
static int resample_module_get_enum(
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct audioresample *p_resample = snd_kcontrol_chip(kcontrol);
if (!p_resample) {
pr_debug("audio resample is not init\n");
return 0;
}
ucontrol->value.enumerated.item[0] = p_resample->resample_module;
return 0;
}
static int resample_module_set_enum(
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct audioresample *p_resample = snd_kcontrol_chip(kcontrol);
if (!p_resample) {
pr_debug("audio resample is not init\n");
return 0;
}
p_resample->resample_module = ucontrol->value.enumerated.item[0];
/* update info to ddr */
aml_set_resample(p_resample->id,
p_resample->enable,
p_resample->resample_module);
return 0;
}
static const struct snd_kcontrol_new asrc_a_controls[] = {
SOC_ENUM_EXT("Hardware resample enable",
auge_resample_enum,
resample_get_enum,
resample_set_enum),
SOC_SINGLE_EXT_TLV("Hw resample pause enable",
EE_AUDIO_RESAMPLEA_CTRL2, 24, 0x1, 0,
mixer_audiobus_read, mixer_audiobus_write,
NULL),
SOC_SINGLE_EXT_TLV("Hw resample pause thd",
EE_AUDIO_RESAMPLEA_CTRL2, 11, 0x1fff, 0,
mixer_audiobus_read, mixer_audiobus_write,
NULL),
SOC_ENUM_EXT("Hw resample module",
auge_resample_module_enum,
resample_module_get_enum,
resample_module_set_enum),
};
static const struct snd_kcontrol_new asrc_b_controls[] = {
SOC_ENUM_EXT("Hardware resample b enable",
auge_resample_enum,
resample_get_enum,
resample_set_enum),
SOC_SINGLE_EXT_TLV("Hw resample b pause enable",
EE_AUDIO_RESAMPLEB_CTRL2, 24, 0x1, 0,
mixer_audiobus_read, mixer_audiobus_write,
NULL),
SOC_SINGLE_EXT_TLV("Hw resample b pause thd",
EE_AUDIO_RESAMPLEB_CTRL2, 11, 0x1fff, 0,
mixer_audiobus_read, mixer_audiobus_write,
NULL),
SOC_ENUM_EXT("Hw resample b module",
auge_resample_module_enum,
resample_module_get_enum,
resample_module_set_enum),
};
static const struct snd_kcontrol_new rsamp_a_controls[] = {
SOC_ENUM_EXT("Hardware resample enable",
auge_resample_enum,
resample_get_enum,
resample_set_enum),
SOC_ENUM_EXT("Hw resample module",
auge_resample_module_enum,
resample_module_get_enum,
resample_module_set_enum),
};
int card_add_resample_kcontrols(struct snd_soc_card *card)
{
unsigned int idx, i;
int err = 0;
struct snd_kcontrol *kcontrol;
if (s_resample_a && s_resample_a->chipinfo) {
if (s_resample_a->chipinfo->resample_version == 1) {
i = ARRAY_SIZE(rsamp_a_controls);
for (idx = 0; idx < i; idx++) {
kcontrol = snd_ctl_new1(&rsamp_a_controls[idx],
s_resample_a);
err = snd_ctl_add(card->snd_card, kcontrol);
}
} else if (s_resample_a->chipinfo->resample_version == 0) {
i = ARRAY_SIZE(asrc_a_controls);
for (idx = 0; idx < i; idx++) {
kcontrol = snd_ctl_new1(&asrc_a_controls[idx],
s_resample_a);
err = snd_ctl_add(card->snd_card, kcontrol);
}
}
if (err < 0)
return err;
}
if (s_resample_b && s_resample_b->chipinfo) {
if (s_resample_b->chipinfo->resample_version == 0) {
i = ARRAY_SIZE(asrc_b_controls);
for (idx = 0; idx < i; idx++) {
kcontrol = snd_ctl_new1(&asrc_b_controls[idx],
s_resample_b);
err = snd_ctl_add(card->snd_card, kcontrol);
}
if (err < 0)
return err;
}
}
return 0;
}
static int new_resample_init(struct audioresample *p_resample)
{
if (!p_resample)
return -ENOMEM;
pr_info("%s: Start init new resample %s parameters!\n",
__func__, (p_resample->id == RESAMPLE_A) ? "A" : "B");
p_resample->enable = 1;
new_resample_init_param(p_resample->id);
if (p_resample->id == RESAMPLE_A) {
/* default resample A for tv input source */
new_resample_set_ratio(p_resample->id,
DEFAULT_SPK_SAMPLERATE,
DEFAULT_SPK_SAMPLERATE);
} else if (p_resample->id == RESAMPLE_B) {
/* default resample B for loopback downsample */
new_resample_set_ratio(p_resample->id,
DEFAULT_SPK_SAMPLERATE,
p_resample->capture_sample_rate);
new_resampleB_set_format(p_resample->id,
p_resample->capture_sample_rate,
clk_get_rate(p_resample->sclk));
}
return 0;
}
static struct resample_chipinfo axg_resample_chipinfo = {
.resample_version = 0,
};
static struct resample_chipinfo g12a_resample_chipinfo = {
.dividor_fn = true,
.resample_version = 0,
};
static struct resample_chipinfo tl1_resample_a_chipinfo = {
.num = 2,
.id = RESAMPLE_A,
.dividor_fn = true,
.resample_version = 0,
};
static struct resample_chipinfo tl1_resample_b_chipinfo = {
.num = 2,
.id = RESAMPLE_B,
.dividor_fn = true,
.resample_version = 0,
};
static struct resample_chipinfo sm1_resample_a_chipinfo = {
.num = 2,
.id = RESAMPLE_A,
.dividor_fn = true,
.resample_version = 1,
};
static struct resample_chipinfo sm1_resample_b_chipinfo = {
.num = 2,
.id = RESAMPLE_B,
.dividor_fn = true,
.resample_version = 1,
};
static struct resample_chipinfo a1_resample_a_chipinfo = {
.num = 2, /* virtual numid, valid is 1 */
.id = RESAMPLE_A,
.dividor_fn = true,
.resample_version = 1,
};
static struct resample_chipinfo a1_resample_b_chipinfo = {
.num = 2, /* virtual numid, valid is 1 */
.id = RESAMPLE_B,
.dividor_fn = true,
.resample_version = 1,
};
static struct resample_chipinfo c2_resample_a_chipinfo = {
.num = 2, /* virtual numid, valid is 1 */
.id = RESAMPLE_A,
.dividor_fn = true,
.resample_version = 1,
.chnum_sync = true,
};
static struct resample_chipinfo c2_resample_b_chipinfo = {
.num = 2, /* virtual numid, valid is 1 */
.id = RESAMPLE_B,
.dividor_fn = true,
.resample_version = 1,
.chnum_sync = true,
};
static const struct of_device_id resample_device_id[] = {
{
.compatible = "amlogic, axg-resample",
.data = &axg_resample_chipinfo,
},
{
.compatible = "amlogic, g12a-resample",
.data = &g12a_resample_chipinfo,
},
{
.compatible = "amlogic, tl1-resample-a",
.data = &tl1_resample_a_chipinfo,
},
{
.compatible = "amlogic, tl1-resample-b",
.data = &tl1_resample_b_chipinfo,
},
{
.compatible = "amlogic, sm1-resample-a",
.data = &sm1_resample_a_chipinfo,
},
{
.compatible = "amlogic, sm1-resample-b",
.data = &sm1_resample_b_chipinfo,
},
{
.compatible = "amlogic, a1-resample-a",
.data = &a1_resample_a_chipinfo,
},
{
.compatible = "amlogic, a1-resample-b",
.data = &a1_resample_b_chipinfo,
},
{
.compatible = "amlogic, c2-resample-a",
.data = &c2_resample_a_chipinfo,
},
{
.compatible = "amlogic, c2-resample-b",
.data = &c2_resample_b_chipinfo,
},
{}
};
MODULE_DEVICE_TABLE(of, resample_device_id);
static int resample_platform_probe(struct platform_device *pdev)
{
struct audioresample *p_resample;
struct device *dev = &pdev->dev;
struct resample_chipinfo *p_chipinfo;
unsigned int resample_module;
int ret;
pr_info("%s\n", __func__);
p_resample = devm_kzalloc(&pdev->dev,
sizeof(struct audioresample),
GFP_KERNEL);
if (!p_resample) {
/*dev_err(&pdev->dev, "Can't allocate pcm_p\n");*/
return -ENOMEM;
}
/* match data */
p_chipinfo = (struct resample_chipinfo *)
of_device_get_match_data(dev);
if (!p_chipinfo) {
dev_warn_once(dev, "check whether to update resample chipinfo\n");
return -EINVAL;
}
p_resample->id = p_chipinfo->id;
p_resample->chipinfo = p_chipinfo;
if (p_chipinfo->id == 0) {
ret = of_property_read_u32(pdev->dev.of_node, "resample_module",
&resample_module);
if (ret < 0) {
dev_err(&pdev->dev, "Can't retrieve resample_module\n");
return -EINVAL;
}
} else {
resample_module = LOOPBACK_A;
ret = of_property_read_u32(pdev->dev.of_node,
"capture_sample_rate",
&p_resample->capture_sample_rate);
if (ret < 0 ||
p_resample->capture_sample_rate != DEFAULT_SPK_SAMPLERATE) {
p_resample->capture_sample_rate =
DEFAULT_MIC_SAMPLERATE;
}
}
/* config from dts */
p_resample->resample_module = resample_module;
p_resample->pll = devm_clk_get(&pdev->dev, "resample_pll");
if (IS_ERR(p_resample->pll)) {
dev_err(&pdev->dev,
"Can't retrieve resample_pll clock\n");
ret = PTR_ERR(p_resample->pll);
return ret;
}
ret = of_property_read_u32(dev->of_node, "src-clk-freq",
&p_resample->src_clk_rate);
if (ret < 0)
p_resample->src_clk_rate = 0;
pr_info("%s src clk rate from dts:%d\n",
__func__, p_resample->src_clk_rate);
p_resample->sclk = devm_clk_get(&pdev->dev, "resample_src");
if (IS_ERR(p_resample->sclk)) {
dev_err(&pdev->dev,
"Can't retrieve resample_src clock\n");
ret = PTR_ERR(p_resample->sclk);
return ret;
}
p_resample->clk = devm_clk_get(&pdev->dev, "resample_clk");
if (IS_ERR(p_resample->clk)) {
dev_err(&pdev->dev,
"Can't retrieve resample_clk clock\n");
ret = PTR_ERR(p_resample->clk);
return ret;
}
ret = clk_set_parent(p_resample->sclk, p_resample->pll);
if (ret) {
dev_err(&pdev->dev,
"Can't set resample_src parent clock\n");
return ret;
}
ret = clk_set_parent(p_resample->clk, p_resample->sclk);
if (ret) {
dev_err(&pdev->dev,
"Can't set resample_clk parent clock\n");
return ret;
}
p_resample->dev = dev;
if (p_chipinfo && p_chipinfo->id == 1)
s_resample_b = p_resample;
else
s_resample_a = p_resample;
resample_clk_set(p_resample, DEFAULT_SPK_SAMPLERATE);
ret = clk_prepare_enable(p_resample->clk);
if (ret) {
pr_err("Can't enable resample_clk clock: %d\n",
ret);
return ret;
}
if (p_chipinfo && p_chipinfo->resample_version == 1) {
new_resample_init(p_resample);
if (p_chipinfo->chnum_sync)
aml_resample_chsync_enable();
}
aml_set_resample(p_resample->id, p_resample->enable,
p_resample->resample_module);
pr_info("resample id = %d, new resample = %d, resample_module = %d\n",
p_chipinfo->id, p_chipinfo->resample_version,
p_resample->resample_module);
return 0;
}
static struct platform_driver resample_platform_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(resample_device_id),
},
.probe = resample_platform_probe,
};
module_platform_driver(resample_platform_driver);