Implement TDM driver for JH7110 SoC

Signed-off-by: Walker Chen <walker.chen@starfivetech.com>
This commit is contained in:
Walker Chen 2022-06-20 20:54:16 +08:00
parent 70a6a66c7e
commit edd12a5579
13 changed files with 590 additions and 324 deletions

View file

@ -0,0 +1,17 @@
&sound{
simple-audio-card,dai-link@0 {
reg = <0>;
status = "okay";
format = "i2s";
bitclock-master = <&dailink_master>;
frame-master = <&dailink_master>;
dailink_master:cpu {
sound-dai = <&i2srx_3ch>;
};
dailink_slave:codec {
sound-dai = <&pdm>;
};
};
};

View file

@ -0,0 +1,34 @@
&sound{
simple-audio-card,dai-link@0 {
reg = <0>;
status = "okay";
format = "dsp_a";
bitclock-master = <&dailink_master>;
frame-master = <&dailink_master>;
widgets =
"Microphone", "Mic Jack",
"Line", "Line In",
"Line", "Line Out",
"Speaker", "Speaker",
"Headphone", "Headphone Jack";
routing =
"Headphone Jack", "HP_L",
"Headphone Jack", "HP_R",
"Speaker", "SPK_LP",
"Speaker", "SPK_LN",
"LINPUT1", "Mic Jack",
"LINPUT3", "Mic Jack",
"RINPUT1", "Mic Jack",
"RINPUT2", "Mic Jack";
cpu {
sound-dai = <&tdm>;
};
dailink_master:codec {
sound-dai = <&wm8960>;
clocks = <&wm8960_mclk>;
clock-names = "mclk";
};
};
};

View file

@ -438,7 +438,7 @@
&tdm {
pinctrl-names = "default";
pinctrl-0 = <&tdm0_pins>;
status = "disabled";
status = "okay";
};
&spdif0 {

View file

@ -1035,10 +1035,14 @@
<&clkgen JH7110_TDM_CLK_AHB>,
<&clkgen JH7110_APB0>,
<&clkgen JH7110_TDM_CLK_APB>,
<&clkgen JH7110_TDM_INTERNAL>;
<&clkgen JH7110_TDM_INTERNAL>,
<&tdm_ext>,
<&clkgen JH7110_TDM_CLK_TDM>,
<&clkgen JH7110_MCLK_INNER>;
clock-names = "clk_ahb0", "clk_tdm_ahb",
"clk_apb0", "clk_tdm_apb",
"clk_tdm_intl";
"clk_tdm_internal", "clk_tdm_ext",
"clk_tdm", "mclk_inner";
resets = <&rstgen RSTN_U0_TDM16SLOT_AHB>,
<&rstgen RSTN_U0_TDM16SLOT_APB>,
<&rstgen RSTN_U0_TDM16SLOT_TDM>;
@ -1588,33 +1592,34 @@
simple-audio-card,dai-link@0 {
reg = <0>;
format = "left_j";
bitclock-master = <&sndcpu0>;
frame-master = <&sndcpu0>;
status = "okay";
sndcpu0: cpu {
sound-dai = <&pwmdac>;
};
codec {
sound-dai = <&pwmdac_codec>;
};
};
simple-audio-card,dai-link@1 {
reg = <0>;
status = "okay";
format = "i2s";
format = "dsp_a";
bitclock-master = <&dailink_master>;
frame-master = <&dailink_master>;
dailink_master:cpu {
sound-dai = <&i2srx_3ch>;
widgets =
"Microphone", "Mic Jack",
"Line", "Line In",
"Line", "Line Out",
"Speaker", "Speaker",
"Headphone", "Headphone Jack";
routing =
"Headphone Jack", "HP_L",
"Headphone Jack", "HP_R",
"Speaker", "SPK_LP",
"Speaker", "SPK_LN",
"LINPUT1", "Mic Jack",
"LINPUT3", "Mic Jack",
"RINPUT1", "Mic Jack",
"RINPUT2", "Mic Jack";
cpu {
sound-dai = <&tdm>;
};
dailink_slave:codec {
sound-dai = <&pdm>;
dailink_master:codec {
sound-dai = <&wm8960>;
clocks = <&wm8960_mclk>;
clock-names = "mclk";
};
};
};

View file

@ -117,4 +117,10 @@
#clock-cells = <0>;
clock-frequency = <297000000>;
};
wm8960_mclk: wm8960_mclk {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <24576000>;
};
};

View file

