1.[alsa]Resolve the failure of pwmdac to play mono 8K audio

2.[dma] Modify the DMA error interrupt handling process

Signed-off-by: jenny.zhang <jenny.zhang@starfivetech.com>
This commit is contained in:
jenny.zhang 2022-01-27 22:30:49 -08:00
parent 7d3709aaf0
commit f51ce46905
4 changed files with 95 additions and 199 deletions

210
drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c Normal file → Executable file
View file

@ -309,13 +309,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;
}
@ -329,64 +328,29 @@ static void write_chan_llp(struct axi_dma_chan *chan, dma_addr_t adr)
axi_chan_iowrite64(chan, CH_LLP, adr);
}
static void dw_axi_dma_set_byte_halfword(struct axi_dma_chan *chan, bool set)
{
u32 offset = DMAC_APB_BYTE_WR_CH_EN;
u32 reg_width, val;
if (!chan->chip->apb_regs) {
dev_dbg(chan->chip->dev, "apb_regs not initialized\n");
return;
}
reg_width = __ffs(chan->config.dst_addr_width);
if (reg_width == DWAXIDMAC_TRANS_WIDTH_16)
offset = DMAC_APB_HALFWORD_WR_CH_EN;
val = ioread32(chan->chip->apb_regs + offset);
if (set)
val |= BIT(chan->id);
else
val &= ~BIT(chan->id);
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)
{
struct axi_dma_hw_desc *hw_desc = NULL;
u32 priority = chan->chip->dw->hdata->priority[chan->id];
u32 reg, irq_mask;
s32 descs_flush, descs_count;
u8 lms = 0; /* Select AXI0 master for LLI fetching */
#ifdef CONFIG_DW_AXI_DMAC_STARFIVE
s32 descs_flush, descs_count;
struct axi_dma_hw_desc *hw_desc = NULL;
chan->is_err = false;
if (unlikely(axi_chan_is_hw_enable(chan))) {
dev_err(chan2dev(chan), "%s is non-idle!\n",
axi_chan_name(chan));
axi_chan_disable(chan);
chan->is_err = true;
//return;
}
#else
if (unlikely(axi_chan_is_hw_enable(chan))) {
dev_err(chan2dev(chan), "%s is non-idle!\n",
axi_chan_name(chan));
return;
}
#endif
axi_dma_enable(chan->chip);
reg = (DWAXIDMAC_MBLK_TYPE_LL << CH_CFG_L_DST_MULTBLK_TYPE_POS |
DWAXIDMAC_MBLK_TYPE_LL << CH_CFG_L_SRC_MULTBLK_TYPE_POS);
#ifdef CONFIG_DW_AXI_DMAC_STARFIVE
if (chan->hw_handshake_num) {
switch (chan->direction) {
case DMA_MEM_TO_DEV:
@ -399,7 +363,6 @@ static void axi_chan_block_xfer_start(struct axi_dma_chan *chan,
break;
}
}
#endif
axi_chan_iowrite32(chan, CH_CFG_L, reg);
@ -409,7 +372,6 @@ static void axi_chan_block_xfer_start(struct axi_dma_chan *chan,
DWAXIDMAC_HS_SEL_HW << CH_CFG_H_HS_SEL_SRC_POS);
switch (chan->direction) {
case DMA_MEM_TO_DEV:
dw_axi_dma_set_byte_halfword(chan, true);
reg |= (chan->config.device_fc ?
DWAXIDMAC_TT_FC_MEM_TO_PER_DST :
DWAXIDMAC_TT_FC_MEM_TO_PER_DMAC)
@ -435,23 +397,13 @@ static void axi_chan_block_xfer_start(struct axi_dma_chan *chan,
irq_mask |= DWAXIDMAC_IRQ_SUSPENDED;
axi_chan_irq_set(chan, irq_mask);
/* flush all the desc */
#ifdef CONFIG_DW_AXI_DMAC_STARFIVE
/*flush all the desc */
#ifdef CONFIG_SOC_STARFIVE_VIC7100
if(chan->chip->flag->need_flush) {
int count = atomic_read(&chan->descs_allocated);
int i;
for (i = 0; i < count; i++) {
starfive_flush_dcache(first->hw_desc[i].llp,
sizeof(*first->hw_desc[i].lli));
dev_dbg(chan->chip->dev,
"sar:%#llx dar:%#llx llp:%#llx ctl:0x%x:%08x\n",
first->hw_desc[i].lli->sar,
first->hw_desc[i].lli->dar,
first->hw_desc[i].lli->llp,
first->hw_desc[i].lli->ctl_hi,
first->hw_desc[i].lli->ctl_lo);
descs_count = atomic_read(&chan->descs_allocated);
for (descs_flush = 0; descs_flush < descs_count; descs_flush++) {
hw_desc = &first->hw_desc[descs_flush];
starfive_flush_dcache(hw_desc->llp, sizeof(*hw_desc->lli));
}
}
#endif
@ -542,48 +494,6 @@ static void dma_chan_free_chan_resources(struct dma_chan *dchan)
pm_runtime_put(chan->chip->dev);
}
static void dw_axi_dma_set_hw_channel(struct axi_dma_chip *chip,
u32 handshake_num, bool set)
{
unsigned long start = 0;
unsigned long reg_value;
unsigned long reg_mask;
unsigned long reg_set;
unsigned long mask;
unsigned long val;
if (!chip->apb_regs) {
dev_dbg(chip->dev, "apb_regs not initialized\n");
return;
}
/*
* An unused DMA channel has a default value of 0x3F.
* Lock the DMA channel by assign a handshake number to the channel.
* Unlock the DMA channel by assign 0x3F to the channel.
*/
if (set) {
reg_set = UNUSED_CHANNEL;
val = handshake_num;
} else {
reg_set = handshake_num;
val = UNUSED_CHANNEL;
}
reg_value = lo_hi_readq(chip->apb_regs + DMAC_APB_HW_HS_SEL_0);
for_each_set_clump8(start, reg_mask, &reg_value, 64) {
if (reg_mask == reg_set) {
mask = GENMASK_ULL(start + 7, start);
reg_value &= ~mask;
reg_value |= rol64(val, start);
lo_hi_writeq(reg_value,
chip->apb_regs + DMAC_APB_HW_HS_SEL_0);
break;
}
}
}
/*
* If DW_axi_dmac sees CHx_CTL.ShadowReg_Or_LLI_Last bit of the fetched LLI
* as 1, it understands that the current block is the final block in the
@ -709,7 +619,7 @@ static int dw_axi_dma_set_hw_desc(struct axi_dma_chan *chan,
hw_desc->lli->block_ts_lo = cpu_to_le32(block_ts - 1);
#ifdef CONFIG_DW_AXI_DMAC_STARFIVE
#ifdef CONFIG_SOC_STARFIVE_VIC7100
ctllo |= DWAXIDMAC_BURST_TRANS_LEN_16 << CH_CTL_L_DST_MSIZE_POS |
DWAXIDMAC_BURST_TRANS_LEN_16 << CH_CTL_L_SRC_MSIZE_POS;
#else
@ -819,8 +729,6 @@ dw_axi_dma_chan_prep_cyclic(struct dma_chan *dchan, dma_addr_t dma_addr,
llp = hw_desc->llp;
} while (total_segments);
dw_axi_dma_set_hw_channel(chan->chip, chan->hw_handshake_num, true);
return vchan_tx_prep(&chan->vc, &desc->vd, flags);
err_desc_get:
@ -899,8 +807,6 @@ dw_axi_dma_chan_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
llp = hw_desc->llp;
} while (num_sgs);
dw_axi_dma_set_hw_channel(chan->chip, chan->hw_handshake_num, true);
return vchan_tx_prep(&chan->vc, &desc->vd, flags);
err_desc_get:
@ -996,6 +902,10 @@ dma_chan_prep_dma_memcpy(struct dma_chan *dchan, dma_addr_t dst_adr,
num++;
}
/* Total len of src/dest sg == 0, so no descriptor were allocated */
if (unlikely(!desc))
return NULL;
/* Set end-of-link to the last link descriptor of list */
set_desc_last(&desc->hw_desc[num - 1]);
/* Managed transfer list */
@ -1046,16 +956,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)
{
struct virt_dma_desc *vd;
unsigned long flags;
#ifdef CONFIG_DW_AXI_DMAC_STARFIVE
struct axi_dma_desc *desc;
#endif
spin_lock_irqsave(&chan->vc.lock, flags);
axi_chan_disable(chan);
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;
u32 val;
int ret;
ret = readl_poll_timeout_atomic(chan->chip->regs + DMAC_CHEN, val,
!(val & chan_active), 10, 2000);
if (ret == -ETIMEDOUT)
dev_warn(chan2dev(chan),
"irq %s failed to stop\n", axi_chan_name(chan));
spin_lock_irqsave(&chan->vc.lock, flags);
/* The bad descriptor currently is in the head of vc list */
vd = vchan_next_desc(&chan->vc);
@ -1065,33 +982,43 @@ static noinline void axi_chan_handle_err(struct axi_dma_chan *chan, u32 status)
spin_unlock_irqrestore(&chan->vc.lock, flags);
return;
}
#ifdef CONFIG_DW_AXI_DMAC_STARFIVE
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 {
#endif
/* 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);
/* Try to restart the controller */
axi_chan_start_first_queued(chan);
#ifdef CONFIG_DW_AXI_DMAC_STARFIVE
}
#endif
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);
}
spin_unlock_irqrestore(&chan->vc.lock, flags);
tasklet_schedule(&chan->dma_tasklet);
}
static void axi_chan_block_xfer_complete(struct axi_dma_chan *chan)
{
int count = atomic_read(&chan->descs_allocated);
@ -1127,6 +1054,9 @@ static void axi_chan_block_xfer_complete(struct axi_dma_chan *chan)
if (hw_desc->llp == llp) {
axi_chan_irq_clear(chan, hw_desc->lli->status_lo);
hw_desc->lli->ctl_hi |= CH_CTL_H_LLI_VALID;
#ifdef CONFIG_SOC_STARFIVE_VIC7100
starfive_flush_dcache(hw_desc->llp, sizeof(*hw_desc->lli));
#endif
desc->completed_blocks = i;
if (((hw_desc->len * (i + 1)) % desc->period_len) == 0)
@ -1169,11 +1099,11 @@ static irqreturn_t dw_axi_dma_interrupt(int irq, void *dev_id)
dev_vdbg(chip->dev, "%s %u IRQ status: 0x%08x\n",
axi_chan_name(chan), i, status);
if (status & DWAXIDMAC_IRQ_ALL_ERR)
if (status & DWAXIDMAC_IRQ_ALL_ERR) {
axi_chan_handle_err(chan, status);
}
else if (status & DWAXIDMAC_IRQ_DMA_TRF) {
axi_chan_block_xfer_complete(chan);
dev_dbg(chip->dev, "axi_chan_block_xfer_complete.\n");
}
}
@ -1200,13 +1130,6 @@ static int dma_chan_terminate_all(struct dma_chan *dchan)
dev_warn(dchan2dev(dchan),
"%s failed to stop\n", axi_chan_name(chan));
if (chan->direction != DMA_MEM_TO_MEM)
dw_axi_dma_set_hw_channel(chan->chip,
chan->hw_handshake_num, false);
if (chan->direction == DMA_MEM_TO_DEV)
dw_axi_dma_set_byte_halfword(chan, false);
spin_lock_irqsave(&chan->vc.lock, flags);
vchan_get_all_descriptors(&chan->vc, &head);
@ -1367,7 +1290,7 @@ static int parse_device_properties(struct axi_dma_chip *chip)
if(chip->dw->hdata->nr_channels > 8){
chip->flag->nr_chan_8 = true;
#ifdef CONFIG_DW_AXI_DMAC_STARFIVE
#ifdef CONFIG_SOC_STARFIVE_VIC7100
chip->flag->need_flush = true;
#endif
}
@ -1428,7 +1351,6 @@ static int parse_device_properties(struct axi_dma_chip *chip)
static int dw_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct axi_dma_chip *chip;
struct resource *mem;
struct dw_axi_dma *dw;
@ -1467,12 +1389,6 @@ static int dw_probe(struct platform_device *pdev)
if (IS_ERR(chip->regs))
return PTR_ERR(chip->regs);
if (of_device_is_compatible(node, "intel,kmb-axi-dma")) {
chip->apb_regs = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(chip->apb_regs))
return PTR_ERR(chip->apb_regs);
}
chip->core_clk = devm_clk_get(chip->dev, "core-clk");
if (IS_ERR(chip->core_clk))
return PTR_ERR(chip->core_clk);
@ -1507,6 +1423,8 @@ 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 */
@ -1544,11 +1462,7 @@ static int dw_probe(struct platform_device *pdev)
* Therefore, set constraint to 1024 * 4.
*/
dw->dma.dev->dma_parms = &dw->dma_parms;
#ifdef CONFIG_DW_AXI_DMAC_STARFIVE
dma_set_max_seg_size(&pdev->dev, DMAC_MAX_BLK_SIZE);
#else
dma_set_max_seg_size(&pdev->dev, MAX_BLOCK_SIZE);
#endif
platform_set_drvdata(pdev, chip);
pm_runtime_enable(chip->dev);
@ -1603,6 +1517,7 @@ static int dw_remove(struct platform_device *pdev)
for (i = 0; i < dw->hdata->nr_channels; i++) {
axi_chan_disable(&chip->dw->chan[i]);
axi_chan_irq_disable(&chip->dw->chan[i], DWAXIDMAC_IRQ_ALL);
tasklet_kill(&chan->dma_tasklet);
}
axi_dma_disable(chip);
@ -1628,7 +1543,6 @@ static const struct dev_pm_ops dw_axi_dma_pm_ops = {
static const struct of_device_id dw_dma_of_id_table[] = {
{ .compatible = "snps,axi-dma-1.01a" },
{ .compatible = "intel,kmb-axi-dma" },
{}
};
MODULE_DEVICE_TABLE(of, dw_dma_of_id_table);
@ -1638,7 +1552,7 @@ static struct platform_driver dw_driver = {
.remove = dw_remove,
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = dw_dma_of_id_table,
.of_match_table = of_match_ptr(dw_dma_of_id_table),
.pm = &dw_axi_dma_pm_ops,
},
};

