aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/dma
diff options
context:
space:
mode:
authorAndy Shevchenko <andriy.shevchenko@linux.intel.com>2013-01-25 04:48:03 -0500
committerVinod Koul <vinod.koul@intel.com>2013-01-28 07:04:50 -0500
commit4702d5244ca947263e8b7eb2ba6d8721e80c46e2 (patch)
tree127c5fdd5590467d781aca04685517e97d02bb01 /drivers/dma
parent176dcec50f3f0bc46f11b983c1a3bbc2dd3514fd (diff)
dw_dmac: return proper residue value
Currently the driver returns full length of the active descriptor which is wrong. We have to go throught the active descriptor and substract the length of each sent children in the chain from the total length along with the actual data in the DMA channel registers. The cyclic case is not handled by this patch due to len field in the descriptor structure is left untouched by the original code. Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Acked-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Diffstat (limited to 'drivers/dma')
-rw-r--r--drivers/dma/dw_dmac.c47
-rw-r--r--drivers/dma/dw_dmac_regs.h1
2 files changed, 46 insertions, 2 deletions
diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c
index 5816da34129d..35c8dd5f1013 100644
--- a/drivers/dma/dw_dmac.c
+++ b/drivers/dma/dw_dmac.c
@@ -282,6 +282,7 @@ static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first)
282 282
283 dwc_initialize(dwc); 283 dwc_initialize(dwc);
284 284
285 dwc->residue = first->total_len;
285 dwc->tx_node_active = &first->tx_list; 286 dwc->tx_node_active = &first->tx_list;
286 287
287 /* Submit first block */ 288 /* Submit first block */
@@ -385,6 +386,15 @@ static void dwc_complete_all(struct dw_dma *dw, struct dw_dma_chan *dwc)
385 dwc_descriptor_complete(dwc, desc, true); 386 dwc_descriptor_complete(dwc, desc, true);
386} 387}
387 388
389/* Returns how many bytes were already received from source */
390static inline u32 dwc_get_sent(struct dw_dma_chan *dwc)
391{
392 u32 ctlhi = channel_readl(dwc, CTL_HI);
393 u32 ctllo = channel_readl(dwc, CTL_LO);
394
395 return (ctlhi & DWC_CTLH_BLOCK_TS_MASK) * (1 << (ctllo >> 4 & 7));
396}
397
388static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) 398static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
389{ 399{
390 dma_addr_t llp; 400 dma_addr_t llp;
@@ -412,6 +422,12 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
412 422
413 head = &desc->tx_list; 423 head = &desc->tx_list;
414 if (active != head) { 424 if (active != head) {
425 /* Update desc to reflect last sent one */
426 if (active != head->next)
427 desc = to_dw_desc(active->prev);
428
429 dwc->residue -= desc->len;
430
415 child = to_dw_desc(active); 431 child = to_dw_desc(active);
416 432
417 /* Submit next block */ 433 /* Submit next block */
@@ -424,6 +440,9 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
424 /* We are done here */ 440 /* We are done here */
425 clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); 441 clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags);
426 } 442 }
443
444 dwc->residue = 0;
445
427 spin_unlock_irqrestore(&dwc->lock, flags); 446 spin_unlock_irqrestore(&dwc->lock, flags);
428 447
429 dwc_complete_all(dw, dwc); 448 dwc_complete_all(dw, dwc);
@@ -431,6 +450,7 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
431 } 450 }
432 451
433 if (list_empty(&dwc->active_list)) { 452 if (list_empty(&dwc->active_list)) {
453 dwc->residue = 0;
434 spin_unlock_irqrestore(&dwc->lock, flags); 454 spin_unlock_irqrestore(&dwc->lock, flags);
435 return; 455 return;
436 } 456 }
@@ -445,6 +465,9 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
445 (unsigned long long)llp); 465 (unsigned long long)llp);
446 466
447 list_for_each_entry_safe(desc, _desc, &dwc->active_list, desc_node) { 467 list_for_each_entry_safe(desc, _desc, &dwc->active_list, desc_node) {
468 /* initial residue value */
469 dwc->residue = desc->total_len;
470
448 /* check first descriptors addr */ 471 /* check first descriptors addr */
449 if (desc->txd.phys == llp) { 472 if (desc->txd.phys == llp) {
450 spin_unlock_irqrestore(&dwc->lock, flags); 473 spin_unlock_irqrestore(&dwc->lock, flags);
@@ -454,16 +477,21 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
454 /* check first descriptors llp */ 477 /* check first descriptors llp */
455 if (desc->lli.llp == llp) { 478 if (desc->lli.llp == llp) {
456 /* This one is currently in progress */ 479 /* This one is currently in progress */
480 dwc->residue -= dwc_get_sent(dwc);
457 spin_unlock_irqrestore(&dwc->lock, flags); 481 spin_unlock_irqrestore(&dwc->lock, flags);
458 return; 482 return;
459 } 483 }
460 484
461 list_for_each_entry(child, &desc->tx_list, desc_node) 485 dwc->residue -= desc->len;
486 list_for_each_entry(child, &desc->tx_list, desc_node) {
462 if (child->lli.llp == llp) { 487 if (child->lli.llp == llp) {
463 /* Currently in progress */ 488 /* Currently in progress */
489 dwc->residue -= dwc_get_sent(dwc);
464 spin_unlock_irqrestore(&dwc->lock, flags); 490 spin_unlock_irqrestore(&dwc->lock, flags);
465 return; 491 return;
466 } 492 }
493 dwc->residue -= child->len;
494 }
467 495
468 /* 496 /*
469 * No descriptors so far seem to be in progress, i.e. 497 * No descriptors so far seem to be in progress, i.e.
@@ -1054,6 +1082,21 @@ static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
1054 return 0; 1082 return 0;
1055} 1083}
1056 1084
1085static inline u32 dwc_get_residue(struct dw_dma_chan *dwc)
1086{
1087 unsigned long flags;
1088 u32 residue;
1089
1090 spin_lock_irqsave(&dwc->lock, flags);
1091
1092 residue = dwc->residue;
1093 if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags) && residue)
1094 residue -= dwc_get_sent(dwc);
1095
1096 spin_unlock_irqrestore(&dwc->lock, flags);
1097 return residue;
1098}
1099
1057static enum dma_status 1100static enum dma_status
1058dwc_tx_status(struct dma_chan *chan, 1101dwc_tx_status(struct dma_chan *chan,
1059 dma_cookie_t cookie, 1102 dma_cookie_t cookie,
@@ -1070,7 +1113,7 @@ dwc_tx_status(struct dma_chan *chan,
1070 } 1113 }
1071 1114
1072 if (ret != DMA_SUCCESS) 1115 if (ret != DMA_SUCCESS)
1073 dma_set_residue(txstate, dwc_first_active(dwc)->len); 1116 dma_set_residue(txstate, dwc_get_residue(dwc));
1074 1117
1075 if (dwc->paused) 1118 if (dwc->paused)
1076 return DMA_PAUSED; 1119 return DMA_PAUSED;
diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h
index 833b4cf9843d..88dd8eb31957 100644
--- a/drivers/dma/dw_dmac_regs.h
+++ b/drivers/dma/dw_dmac_regs.h
@@ -203,6 +203,7 @@ struct dw_dma_chan {
203 struct list_head active_list; 203 struct list_head active_list;
204 struct list_head queue; 204 struct list_head queue;
205 struct list_head free_list; 205 struct list_head free_list;
206 u32 residue;
206 struct dw_cyclic_desc *cdesc; 207 struct dw_cyclic_desc *cdesc;
207 208
208 unsigned int descs_allocated; 209 unsigned int descs_allocated;