@ -198,8 +198,8 @@
i2c0_pins: i2c0-pins {
i2c0-pins-scl {
sf,pins = <PAD_GPIO36>;
sf,pinmux = <PAD_GPIO36_FUNC_SEL 0>;
sf,pins = <PAD_GPIO57>;
sf,pinmux = <PAD_GPIO57_FUNC_SEL 0>;
sf,pin-ioconfig = <IO(GPIO_IE(1)|(GPIO_PU(1)))>;
sf,pin-gpio-dout = <GPO_LOW>;
sf,pin-gpio-doen = <OEN_I2C0_IC_CLK_OE>;
@ -207,8 +207,8 @@
};
i2c0-pins-sda {
sf,pins = <PAD_GPIO37>;
sf,pinmux = <PAD_GPIO37_FUNC_SEL 0>;
sf,pins = <PAD_GPIO58>;
sf,pinmux = <PAD_GPIO58_FUNC_SEL 0>;
sf,pin-ioconfig = <IO(GPIO_IE(1)|(GPIO_PU(1)))>;
sf,pin-gpio-dout = <GPO_LOW>;
sf,pin-gpio-doen = <OEN_I2C0_IC_DATA_OE>;
@ -412,6 +412,7 @@
};
pwmdac0_pins: pwmdac0-pins {
/*
pwmdac0-pins-left {
sf,pins = <PAD_GPIO57>;
sf,pinmux = <PAD_GPIO57_FUNC_SEL 0>;
@ -419,6 +420,7 @@
sf,pin-gpio-dout = <GPO_PWMDAC0_LEFT_OUTPUT>;
sf,pin-gpio-doen = <OEN_LOW>;
};
*/
pwmdac0-pins-right {
sf,pins = <PAD_GPIO42>;
@ -449,47 +451,40 @@
tdm0_pins: tdm0-pins {
tdm0-pins-tx {
sf,pins = <PAD_GPIO21>;
sf,pinmux = <PAD_GPIO21_FUNC_SEL 0>;
sf,pins = <PAD_GPIO44>;
sf,pinmux = <PAD_GPIO44_FUNC_SEL 0>;
sf,pin-ioconfig = <IO(GPIO_IE(1))>;
sf,pin-gpio-dout = <GPO_TDM0_PCM_TXD>;
sf,pin-gpio-doen = <OEN_LOW>;
};
tdm0-pins-rx {
sf,pins = <PAD_GPIO15>;
sf,pinmux = <PAD_GPIO15_FUNC_SEL 0>;
sf,pins = <PAD_GPIO61>;
sf,pinmux = <PAD_GPIO61_FUNC_SEL 0>;
sf,pin-ioconfig = <IO(GPIO_IE(1))>;
sf,pin-gpio-doen = <OEN_HIGH>;
sf,pin-gpio-din = <GPI_TDM0_PCM_RXD>;
};
tdm0-pins-sync {
sf,pins = <PAD_GPIO22>;
sf,pinmux = <PAD_GPIO22_FUNC_SEL 0>;
sf,pins = <PAD_GPIO63>;
sf,pinmux = <PAD_GPIO63_FUNC_SEL 0>;
sf,pin-ioconfig = <IO(GPIO_IE(1))>;
sf,pin-gpio-dout = <GPO_TDM0_PCM_SYNCOUT>;
sf,pin-gpio-doen = <OEN_LOW>;
sf,pin-gpio-doen = <OEN_HIGH>;
sf,pin-gpio-din = <GPI_TDM0_PCM_SYNCIN>;
};
tdm0-pins-mclk {
sf,pins = <PAD_GPIO30>;
sf,pinmux = <PAD_GPIO30_FUNC_SEL 0>;
tdm0-pins-pcmclk {
sf,pins = <PAD_GPIO38>;
sf,pinmux = <PAD_GPIO38_FUNC_SEL 0>;
sf,pin-ioconfig = <IO(GPIO_IE(1))>;
sf,pin-gpio-dout = <GPO_CRG0_MCLK_OUT>;
sf,pin-gpio-doen = <OEN_LOW>;
};
tdm0-pins-mst {
sf,pins = <PAD_GPIO39>;
sf,pinmux = <PAD_GPIO39_FUNC_SEL 0>;
sf,pin-ioconfig = <IO(GPIO_IE(1))>;
sf,pin-gpio-dout = <GPO_TDM0_CLK_MST>;
sf,pin-gpio-doen = <OEN_LOW>;
sf,pin-gpio-doen = <OEN_HIGH>;
sf,pin-gpio-din = <GPI_TDM0_CLK_SLV>;
};
};
i2s_clk_pins: i2s-clk0 {
/*
i2s-clk0_mclk {
sf,pins = <PAD_GPIO58>;
sf,pinmux = <PAD_GPIO58_FUNC_SEL 0>;
@ -497,6 +492,7 @@
sf,pin-gpio-dout = <GPO_CRG0_MCLK_OUT>;
sf,pin-gpio-doen = <OEN_LOW>;
};
*/
};
i2stx_pins: i2stx-pins {
@ -937,11 +933,13 @@
sf,pinmux = <PAD_GPIO37_FUNC_SEL 1>;
sf,pin-ioconfig = <IO(GPIO_IE(0))>;
};
/*
rgb-2-pins {
sf,pins = <PAD_GPIO38>;
sf,pinmux = <PAD_GPIO38_FUNC_SEL 1>;
sf,pin-ioconfig = <IO(GPIO_IE(0))>;
};
*/
rgb-3-pins {
sf,pins = <PAD_GPIO39>;
sf,pinmux = <PAD_GPIO39_FUNC_SEL 1>;
@ -967,11 +965,13 @@
sf,pinmux = <PAD_GPIO43_FUNC_SEL 1>;
sf,pin-ioconfig = <IO(GPIO_IE(0))>;
};
/*
rgb-8-pins {
sf,pins = <PAD_GPIO44>;
sf,pinmux = <PAD_GPIO44_FUNC_SEL 1>;
sf,pin-ioconfig = <IO(GPIO_IE(0))>;
};
*/
rgb-9-pins {
sf,pins = <PAD_GPIO45>;
sf,pinmux = <PAD_GPIO45_FUNC_SEL 1>;
@ -1032,6 +1032,7 @@
sf,pinmux = <PAD_GPIO56_FUNC_SEL 1>;
sf,pin-ioconfig = <IO(GPIO_IE(0))>;
};
/*
rgb-21-pins {
sf,pins = <PAD_GPIO57>;
sf,pinmux = <PAD_GPIO57_FUNC_SEL 1>;
@ -1042,6 +1043,7 @@
sf,pinmux = <PAD_GPIO58_FUNC_SEL 1>;
sf,pin-ioconfig = <IO(GPIO_IE(0))>;
};
*/
rgb-23-pins {
sf,pins = <PAD_GPIO59>;
sf,pinmux = <PAD_GPIO59_FUNC_SEL 1>;
@ -1062,11 +1064,13 @@
sf,pinmux = <PAD_GPIO62_FUNC_SEL 1>;
sf,pin-ioconfig = <IO(GPIO_IE(0))>;
};
/*
rgb-27-pins {
sf,pins = <PAD_GPIO63>;
sf,pinmux = <PAD_GPIO63_FUNC_SEL 1>;
sf,pin-ioconfig = <IO(GPIO_IE(0))>;
};
*/
};
inno_hdmi_pins: inno_hdmi-pins {
inno_hdmi-scl {
@ -1114,11 +1118,13 @@
sf,pinmux = <PAD_GPIO37_FUNC_SEL 1>;
sf,pin-ioconfig = <IO(GPIO_IE(0))>;
};
/*
mipitx-3-pins {
sf,pins = <PAD_GPIO38>;
sf,pinmux = <PAD_GPIO38_FUNC_SEL 1>;
sf,pin-ioconfig = <IO(GPIO_IE(0))>;
};
*/
mipitx-4-pins {
sf,pins = <PAD_GPIO39>;
sf,pinmux = <PAD_GPIO39_FUNC_SEL 1>;

View file

@ -192,10 +192,8 @@ CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_SOC=y
CONFIG_SND_DESIGNWARE_I2S=y
CONFIG_SND_DESIGNWARE_I2S_STARFIVE_JH7110=y
CONFIG_SND_STARFIVE_PWMDAC=y
CONFIG_SND_STARFIVE_PDM=y
CONFIG_SND_STARFIVE_TDM=y
CONFIG_SND_SOC_WM8960=y
CONFIG_SND_SIMPLE_CARD=y
CONFIG_USB=y
CONFIG_USB_XHCI_HCD=y

View file

@ -198,11 +198,18 @@ static inline void axi_chan_disable(struct axi_dma_chan *chan)
{
struct dma_multi *multi = &chan->chip->multi;
u32 val;
int ret;
u32 chan_active = BIT(chan->id) << DMAC_CHAN_EN_SHIFT;
val = axi_dma_ioread32(chan->chip, multi->en.ch_en);
val &= ~(BIT(chan->id) << multi->en.ch_en_shift);
val |= BIT(chan->id) << multi->en.ch_en_we_shift;
axi_dma_iowrite32(chan->chip, multi->en.ch_en, val);
ret = readl_poll_timeout_atomic(chan->chip->regs + DMAC_CHEN, val,
!(val & chan_active), 10, 100000); //10 ms
if (ret == -ETIMEDOUT)
pr_info("dma: failed to stop\n");
}
static inline void axi_chan_enable(struct axi_dma_chan *chan)
@ -337,13 +344,12 @@ dma_chan_tx_status(struct dma_chan *dchan, dma_cookie_t cookie,
len = vd_to_axi_desc(vdesc)->hw_desc[0].len;
completed_length = completed_blocks * len;
bytes = length - completed_length;
spin_unlock_irqrestore(&chan->vc.lock, flags);
dma_set_residue(txstate, bytes);
} else {
bytes = vd_to_axi_desc(vdesc)->length;
spin_unlock_irqrestore(&chan->vc.lock, flags);
}
spin_unlock_irqrestore(&chan->vc.lock, flags);
dma_set_residue(txstate, bytes);
return status;
}
@ -380,6 +386,7 @@ static void dw_axi_dma_set_byte_halfword(struct axi_dma_chan *chan, bool set)
iowrite32(val, chan->chip->apb_regs + offset);
}
/* Called in chan locked context */
static void axi_chan_block_xfer_start(struct axi_dma_chan *chan,
struct axi_dma_desc *first)
@ -389,12 +396,11 @@ static void axi_chan_block_xfer_start(struct axi_dma_chan *chan,
u32 reg_lo, reg_hi, irq_mask;
u8 lms = 0; /* Select AXI0 master for LLI fetching */
chan->is_err = false;
if (unlikely(axi_chan_is_hw_enable(chan))) {
//printk(KERN_INFO ">>>>>>>>>>axi_chan_block_xfer_start\n");
dev_err(chan2dev(chan), "%s is non-idle!\n",
axi_chan_name(chan));
axi_chan_disable(chan);
chan->is_err = true;
//return;
}
@ -522,6 +528,7 @@ static int dma_chan_alloc_chan_resources(struct dma_chan *dchan)
/* ASSERT: channel is idle */
if (axi_chan_is_hw_enable(chan)) {
printk(KERN_INFO ">>>>>>>>>>dma_chan_alloc_chan_resources\n");
dev_err(chan2dev(chan), "%s is non-idle!\n",
axi_chan_name(chan));
return -EBUSY;
@ -549,6 +556,7 @@ static void dma_chan_free_chan_resources(struct dma_chan *dchan)
/* ASSERT: channel is idle */
if (axi_chan_is_hw_enable(chan))
printk(KERN_INFO ">>>>>>>>>>dma_chan_free_chan_resources\n");
dev_err(dchan2dev(dchan), "%s is non-idle!\n",
axi_chan_name(chan));
@ -572,7 +580,7 @@ static void dw_axi_dma_set_hw_channel(struct axi_dma_chan *chan, bool set)
unsigned long reg_value, val;
if (!chip->apb_regs) {
dev_err(chip->dev, "apb_regs not initialized\n");
dev_dbg(chip->dev, "apb_regs not initialized\n");
return;
}
@ -1068,16 +1076,23 @@ static void axi_chan_list_dump_lli(struct axi_dma_chan *chan,
axi_chan_dump_lli(chan, &desc_head->hw_desc[i]);
}
static noinline void axi_chan_handle_err(struct axi_dma_chan *chan, u32 status)
static void axi_chan_tasklet(struct tasklet_struct *t)
{
struct axi_dma_chan *chan = from_tasklet(chan, t, dma_tasklet);
struct virt_dma_desc *vd;
u32 chan_active = BIT(chan->id) << DMAC_CHAN_EN_SHIFT;
unsigned long flags;
struct axi_dma_desc *desc;
u32 val;
int ret;
ret = readl_poll_timeout_atomic(chan->chip->regs + DMAC_CHEN, val,
!(val & chan_active), 10, 1000);
if (ret == -ETIMEDOUT)
dev_warn(chan2dev(chan),
"irq %s failed to stop\n", axi_chan_name(chan));
spin_lock_irqsave(&chan->vc.lock, flags);
axi_chan_disable(chan);
/* The bad descriptor currently is in the head of vc list */
vd = vchan_next_desc(&chan->vc);
if (!vd) {
@ -1086,18 +1101,18 @@ static noinline void axi_chan_handle_err(struct axi_dma_chan *chan, u32 status)
spin_unlock_irqrestore(&chan->vc.lock, flags);
return;
}
if (chan->is_err) {
desc = vd_to_axi_desc(vd);
axi_chan_block_xfer_start(chan, desc);
chan->is_err = false;
if (chan->cyclic) {
vchan_cyclic_callback(vd);
axi_chan_enable(chan);
} else {
/* Remove the completed descriptor from issued list */
list_del(&vd->node);
/* WARN about bad descriptor */
dev_err(chan2dev(chan),
"Bad descriptor submitted for %s, cookie: %d, irq: 0x%08x\n",
axi_chan_name(chan), vd->tx.cookie, status);
"Bad descriptor submitted for %s, cookie: %d\n",
axi_chan_name(chan), vd->tx.cookie);
axi_chan_list_dump_lli(chan, vd_to_axi_desc(vd));
vchan_cookie_complete(vd);
@ -1109,6 +1124,20 @@ static noinline void axi_chan_handle_err(struct axi_dma_chan *chan, u32 status)
spin_unlock_irqrestore(&chan->vc.lock, flags);
}
static noinline void axi_chan_handle_err(struct axi_dma_chan *chan, u32 status)
{
unsigned long flags;
spin_lock_irqsave(&chan->vc.lock, flags);
if (unlikely(axi_chan_is_hw_enable(chan))) {
axi_chan_disable(chan);
}
tasklet_schedule(&chan->dma_tasklet);
spin_unlock_irqrestore(&chan->vc.lock, flags);
}
static void axi_chan_block_xfer_complete(struct axi_dma_chan *chan)
{
int count = atomic_read(&chan->descs_allocated);
@ -1537,6 +1566,7 @@ static int dw_probe(struct platform_device *pdev)
chan->vc.desc_free = vchan_desc_put;
vchan_init(&chan->vc, &dw->dma);
tasklet_setup(&chan->dma_tasklet, axi_chan_tasklet);
}
/* Set capabilities */

View file

@ -77,9 +77,10 @@ struct axi_dma_chan {
enum dma_transfer_direction direction;
bool fixed_burst_trans_len;
bool cyclic;
bool is_err;
//bool is_err;
/* these other elements are all protected by vc.lock */
bool is_paused;
struct tasklet_struct dma_tasklet;
};
struct dw_axi_dma {

View file

@ -94,7 +94,7 @@ static int starfive_dt_node_to_map(struct pinctrl_dev *pctldev,
return -EINVAL;
}
nmaps = size / pin_size;
nmaps = size / pin_size * 2;
ngroups = size / pin_size;
pgnames = devm_kcalloc(dev, ngroups, sizeof(*pgnames), GFP_KERNEL);
@ -133,6 +133,11 @@ static int starfive_dt_node_to_map(struct pinctrl_dev *pctldev,
}
pgnames[ngroups++] = grpname;
map[nmaps].type = PIN_MAP_TYPE_MUX_GROUP;
map[nmaps].data.mux.function = np->name;
map[nmaps].data.mux.group = grpname;
nmaps += 1;
list = of_get_property(child, "sf,pins", &psize);
if (!list) {
@ -155,22 +160,30 @@ static int starfive_dt_node_to_map(struct pinctrl_dev *pctldev,
info->starfive_pinctrl_parse_pin(sfp,
pins_id, pin_data, list, child);
map[nmaps].type = PIN_MAP_TYPE_CONFIGS_PIN;
map[nmaps].data.configs.group_or_pin =
pin_get_name(pctldev, pin_data->pin);
map[nmaps].data.configs.configs =
&pin_data->pin_config.io_config;
map[nmaps].data.configs.num_configs = 1;
nmaps += 1;
list++;
}
offset += i;
/*
map[nmaps].type = PIN_MAP_TYPE_MUX_GROUP;
map[nmaps].data.mux.function = np->name;
map[nmaps].data.mux.group = grpname;
nmaps += 1;
*/
ret = pinctrl_generic_add_group(pctldev,
grpname, pins_id, child_num_pins, pin_data);
if (ret < 0) {
dev_err(dev, "error adding group %s: %d\n", grpname, ret);
goto put_child;
}
#if 0
ret = pinconf_generic_parse_dt_config(child, pctldev,
&map[nmaps].data.configs.configs,
&map[nmaps].data.configs.num_configs);
@ -187,6 +200,7 @@ static int starfive_dt_node_to_map(struct pinctrl_dev *pctldev,
map[nmaps].type = PIN_MAP_TYPE_CONFIGS_GROUP;
map[nmaps].data.configs.group_or_pin = grpname;
nmaps += 1;
#endif
}
ret = pinmux_generic_add_function(pctldev, np->name, pgnames, ngroups, NULL);

View file

@ -15,6 +15,7 @@
#include <linux/clk.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/debugfs.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@ -22,11 +23,11 @@
#include <sound/initval.h>
#include <sound/tlv.h>
#include <sound/wm8960.h>
#include <linux/debugfs.h>
#include "wm8960.h"
#define WM8960_MCLK 51200000//4096000
#define WM8960_MCLK 24000000
/* R25 - Power 1 */
#define WM8960_VMID_MASK 0x180
#define WM8960_VREF 0x40
@ -123,7 +124,7 @@ struct wm8960_priv {
struct clk *mclk;
struct regmap *regmap;
int (*set_bias_level)(struct snd_soc_component *,
enum snd_soc_bias_level level);
enum snd_soc_bias_level level);
struct snd_soc_dapm_widget *lout1;
struct snd_soc_dapm_widget *rout1;
struct snd_soc_dapm_widget *out3;
@ -153,6 +154,7 @@ static const char *wm8960_adc_data_output_sel[] = {
"Left Data = Right ADC; Right Data = Right ADC",
"Left Data = Right ADC; Right Data = Left ADC",
};
static const char *wm8960_dmonomix[] = {"Stereo", "Mono"};
static const struct soc_enum wm8960_enum[] = {
@ -514,7 +516,7 @@ static int wm8960_add_widgets(struct snd_soc_component *component)
if (strcmp(w->name, "OUT3 VMID") == 0)
wm8960->out3 = w;
}
return 0;
}
@ -697,13 +699,6 @@ int wm8960_configure_pll(struct snd_soc_component *component, int freq_in,
lrclk = wm8960->lrclk;
closest = freq_in;
/* Judge whether the lr clock is 0, if equal to 0, there is
* no need to perform the following steps*/
if (!lrclk)
{
return 0;
}
best_freq_out = -EINVAL;
*sysclk_idx = *dac_idx = *bclk_idx = -1;
@ -744,6 +739,7 @@ int wm8960_configure_pll(struct snd_soc_component *component, int freq_in,
return best_freq_out;
}
static int wm8960_configure_clocking(struct snd_soc_component *component)
{
struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component);
@ -864,6 +860,56 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
/* set iface */
snd_soc_component_write(component, WM8960_IFACE1, iface);
/* Temp add by walker */
snd_soc_component_write(component, WM8960_POWER1, 0xfe);
snd_soc_component_write(component, WM8960_POWER2, 0x1f8);
snd_soc_component_write(component, WM8960_POWER2, 0x1f9);
snd_soc_component_write(component, WM8960_PLL1, 0x28);
snd_soc_component_write(component, WM8960_PLL1, 0x38);
snd_soc_component_write(component, WM8960_CLOCK1, 0xdd);
snd_soc_component_write(component, WM8960_CLOCK2, 0x1cc);
snd_soc_component_write(component, WM8960_POWER3, 0x3c);
if (tx) {
snd_soc_component_write(component, WM8960_LOUTMIX, 0x100);
snd_soc_component_write(component, WM8960_ROUTMIX, 0x100);
snd_soc_component_write(component, WM8960_POWER3, 0xc);
snd_soc_component_write(component, WM8960_POWER2, 0x1f9);
snd_soc_component_write(component, WM8960_POWER2, 0x1f9);
snd_soc_component_write(component, WM8960_IFACE1, 0x3);
snd_soc_component_write(component, WM8960_IFACE1, 0x43);
snd_soc_component_write(component, WM8960_POWER1, 0xd6);
snd_soc_component_write(component, WM8960_POWER1, 0xc6);
} else {
snd_soc_component_write(component, WM8960_POWER3, 0x30);
snd_soc_component_write(component, WM8960_POWER1, 0xfe);
snd_soc_component_write(component, WM8960_POWER1, 0xfe);
snd_soc_component_write(component, WM8960_POWER3, 0x30);
snd_soc_component_write(component, WM8960_POWER3, 0x30);
snd_soc_component_write(component, WM8960_POWER1, 0xfe);
snd_soc_component_write(component, WM8960_POWER1, 0xfe);
snd_soc_component_write(component, WM8960_ADDCTL2, 0x0);
snd_soc_component_write(component, WM8960_IFACE1, 0x3);
snd_soc_component_write(component, WM8960_IFACE1, 0x43);
snd_soc_component_write(component, WM8960_POWER1, 0xfe);
snd_soc_component_write(component, WM8960_LINPATH, 0x108);
snd_soc_component_write(component, WM8960_POWER1, 0xfe);
snd_soc_component_write(component, WM8960_RINPATH, 0x108);
}
snd_soc_component_write(component, WM8960_ADDCTL1, 0xc0);
snd_soc_component_write(component, WM8960_ADDCTL4, 0x0);
snd_soc_component_write(component, WM8960_BYPASS1, 0x0);
snd_soc_component_write(component, WM8960_BYPASS2, 0x0);
snd_soc_component_write(component, WM8960_CLASSD1, 0xf7);
snd_soc_component_write(component, WM8960_DACCTL1, 0x0);
snd_soc_component_write(component, WM8960_NOISEG, 0xf9);
snd_soc_component_write(component, WM8960_ALC1, 0x1bb);
snd_soc_component_write(component, WM8960_ALC2, 0x30);
/* bclk inverted */
snd_soc_component_update_bits(component, WM8960_IFACE1, 0x80, 0x80);
snd_soc_component_write(component, WM8960_POWER2, 0x1f9);
wm8960->is_stream_in_use[tx] = true;
if (!wm8960->is_stream_in_use[!tx])
@ -1290,6 +1336,7 @@ static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
{
struct snd_soc_component *component = dai->component;
struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component);
clk_id = WM8960_SYSCLK_PLL;
switch (clk_id) {
@ -1351,11 +1398,15 @@ static struct snd_soc_dai_driver wm8960_dai = {
static int wm8960_reg_debug_show(struct seq_file *s, void *data)
{
struct snd_soc_component *component = s->private;
int i, reg;
for (i = 0; i < WM8960_REG_MAX; i++)
{
int i, reg;
for (i = 0; i < WM8960_REG_MAX; i++) {
if ((i == 0xc) || (i == 0xd) || (i == 0xe) || (i == 0xf) ||
(i == 0x1e) || (i == 0x1f) || (i == 0x23) ||
(i == 0x24) || (i == 0x32))
continue;
reg = snd_soc_component_read(component, i);
printk("wm8960 reg:0x%x: 0x%x\n", i, reg);
pr_info("reg:0x%x value:0x%x\n", i, reg);
}
return 0;
}
@ -1364,9 +1415,11 @@ DEFINE_SHOW_ATTRIBUTE(wm8960_reg_debug);
static void wm8960_create_debugfs(struct snd_soc_component *component)
{
struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component);
wm8960->debug_file = debugfs_create_file("wm8960_reg", 0666, NULL,
component, &wm8960_reg_debug_fops);
}
static int wm8960_probe(struct snd_soc_component *component)
{
struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component);
@ -1377,21 +1430,23 @@ static int wm8960_probe(struct snd_soc_component *component)
else
wm8960->set_bias_level = wm8960_set_bias_level_out3;
#if 1
snd_soc_component_update_bits(component, WM8960_LDAC, 0x100, 0x100);
snd_soc_component_update_bits(component, WM8960_RDAC, 0x100, 0x100);
snd_soc_component_update_bits(component, WM8960_LOUT1, 0x100, 0x100);
snd_soc_component_update_bits(component, WM8960_ROUT1, 0x100, 0x100);
snd_soc_component_update_bits(component, WM8960_LOUT1, 0x100, 0x100);
snd_soc_component_update_bits(component, WM8960_ROUT1, 0x100, 0x100);
snd_soc_component_update_bits(component, WM8960_LOUT2, 0x100, 0x100);
snd_soc_component_update_bits(component, WM8960_ROUT2, 0x100, 0x100);
snd_soc_component_update_bits(component, WM8960_POWER2, 0x1fB, 0x198);
snd_soc_component_update_bits(component, WM8960_LOUTMIX, 0x1F0, 0x100);
snd_soc_component_update_bits(component, WM8960_ROUTMIX, 0x1F0, 0x100);
snd_soc_component_update_bits(component, WM8960_LOUT1, 0x7f, 0x6f);
snd_soc_component_update_bits(component, WM8960_ROUT1, 0x7f, 0x6f);
snd_soc_component_update_bits(component, WM8960_LOUT2, 0x7f, 0x7f);
snd_soc_component_update_bits(component, WM8960_ROUT2, 0x7f, 0x7f);
#endif
snd_soc_component_update_bits(component, WM8960_LOUT1, 0x1ff, 0x170);
snd_soc_component_update_bits(component, WM8960_ROUT1, 0x1ff, 0x170);
snd_soc_component_update_bits(component, WM8960_LOUT2, 0x1ff, 0x170);
snd_soc_component_update_bits(component, WM8960_ROUT2, 0x1ff, 0x170);
snd_soc_component_write(component, WM8960_LDAC, 0x1e0);
snd_soc_component_write(component, WM8960_RDAC, 0x1e0);
snd_soc_component_write(component, WM8960_LADC, 0x1e0);
snd_soc_component_write(component, WM8960_RADC, 0x1e0);
snd_soc_add_component_controls(component, wm8960_snd_controls,
ARRAY_SIZE(wm8960_snd_controls));
@ -1497,14 +1552,14 @@ static int wm8960_i2c_probe(struct i2c_client *i2c,
regmap_update_bits(wm8960->regmap, WM8960_ROUT1, 0x100, 0x100);
regmap_update_bits(wm8960->regmap, WM8960_LOUT2, 0x100, 0x100);
regmap_update_bits(wm8960->regmap, WM8960_ROUT2, 0x100, 0x100);
regmap_update_bits(wm8960->regmap, WM8960_LINPATH, 0x138, 0x138);
regmap_update_bits(wm8960->regmap, WM8960_RINPATH, 0x138, 0x138);
regmap_update_bits(wm8960->regmap, WM8960_POWER1, 0x7E, 0x7E);
regmap_update_bits(wm8960->regmap, WM8960_POWER3, 0x30, 0x30);
regmap_update_bits(wm8960->regmap, WM8960_LINVOL, 0x19F, 0x197);
regmap_update_bits(wm8960->regmap, WM8960_RINVOL, 0x19F, 0x197);
regmap_update_bits(wm8960->regmap, WM8960_LINVOL, 0x1ff, 0x128);
regmap_update_bits(wm8960->regmap, WM8960_RINVOL, 0x1ff, 0x128);
/* ADCLRC pin configured as GPIO. */
regmap_update_bits(wm8960->regmap, WM8960_IFACE2, 1 << 6,
wm8960->pdata.gpio_cfg[0] << 6);
@ -1558,4 +1613,4 @@ module_i2c_driver(wm8960_i2c_driver);
MODULE_DESCRIPTION("ASoC WM8960 driver");
MODULE_AUTHOR("Liam Girdwood");
MODULE_LICENSE("GPL");

498
sound/soc/starfive/starfive_tdm.c Normal file → Executable file
View file

@ -18,93 +18,105 @@
#include <sound/tlv.h>
#include "starfive_tdm.h"
#define AUDIOC_CLK (12288000)
#define CLOCK_BASE 0x13020000UL
static inline u32 sf_tdm_readl(struct sf_tdm_dev *tdm, u16 reg)
static inline u32 sf_tdm_readl(struct sf_tdm_dev *dev, u16 reg)
{
return readl_relaxed(tdm->tdm_base + reg);
return readl_relaxed(dev->tdm_base + reg);
}
static inline void sf_tdm_writel(struct sf_tdm_dev *tdm, u16 reg, u32 val)
static inline void sf_tdm_writel(struct sf_tdm_dev *dev, u16 reg, u32 val)
{
writel_relaxed(val, tdm->tdm_base + reg);
writel_relaxed(val, dev->tdm_base + reg);
}
static void sf_tdm_start(struct sf_tdm_dev *tdm, struct snd_pcm_substream *substream)
static void sf_tdm_start(struct sf_tdm_dev *dev, struct snd_pcm_substream *substream)
{
u32 data;
data = sf_tdm_readl(tdm, TDM_PCMGBCR);
sf_tdm_writel(tdm, TDM_PCMGBCR, data | 0x1 | (0x1<<4));
unsigned int val;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
sf_tdm_writel(tdm, TDM_PCMTXCR, sf_tdm_readl(tdm, TDM_PCMTXCR) | 0x1);
else
sf_tdm_writel(tdm, TDM_PCMRXCR, sf_tdm_readl(tdm, TDM_PCMRXCR) | 0x1);
data = sf_tdm_readl(dev, TDM_PCMGBCR);
sf_tdm_writel(dev, TDM_PCMGBCR, data | PCMGBCR_ENABLE);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
val = sf_tdm_readl(dev, TDM_PCMTXCR);
sf_tdm_writel(dev, TDM_PCMTXCR, val | PCMTXCR_TXEN);
} else {
val = sf_tdm_readl(dev, TDM_PCMRXCR);
sf_tdm_writel(dev, TDM_PCMRXCR, val | PCMRXCR_RXEN);
}
}
static void sf_tdm_stop(struct sf_tdm_dev*tdm, struct snd_pcm_substream *substream)
static void sf_tdm_stop(struct sf_tdm_dev *dev, struct snd_pcm_substream *substream)
{
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
sf_tdm_writel(tdm, TDM_PCMTXCR, sf_tdm_readl(tdm, TDM_PCMTXCR) & 0xffe);
else
sf_tdm_writel(tdm, TDM_PCMRXCR, sf_tdm_readl(tdm, TDM_PCMRXCR) & 0xffe);
unsigned int val;
unsigned int bcr;
sf_tdm_writel(tdm, TDM_PCMGBCR, sf_tdm_readl(tdm, TDM_PCMGBCR) & 0x1e);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
val = sf_tdm_readl(dev, TDM_PCMTXCR);
val &= ~PCMTXCR_TXEN;
sf_tdm_writel(dev, TDM_PCMTXCR, val);
} else {
val = sf_tdm_readl(dev, TDM_PCMRXCR);
val &= ~PCMRXCR_RXEN;
sf_tdm_writel(dev, TDM_PCMRXCR, val);
}
bcr = sf_tdm_readl(dev, TDM_PCMGBCR);
sf_tdm_writel(dev, TDM_PCMGBCR, bcr & PCMGBCR_MASK);
}
static int sf_tdm_syncdiv(struct sf_tdm_dev *tdm)
static int sf_tdm_syncdiv(struct sf_tdm_dev *dev)
{
u32 sl, sscale, syncdiv;
sl = (tdm->rx.sl >= tdm->tx.sl) ? tdm->rx.sl:tdm->tx.sl;
sscale = (tdm->rx.sscale >= tdm->tx.sscale) ? tdm->rx.sscale:tdm->tx.sscale;
syncdiv = tdm->pcmclk / tdm->samplerate - 1;
sl = (dev->rx.sl >= dev->tx.sl) ? dev->rx.sl:dev->tx.sl;
sscale = (dev->rx.sscale >= dev->tx.sscale) ? dev->rx.sscale:dev->tx.sscale;
syncdiv = dev->pcmclk / dev->samplerate - 1;
if ((syncdiv + 1) < (sl * sscale)) {
pr_info("set syncdiv failed !\n");
return -1;
}
if ((tdm->syncm == TDM_SYNCM_LONG) && ((tdm->rx.sscale <= 1) || (tdm->tx.sscale <= 1))) {
if ((dev->syncm == TDM_SYNCM_LONG) &&
((dev->rx.sscale <= 1) || (dev->tx.sscale <= 1))) {
if ((syncdiv + 1) <= sl) {
pr_info("set syncdiv failed ! it must be (syncdiv+1) > max[txsl, rxsl]\n");
pr_info("set syncdiv failed! it must be (syncdiv+1) > max[tx.sl, rx.sl]\n");
return -1;
}
}
sf_tdm_writel(tdm, TDM_PCMDIV, syncdiv);
sf_tdm_writel(dev, TDM_PCMDIV, syncdiv);
return 0;
}
static void sf_tdm_contrl(struct sf_tdm_dev *tdm)
static void sf_tdm_contrl(struct sf_tdm_dev *dev)
{
u32 data;
data = (tdm->clkpolity << 5) | (tdm->elm << 3) | (tdm->syncm << 2) | (tdm->mode << 1);
sf_tdm_writel(tdm, TDM_PCMGBCR, data);
data = (dev->clkpolity << 5) | (dev->elm << 3) | (dev->syncm << 2) | (dev->ms_mode << 1);
sf_tdm_writel(dev, TDM_PCMGBCR, data);
}
static void sf_tdm_config(struct sf_tdm_dev *tdm, struct snd_pcm_substream *substream)
static void sf_tdm_config(struct sf_tdm_dev *dev, struct snd_pcm_substream *substream)
{
u32 datarx, datatx;
sf_tdm_stop(tdm, substream);
sf_tdm_contrl(tdm);
sf_tdm_syncdiv(tdm);
datarx = (tdm->rx.ifl << 11) | (tdm->rx.wl << 8) | (tdm->rx.sscale << 4) |
(tdm->rx.sl << 2) | (tdm->rx.lrj << 1);
sf_tdm_stop(dev, substream);
sf_tdm_contrl(dev);
sf_tdm_syncdiv(dev);
datatx = (tdm->tx.ifl << 11) | (tdm->tx.wl << 8) | (tdm->tx.sscale << 4) |
(tdm->tx.sl << 2) | (tdm->tx.lrj << 1);
datarx = (dev->rx.ifl << 11) | (dev->rx.wl << 8) | (dev->rx.sscale << 4) |
(dev->rx.sl << 2) | (dev->rx.lrj << 1);
datatx = (dev->tx.ifl << 11) | (dev->tx.wl << 8) | (dev->tx.sscale << 4) |
(dev->tx.sl << 2) | (dev->tx.lrj << 1);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
sf_tdm_writel(tdm, TDM_PCMTXCR, datatx);
sf_tdm_writel(dev, TDM_PCMTXCR, datatx);
else
sf_tdm_writel(tdm, TDM_PCMRXCR, datarx);
sf_tdm_start(tdm, substream);
sf_tdm_writel(dev, TDM_PCMRXCR, datarx);
}
@ -117,99 +129,121 @@ static const struct snd_soc_component_driver sf_tdm_component = {
.resume = sf_tdm_resume,
};
static int sf_tdm_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
struct sf_tdm_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
struct snd_dmaengine_dai_dma_data *dma_data = NULL;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
dma_data = &dev->play_dma_data;
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
dma_data = &dev->capture_dma_data;
snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)dma_data);
return 0;
}
static void sf_tdm_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
snd_soc_dai_set_dma_data(dai, substream, NULL);
}
static int sf_tdm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
struct sf_tdm_dev *dev = snd_soc_dai_get_drvdata(dai);
int chan_wl, chan_sl, chan_nr;
struct snd_dmaengine_dai_dma_data *dma_data = NULL;
unsigned int data_width;
unsigned int mclk_rate;
int ret;
dev->samplerate = params_rate(params);
switch (dev->samplerate) {
case 8000:
mclk_rate = 12288000;
dev->pcmclk = 256000;
break;
case 16000:
mclk_rate = 12288000;
dev->pcmclk = 512000;
break;
default:
pr_err("TDM: not support sample rate:%d\n", dev->samplerate);
return -EINVAL;
}
data_width = params_width(params);
dev->pcmclk = 2 * dev->samplerate * data_width;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
chan_wl = TDM_8BIT_WORD_LEN;
chan_sl = TDM_8BIT_SLOT_LEN;
break;
case SNDRV_PCM_FORMAT_S16_LE:
chan_wl = TDM_16BIT_WORD_LEN;
chan_sl = TDM_16BIT_SLOT_LEN;
break;
case SNDRV_PCM_FORMAT_S8:
chan_wl = TDM_8BIT_WORD_LEN;
chan_sl = TDM_8BIT_SLOT_LEN;
break;
case SNDRV_PCM_FORMAT_S24_LE:
chan_wl = TDM_24BIT_WORD_LEN;
chan_sl = TDM_32BIT_SLOT_LEN;
break;
case SNDRV_PCM_FORMAT_S16_LE:
chan_wl = TDM_16BIT_WORD_LEN;
chan_sl = TDM_16BIT_SLOT_LEN;
break;
case SNDRV_PCM_FORMAT_S32_LE:
chan_wl = TDM_32BIT_WORD_LEN;
chan_sl = TDM_32BIT_SLOT_LEN;
break;
case SNDRV_PCM_FORMAT_S24_LE:
chan_wl = TDM_24BIT_WORD_LEN;
chan_sl = TDM_32BIT_SLOT_LEN;
break;
default:
dev_err(dev->dev, "tdm: unsupported PCM fmt");
return -EINVAL;
case SNDRV_PCM_FORMAT_S32_LE:
chan_wl = TDM_32BIT_WORD_LEN;
chan_sl = TDM_32BIT_SLOT_LEN;
break;
default:
dev_err(dev->dev, "tdm: unsupported PCM fmt");
return -EINVAL;
}
chan_nr = params_channels(params);
switch (chan_nr) {
case TWO_CHANNEL_SUPPORT:
case FOUR_CHANNEL_SUPPORT:
case SIX_CHANNEL_SUPPORT:
case EIGHT_CHANNEL_SUPPORT:
break;
default:
dev_err(dev->dev, "channel not supported\n");
return -EINVAL;
case TWO_CHANNEL_SUPPORT:
case FOUR_CHANNEL_SUPPORT:
case SIX_CHANNEL_SUPPORT:
case EIGHT_CHANNEL_SUPPORT:
break;
default:
dev_err(dev->dev, "channel not supported\n");
return -EINVAL;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
dev->tx.wl = chan_wl;
dev->tx.sl = chan_sl;
dev->tx.sscale = chan_nr;
dma_data = &dev->play_dma_data;
} else {
dev->rx.wl = chan_wl;
dev->rx.sl = chan_sl;
dev->rx.sscale = chan_nr;
dma_data = &dev->capture_dma_data;
}
dev->samplerate = params_rate(params);
if (!dev->mode) {
sf_tdm_syncdiv(dev);
snd_soc_dai_set_dma_data(dai, substream, dma_data);
ret = clk_set_rate(dev->clk_mclk_inner, mclk_rate);
if (ret) {
dev_info(dev->dev, "Can't set clk_mclk: %d\n", ret);
return ret;
}
ret = clk_set_rate(dev->clk_tdm_internal, dev->pcmclk);
if (ret) {
dev_info(dev->dev, "Can't set clk_tdm_internal: %d\n", ret);
return ret;
}
ret = clk_set_parent(dev->clk_tdm, dev->clk_tdm_ext);
if (ret) {
dev_info(dev->dev, "Can't set clock source for clk_tdm: %d\n", ret);
return ret;
}
ret = clk_prepare_enable(dev->clk_tdm_ahb);
if (ret) {
dev_err(dev->dev, "Failed to prepare enable clk_tdm_ahb\n");
return ret;
}
ret = clk_prepare_enable(dev->clk_tdm_apb);
if (ret) {
dev_err(dev->dev, "Failed to prepare enable clk_tdm_apb\n");
return ret;
}
sf_tdm_config(dev, substream);
return 0;
}
static int sf_tdm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
return 0;
}
static int sf_tdm_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
@ -244,10 +278,10 @@ static int sf_tdm_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
dev->mode = TDM_AS_SLAVE;
dev->ms_mode = TDM_AS_SLAVE;
break;
case SND_SOC_DAIFMT_CBS_CFS:
dev->mode = TDM_AS_MASTER;
dev->ms_mode = TDM_AS_MASTER;
break;
case SND_SOC_DAIFMT_CBM_CFS:
case SND_SOC_DAIFMT_CBS_CFM:
@ -262,119 +296,193 @@ static int sf_tdm_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
}
static const struct snd_soc_dai_ops sf_tdm_dai_ops = {
.startup = sf_tdm_startup,
.shutdown = sf_tdm_shutdown,
.hw_params = sf_tdm_hw_params,
.prepare = sf_tdm_prepare,
.trigger = sf_tdm_trigger,
.set_fmt = sf_tdm_set_fmt,
};
static int tdm_configure_dai(struct sf_tdm_dev *dev,
struct snd_soc_dai_driver *sf_tdm_dai,
unsigned int rates)
{
sf_tdm_dai->playback.channels_min = TDM_MIN_CHANNEL_NUM;
sf_tdm_dai->playback.channels_max = TDM_MAX_CHANNEL_NUM;
sf_tdm_dai->playback.formats = SNDRV_PCM_FMTBIT_S8|SNDRV_PCM_FMTBIT_S16_LE|SNDRV_PCM_FMTBIT_S24_LE|SNDRV_PCM_FMTBIT_S32_LE;
sf_tdm_dai->playback.rates = rates;
sf_tdm_dai->capture.channels_min = TDM_MIN_CHANNEL_NUM;
sf_tdm_dai->capture.channels_max = TDM_MAX_CHANNEL_NUM;
sf_tdm_dai->capture.formats = SNDRV_PCM_FMTBIT_S8|SNDRV_PCM_FMTBIT_S16_LE|SNDRV_PCM_FMTBIT_S24_LE|SNDRV_PCM_FMTBIT_S32_LE;
sf_tdm_dai->capture.rates = rates;
return 0;
}
static int sf_tdm_dai_probe(struct snd_soc_dai *dai)
{
struct sf_tdm_dev *dev = snd_soc_dai_get_drvdata(dai);
snd_soc_dai_init_dma_data(dai, &dev->play_dma_data, &dev->capture_dma_data);
snd_soc_dai_set_drvdata(dai, dev);
return 0;
}
static int sf_tdm_clock_init(struct platform_device *pdev, struct sf_tdm_dev *dev)
#define SF_TDM_RATE (SNDRV_PCM_RATE_8000 | \
SNDRV_PCM_RATE_16000 | \
SNDRV_PCM_RATE_32000)
#define SF_TDM_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_driver sf_tdm_dai = {
.name = "sf_tdm",
.id = 0,
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 8,
.rates = SF_TDM_RATE,
.formats = SF_TDM_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 8,
.rates = SF_TDM_RATE,
.formats = SF_TDM_FORMATS,
},
.ops = &sf_tdm_dai_ops,
.probe = sf_tdm_dai_probe,
.symmetric_rate = 1,
};
static void tdm_init_params(struct sf_tdm_dev *dev)
{
dev->clkpolity = TDM_TX_RASING_RX_FALLING;
if (dev->frame_mode == SHORT_LATER) {
dev->elm = TDM_ELM_LATE;
dev->syncm = TDM_SYNCM_SHORT;
} else if (dev->frame_mode == SHORT_EARLY) {
dev->elm = TDM_ELM_EARLY;
dev->syncm = TDM_SYNCM_SHORT;
} else {
dev->elm = TDM_ELM_EARLY;
dev->syncm = TDM_SYNCM_LONG;
}
dev->ms_mode = TDM_AS_SLAVE;
dev->rx.ifl = dev->tx.ifl = TDM_FIFO_HALF;
dev->rx.wl = dev->tx.wl = TDM_16BIT_WORD_LEN;
dev->rx.sscale = dev->tx.sscale = 2;
dev->rx.lrj = dev->tx.lrj = TDM_LEFT_JUSTIFT;
dev->samplerate = 16000;
dev->pcmclk = 512000;
dev->play_dma_data.addr = TDM_FIFO;
dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
dev->play_dma_data.fifo_size = TDM_FIFO_DEPTH/2;
dev->play_dma_data.maxburst = 16;
dev->capture_dma_data.addr = TDM_FIFO;
dev->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
dev->capture_dma_data.fifo_size = TDM_FIFO_DEPTH/2;
dev->capture_dma_data.maxburst = 8;
}
static int sf_tdm_clk_reset_init(struct platform_device *pdev, struct sf_tdm_dev *dev)
{
int ret;
static struct clk_bulk_data clks[] = {
{ .id = "clk_ahb0" },
{ .id = "clk_tdm_ahb" },
{ .id = "clk_apb0" },
{ .id = "clk_tdm_apb" },
{ .id = "clk_tdm_internal" },
{ .id = "clk_tdm_ext" },
{ .id = "clk_tdm" },
{ .id = "mclk_inner" },
};
ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(clks), clks);
if (ret) {
dev_err(&pdev->dev, "failed to get tdm clocks\n");
goto exit;
}
dev->clk_ahb0 = clks[0].clk;
dev->clk_tdm_ahb = clks[1].clk;
dev->clk_apb0 = clks[2].clk;
dev->clk_tdm_apb = clks[3].clk;
dev->clk_tdm_internal = clks[4].clk;
dev->clk_tdm_ext = clks[5].clk;
dev->clk_tdm = clks[6].clk;
dev->clk_mclk_inner = clks[7].clk;
dev->rst_ahb = devm_reset_control_get_exclusive(&pdev->dev, "tdm_ahb");
if (IS_ERR(dev->rst_ahb)) {
dev_err(&pdev->dev, "Failed to get tdm_ahb reset control\n");
return PTR_ERR(dev->rst_ahb);
ret = PTR_ERR(dev->rst_ahb);
goto exit;
}
dev->rst_apb = devm_reset_control_get_exclusive(&pdev->dev, "tdm_apb");
if (IS_ERR(dev->rst_apb)) {
dev_err(&pdev->dev, "Failed to get tdm_apb reset control\n");
return PTR_ERR(dev->rst_apb);
ret = PTR_ERR(dev->rst_apb);
goto exit;
}
dev->rst_tdm = devm_reset_control_get_exclusive(&pdev->dev, "tdm_rst");
if (IS_ERR(dev->rst_tdm)) {
dev_err(&pdev->dev, "Failed to get tdm_rst reset control\n");
return PTR_ERR(dev->rst_tdm);
ret = PTR_ERR(dev->rst_tdm);
goto exit;
}
dev->clk_ahb0 = devm_clk_get(&pdev->dev, "clk_ahb0");
if (IS_ERR(dev->clk_ahb0)) {
dev_err(&pdev->dev, "Failed to get clk_ahb0");
return PTR_ERR(dev->clk_ahb0);
ret = reset_control_assert(dev->rst_ahb);
if (ret) {
dev_err(&pdev->dev, "failed to assert rst_ahb\n");
goto exit;
}
dev->clk_tdm_ahb = devm_clk_get(&pdev->dev, "clk_tdm_ahb");
if (IS_ERR(dev->clk_tdm_ahb)) {
dev_err(&pdev->dev, "Failed to get clk_tdm_ahb");
return PTR_ERR(dev->clk_tdm_ahb);
ret = reset_control_assert(dev->rst_apb);
if (ret) {
dev_err(&pdev->dev, "failed to assert rst_apb\n");
goto exit;
}
dev->clk_apb0 = devm_clk_get(&pdev->dev, "clk_apb0");
if (IS_ERR(dev->clk_apb0)) {
dev_err(&pdev->dev, "Failed to get clk_apb0");
return PTR_ERR(dev->clk_apb0);
ret = reset_control_assert(dev->rst_tdm);
if (ret) {
dev_err(&pdev->dev, "failed to assert rst_tdm\n");
goto exit;
}
dev->clk_tdm_apb = devm_clk_get(&pdev->dev, "clk_tdm_apb");
if (IS_ERR(dev->clk_tdm_apb)) {
dev_err(&pdev->dev, "Failed to get clk_tdm_apb");
return PTR_ERR(dev->clk_tdm_ahb);
}
dev->clk_tdm_intl = devm_clk_get(&pdev->dev, "clk_tdm_intl");
if (IS_ERR(dev->clk_tdm_intl)) {
dev_err(&pdev->dev, "Failed to get clk_tdm_intl");
return PTR_ERR(dev->clk_tdm_intl);
ret = clk_prepare_enable(dev->clk_mclk_inner);
if (ret) {
dev_err(&pdev->dev, "failed to prepare enable clk_mclk_inner\n");
goto exit;
}
ret = clk_prepare_enable(dev->clk_ahb0);
if (ret) {
dev_err(&pdev->dev, "Failed to prepare enable clk_ahb0\n");
goto err_clk_disable;
goto err_dis_ahb0;
}
ret = clk_prepare_enable(dev->clk_tdm_ahb);
if (ret) {
dev_err(&pdev->dev, "Failed to prepare enable clk_tdm_ahb\n");
goto err_clk_disable;
goto err_dis_tdm_ahb;
}
ret = clk_prepare_enable(dev->clk_apb0);
if (ret) {
dev_err(&pdev->dev, "Failed to prepare enable clk_apb0\n");
goto err_clk_disable;
goto err_dis_apb0;
}
ret = clk_prepare_enable(dev->clk_tdm_apb);
if (ret) {
dev_err(&pdev->dev, "Failed to prepare enable clk_tdm_apb\n");
goto err_clk_disable;
goto err_dis_tdm_apb;
}
ret = clk_prepare_enable(dev->clk_tdm_intl);
ret = clk_prepare_enable(dev->clk_tdm_internal);
if (ret) {
dev_err(&pdev->dev, "Failed to prepare enable clk_tdm_intl\n");
goto err_clk_disable;
goto err_dis_tdm_internal;
}
ret = clk_prepare_enable(dev->clk_tdm_ext);
if (ret) {
dev_err(&pdev->dev, "failed to prepare enable clk_tdm_ext\n");
goto err_dis_tdm_ext;
}
ret = reset_control_deassert(dev->rst_ahb);
@ -398,6 +506,20 @@ static int sf_tdm_clock_init(struct platform_device *pdev, struct sf_tdm_dev *de
return 0;
err_clk_disable:
clk_disable_unprepare(dev->clk_tdm_ext);
err_dis_tdm_ext:
clk_disable_unprepare(dev->clk_tdm_internal);
err_dis_tdm_internal:
clk_disable_unprepare(dev->clk_tdm_apb);
err_dis_tdm_apb:
clk_disable_unprepare(dev->clk_apb0);
err_dis_apb0:
clk_disable_unprepare(dev->clk_tdm_ahb);
err_dis_tdm_ahb:
clk_disable_unprepare(dev->clk_ahb0);
err_dis_ahb0:
clk_disable_unprepare(dev->clk_mclk_inner);
exit:
return ret;
}
@ -406,19 +528,11 @@ static int sf_tdm_probe(struct platform_device *pdev)
struct sf_tdm_dev *dev;
struct resource *res;
int ret;
struct snd_soc_dai_driver *sf_tdm_dai;
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
sf_tdm_dai = devm_kzalloc(&pdev->dev, sizeof(*sf_tdm_dai), GFP_KERNEL);
if (!sf_tdm_dai)
return -ENOMEM;
sf_tdm_dai->ops = &sf_tdm_dai_ops;
sf_tdm_dai->probe = sf_tdm_dai_probe;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dev->tdm_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(dev->tdm_base))
@ -426,72 +540,38 @@ static int sf_tdm_probe(struct platform_device *pdev)
dev->dev = &pdev->dev;
ret = tdm_configure_dai(dev, sf_tdm_dai, SNDRV_PCM_RATE_8000_192000);
if (ret < 0)
return ret;
dev->clkpolity = TDM_TX_RASING_RX_FALLING;
dev->tritxen = 1;
dev->elm = TDM_ELM_LATE;
dev->syncm = TDM_SYNCM_SHORT;
dev->mode = TDM_AS_MASTER;
dev->rx.ifl = TDM_FIFO_HALF;
dev->tx.ifl = TDM_FIFO_HALF;
dev->rx.sscale = 1;
dev->tx.sscale = 1;
dev->rx.lrj = TDM_LEFT_JUSTIFT;
dev->tx.lrj = TDM_LEFT_JUSTIFT;
dev->samplerate = 16000;
dev->pcmclk = 4096000;
dev->play_dma_data.addr = res->start + TDM_TXDMA;
dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
dev->play_dma_data.fifo_size = TDM_FIFO_DEPTH/2;
dev->play_dma_data.maxburst = 16;
dev->capture_dma_data.addr = res->start + TDM_RXDMA;
dev->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
dev->capture_dma_data.fifo_size = TDM_FIFO_DEPTH/2;
dev->capture_dma_data.maxburst = 16;
ret = sf_tdm_clock_init(pdev, dev);
ret = sf_tdm_clk_reset_init(pdev, dev);
if (ret) {
dev_err(&pdev->dev, "failed to enable audio-tdm clock\n");
return ret;
}
ret = sf_tdm_clock_init(pdev, dev);
if (ret) {
dev_err(&pdev->dev, "failed to enable audio-tdm clock\n");
return ret;
}
dev->frame_mode = SHORT_LATER;
tdm_init_params(dev);
dev_set_drvdata(&pdev->dev, dev);
ret = devm_snd_soc_register_component(&pdev->dev, &sf_tdm_component,
sf_tdm_dai, 1);
&sf_tdm_dai, 1);
if (ret != 0) {
dev_err(&pdev->dev, "not able to register dai\n");
dev_err(&pdev->dev, "failed to register dai\n");
return ret;
}
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
if (ret) {
dev_err(&pdev->dev, "could not register pcm: %d\n",
ret);
dev_err(&pdev->dev, "could not register pcm: %d\n", ret);
return ret;
}
return 0;
}
static int sf_tdm_dev_remove(struct platform_device *pdev)
{
return 0;
}
static const struct of_device_id sf_tdm_of_match[] = {
{.compatible = "starfive,sf-tdm",},
{.compatible = "starfive,sf-tdm",},
{}
};
MODULE_DEVICE_TABLE(of, sf_tdm_of_match);

40
sound/soc/starfive/starfive_tdm.h Normal file → Executable file
View file

@ -16,25 +16,35 @@
#include <linux/types.h>
#define TDM_PCMGBCR 0x00
#define PCMGBCR_MASK 0x1e
#define PCMGBCR_ENABLE BIT(0)
#define PCMGBCR_TRITXEN BIT(4)
#define CLKPOL_BIT 5
#define TRITXEN_BIT 4
#define ELM_BIT 3
#define SYNCM_BIT 2
#define MS_BIT 1
#define TDM_PCMTXCR 0x04
#define PCMTXCR_TXEN BIT(0)
#define TDM_PCMRXCR 0x08
#define PCMRXCR_RXEN BIT(0)
#define PCMRXCR_RXSL_MASK 0xc
#define PCMRXCR_RXSL_16BIT 0x4
#define PCMRXCR_RXSL_32BIT 0x8
#define PCMRXCR_SCALE_MASK 0xf0
#define PCMRXCR_SCALE_1CH 0x10
#define TDM_PCMDIV 0x0c
/* DMA registers */
#define TDM_RXDMA 0xc0
#define TDM_TXDMA 0xd0
#define TDM_FIFO 0x170c0000
#define TDM_FIFO_DEPTH 16
#define TDM_MAX_CHANNEL_NUM 8
#define TDM_MIN_CHANNEL_NUM 2
#define TWO_CHANNEL_SUPPORT 2
#define FOUR_CHANNEL_SUPPORT 4
#define SIX_CHANNEL_SUPPORT 6
#define EIGHT_CHANNEL_SUPPORT 8
enum TDM_MODE {
enum TDM_MASTER_SLAVE_MODE {
TDM_AS_MASTER = 0,
TDM_AS_SLAVE,
};
@ -42,10 +52,16 @@ enum TDM_MODE {
enum TDM_CLKPOL {
/* tx raising and rx falling */
TDM_TX_RASING_RX_FALLING = 0,
/* tx raising and rx falling */
/* tx falling and rx raising */
TDM_TX_FALLING_RX_RASING,
};
enum TDM_FRAME_MODE {
SHORT_EARLY = 0,
SHORT_LATER,
LONG,
};
enum TDM_ELM {
/* only work while SYNCM=0 */
TDM_ELM_LATE = 0,
@ -103,7 +119,10 @@ struct sf_tdm_dev {
struct clk *clk_tdm_ahb;
struct clk *clk_apb0;
struct clk *clk_tdm_apb;
struct clk *clk_tdm_intl;
struct clk *clk_tdm_internal;
struct clk *clk_tdm_ext;
struct clk *clk_tdm;
struct clk *clk_mclk_inner;
struct reset_control *rst_ahb;
struct reset_control *rst_apb;
struct reset_control *rst_tdm;
@ -112,7 +131,8 @@ struct sf_tdm_dev {
enum TDM_CLKPOL clkpolity;
enum TDM_ELM elm;
enum TDM_SYNCM syncm;
enum TDM_MODE mode;
enum TDM_MASTER_SLAVE_MODE ms_mode;
enum TDM_FRAME_MODE frame_mode;
unsigned char tritxen;
tdm_chan_cfg_t tx;