1
drivers/dma/dw-axi-dmac/dw-axi-dmac.h Normal file → Executable file
View file

@ -59,6 +59,7 @@ struct axi_dma_chan {
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

@ -41,7 +41,6 @@
* Compatibility
*/
#define CONFIG_SND_STARFIVE
struct snd_pcm_hw_params_old {
unsigned int flags;
unsigned int masks[SNDRV_PCM_HW_PARAM_SUBFORMAT -
@ -730,11 +729,6 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
runtime->subformat = params_subformat(params);
runtime->channels = params_channels(params);
runtime->rate = params_rate(params);
#ifdef CONFIG_SND_STARFIVE
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
runtime->period_size = params_period_size(params)/2;
else
#endif
runtime->period_size = params_period_size(params);
runtime->periods = params_periods(params);
runtime->buffer_size = params_buffer_size(params);

77
sound/soc/starfive/starfive_pwmdac.c Normal file → Executable file
View file

@ -61,9 +61,8 @@ static int pwmdac_shift_bit_info(struct snd_kcontrol *kcontrol,
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = items;
if (uinfo->value.enumerated.item >= items) {
if (uinfo->value.enumerated.item >= items)
uinfo->value.enumerated.item = items - 1;
}
strcpy(uinfo->value.enumerated.name,
pwmdac_ct_shift_bit[uinfo->value.enumerated.item].name);
@ -150,43 +149,6 @@ static int pwmdac_duty_cycle_put(struct snd_kcontrol *kcontrol,
return 0;
}
static int pwmdac_datan_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 1;
uinfo->value.integer.max = PWMDAC_SAMPLE_CNT_511;
uinfo->value.integer.step = 1;
return 0;
}
static int pwmdac_datan_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = dev->datan;
return 0;
}
static int pwmdac_datan_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component);
int sel = ucontrol->value.integer.value[0];
if (sel > PWMDAC_SAMPLE_CNT_511)
return 0;
dev->datan = sel;
return 0;
}
static int pwmdac_data_mode_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@ -460,9 +422,7 @@ static void pwmdac_set(struct sf_pwmdac_dev *dev)
pwmdac_LR_data_change(dev, dev->lr_change);
pwmdac_data_mode(dev, dev->data_mode);
if (dev->shift) {
pwmdac_data_shift(dev, dev->shift);
}
pwmdac_data_shift(dev, dev->shift);
}
static void pwmdac_stop(struct sf_pwmdac_dev *dev)
@ -526,8 +486,6 @@ static int pwmdac_config(struct sf_pwmdac_dev *dev)
static int sf_pwmdac_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct sf_pwmdac_dev *dev = snd_soc_dai_get_drvdata(dai);
//pwmdac_set(dev);
return 0;
}
@ -592,6 +550,35 @@ static int sf_pwmdac_trigger(struct snd_pcm_substream *substream,
return 0;
}
static int sf_pwmdac_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
struct sf_pwmdac_dev *dev = dev_get_drvdata(dai->dev);
dev->play_dma_data.addr = dev->mapbase + PWMDAC_WDATA;
switch (params_channels(params)) {
case 2:
dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
break;
case 1:
dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
break;
default:
dev_err(dai->dev, "%d channels not supported\n",
params_channels(params));
return -EINVAL;
}
dev->play_dma_data.fifo_size = 1;
dev->play_dma_data.maxburst = 16;
snd_soc_dai_init_dma_data(dai, &dev->play_dma_data, NULL);
snd_soc_dai_set_drvdata(dai, dev);
return 0;
}
static int sf_pwmdac_dai_probe(struct snd_soc_dai *dai)
{
struct sf_pwmdac_dev *dev = dev_get_drvdata(dai->dev);
@ -624,7 +611,6 @@ static const struct snd_kcontrol_new pwmdac_snd_controls[] = {
};
static int pwmdac_probe(struct snd_soc_component *component)
{
struct sf_pwmdac_dev *priv = snd_soc_component_get_drvdata(component);
snd_soc_add_component_controls(component, pwmdac_snd_controls,
ARRAY_SIZE(pwmdac_snd_controls));
return 0;
@ -632,6 +618,7 @@ static int pwmdac_probe(struct snd_soc_component *component)
static const struct snd_soc_dai_ops sf_pwmdac_dai_ops = {
.hw_params = sf_pwmdac_hw_params,
.prepare = sf_pwmdac_prepare,
.trigger = sf_pwmdac_trigger,
};