mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-03-16 04:04:06 +00:00
Implement TDM driver for JH7110 SoC
Signed-off-by: Walker Chen <walker.chen@starfivetech.com>
This commit is contained in:
parent
70a6a66c7e
commit
edd12a5579
13 changed files with 590 additions and 324 deletions
17
arch/riscv/boot/dts/starfive/codecs/sf_pdm.dtsi
Normal file
17
arch/riscv/boot/dts/starfive/codecs/sf_pdm.dtsi
Normal 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>;
|
||||
};
|
||||
};
|
||||
};
|
34
arch/riscv/boot/dts/starfive/codecs/sf_tdm.dtsi
Normal file
34
arch/riscv/boot/dts/starfive/codecs/sf_tdm.dtsi
Normal 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";
|
||||
};
|
||||
};
|
||||
};
|
|
@ -438,7 +438,7 @@
|
|||
&tdm {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&tdm0_pins>;
|
||||
status = "disabled";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&spdif0 {
|
||||
|
|
|
@ -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";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -117,4 +117,10 @@
|
|||
#clock-cells = <0>;
|
||||
clock-frequency = <297000000>;
|
||||
};
|
||||
|
||||
wm8960_mclk: wm8960_mclk {
|
||||
compatible = "fixed-clock";
|
||||
#clock-cells = <0>;
|
||||
clock-frequency = <24576000>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
498
sound/soc/starfive/starfive_tdm.c
Normal file → Executable 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
40
sound/soc/starfive/starfive_tdm.h
Normal file → Executable 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;
|
||||
|
|
Loading…
Add table
Reference in a new issue