VisionFive2 Linux kernel

StarFive Tech Linux Kernel for VisionFive (JH7110) boards (mirror)

More than 9999 Commits   35 Branches   59 Tags
author: jenny.zhang <jenny.zhang@starfivetech.com> 2022-01-27 22:30:49 -0800 committer: jenny.zhang <jenny.zhang@starfivetech.com> 2022-01-27 22:30:49 -0800 commit: f51ce469059cd1aec79a6981ec3ebd4b957a41b4 parent: 7d3709aaf0e97c008e52595bb723cb8e4a11d0e6
Commit Summary:
1.[alsa]Resolve the failure of pwmdac to play mono 8K audio 2.[dma] Modify the DMA error interrupt handling process
Diffstat:
4 files changed, 80 insertions, 176 deletions
diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
old mode 100644
new mode 100755
index 60f56e670919..ddc235df4c3b
--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
+++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
@@ -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)
+
+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;
-#ifdef CONFIG_DW_AXI_DMAC_STARFIVE
-	struct axi_dma_desc *desc;
-#endif
-	spin_lock_irqsave(&chan->vc.lock, flags);
+	u32 val;
+	int ret;
 
-	axi_chan_disable(chan);
+	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,
 	},
 };
diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
old mode 100644
new mode 100755
index f0520a493e31..6715a51aa649
--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
+++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
@@ -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 {
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 9641e0137804..cb795135ffc2 100755
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -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);
diff --git a/sound/soc/starfive/starfive_pwmdac.c b/sound/soc/starfive/starfive_pwmdac.c
old mode 100644
new mode 100755
index ee1fceb391b5..1c25e1e2701d
--- a/sound/soc/starfive/starfive_pwmdac.c
+++ b/sound/soc/starfive/starfive_pwmdac.c
@@ -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,
 };