diff options
Diffstat (limited to 'arch/arm/mach-s3c2410/dma.c')
| -rw-r--r-- | arch/arm/mach-s3c2410/dma.c | 163 |
1 files changed, 144 insertions, 19 deletions
diff --git a/arch/arm/mach-s3c2410/dma.c b/arch/arm/mach-s3c2410/dma.c index 094cc52745c5..25855452fe8c 100644 --- a/arch/arm/mach-s3c2410/dma.c +++ b/arch/arm/mach-s3c2410/dma.c | |||
| @@ -112,7 +112,7 @@ dmadbg_capture(s3c2410_dma_chan_t *chan, struct s3c2410_dma_regstate *regs) | |||
| 112 | } | 112 | } |
| 113 | 113 | ||
| 114 | static void | 114 | static void |
| 115 | dmadbg_showregs(const char *fname, int line, s3c2410_dma_chan_t *chan, | 115 | dmadbg_dumpregs(const char *fname, int line, s3c2410_dma_chan_t *chan, |
| 116 | struct s3c2410_dma_regstate *regs) | 116 | struct s3c2410_dma_regstate *regs) |
| 117 | { | 117 | { |
| 118 | printk(KERN_DEBUG "dma%d: %s:%d: DCSRC=%08lx, DISRC=%08lx, DSTAT=%08lx DMT=%02lx, DCON=%08lx\n", | 118 | printk(KERN_DEBUG "dma%d: %s:%d: DCSRC=%08lx, DISRC=%08lx, DSTAT=%08lx DMT=%02lx, DCON=%08lx\n", |
| @@ -132,7 +132,16 @@ dmadbg_showchan(const char *fname, int line, s3c2410_dma_chan_t *chan) | |||
| 132 | chan->number, fname, line, chan->load_state, | 132 | chan->number, fname, line, chan->load_state, |
| 133 | chan->curr, chan->next, chan->end); | 133 | chan->curr, chan->next, chan->end); |
| 134 | 134 | ||
| 135 | dmadbg_showregs(fname, line, chan, &state); | 135 | dmadbg_dumpregs(fname, line, chan, &state); |
| 136 | } | ||
| 137 | |||
| 138 | static void | ||
| 139 | dmadbg_showregs(const char *fname, int line, s3c2410_dma_chan_t *chan) | ||
| 140 | { | ||
| 141 | struct s3c2410_dma_regstate state; | ||
| 142 | |||
| 143 | dmadbg_capture(chan, &state); | ||
| 144 | dmadbg_dumpregs(fname, line, chan, &state); | ||
| 136 | } | 145 | } |
| 137 | 146 | ||
| 138 | #define dbg_showregs(chan) dmadbg_showregs(__FUNCTION__, __LINE__, (chan)) | 147 | #define dbg_showregs(chan) dmadbg_showregs(__FUNCTION__, __LINE__, (chan)) |
| @@ -253,10 +262,14 @@ s3c2410_dma_loadbuffer(s3c2410_dma_chan_t *chan, | |||
| 253 | buf->next); | 262 | buf->next); |
| 254 | reload = (buf->next == NULL) ? S3C2410_DCON_NORELOAD : 0; | 263 | reload = (buf->next == NULL) ? S3C2410_DCON_NORELOAD : 0; |
| 255 | } else { | 264 | } else { |
| 256 | pr_debug("load_state is %d => autoreload\n", chan->load_state); | 265 | //pr_debug("load_state is %d => autoreload\n", chan->load_state); |
| 257 | reload = S3C2410_DCON_AUTORELOAD; | 266 | reload = S3C2410_DCON_AUTORELOAD; |
| 258 | } | 267 | } |
| 259 | 268 | ||
| 269 | if ((buf->data & 0xf0000000) != 0x30000000) { | ||
| 270 | dmawarn("dmaload: buffer is %p\n", (void *)buf->data); | ||
| 271 | } | ||
| 272 | |||
| 260 | writel(buf->data, chan->addr_reg); | 273 | writel(buf->data, chan->addr_reg); |
| 261 | 274 | ||
| 262 | dma_wrreg(chan, S3C2410_DMA_DCON, | 275 | dma_wrreg(chan, S3C2410_DMA_DCON, |
| @@ -370,7 +383,7 @@ static int s3c2410_dma_start(s3c2410_dma_chan_t *chan) | |||
| 370 | tmp |= S3C2410_DMASKTRIG_ON; | 383 | tmp |= S3C2410_DMASKTRIG_ON; |
| 371 | dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); | 384 | dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); |
| 372 | 385 | ||
| 373 | pr_debug("wrote %08lx to DMASKTRIG\n", tmp); | 386 | pr_debug("dma%d: %08lx to DMASKTRIG\n", chan->number, tmp); |
| 374 | 387 | ||
| 375 | #if 0 | 388 | #if 0 |
| 376 | /* the dma buffer loads should take care of clearing the AUTO | 389 | /* the dma buffer loads should take care of clearing the AUTO |
| @@ -384,7 +397,30 @@ static int s3c2410_dma_start(s3c2410_dma_chan_t *chan) | |||
| 384 | 397 | ||
| 385 | dbg_showchan(chan); | 398 | dbg_showchan(chan); |
| 386 | 399 | ||
| 400 | /* if we've only loaded one buffer onto the channel, then chec | ||
| 401 | * to see if we have another, and if so, try and load it so when | ||
| 402 | * the first buffer is finished, the new one will be loaded onto | ||
| 403 | * the channel */ | ||
| 404 | |||
| 405 | if (chan->next != NULL) { | ||
| 406 | if (chan->load_state == S3C2410_DMALOAD_1LOADED) { | ||
| 407 | |||
| 408 | if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { | ||
| 409 | pr_debug("%s: buff not yet loaded, no more todo\n", | ||
| 410 | __FUNCTION__); | ||
| 411 | } else { | ||
| 412 | chan->load_state = S3C2410_DMALOAD_1RUNNING; | ||
| 413 | s3c2410_dma_loadbuffer(chan, chan->next); | ||
| 414 | } | ||
| 415 | |||
| 416 | } else if (chan->load_state == S3C2410_DMALOAD_1RUNNING) { | ||
| 417 | s3c2410_dma_loadbuffer(chan, chan->next); | ||
| 418 | } | ||
| 419 | } | ||
| 420 | |||
| 421 | |||
| 387 | local_irq_restore(flags); | 422 | local_irq_restore(flags); |
| 423 | |||
| 388 | return 0; | 424 | return 0; |
| 389 | } | 425 | } |
| 390 | 426 | ||
| @@ -436,12 +472,11 @@ int s3c2410_dma_enqueue(unsigned int channel, void *id, | |||
| 436 | buf = kmem_cache_alloc(dma_kmem, GFP_ATOMIC); | 472 | buf = kmem_cache_alloc(dma_kmem, GFP_ATOMIC); |
| 437 | if (buf == NULL) { | 473 | if (buf == NULL) { |
| 438 | pr_debug("%s: out of memory (%ld alloc)\n", | 474 | pr_debug("%s: out of memory (%ld alloc)\n", |
| 439 | __FUNCTION__, sizeof(*buf)); | 475 | __FUNCTION__, (long)sizeof(*buf)); |
| 440 | return -ENOMEM; | 476 | return -ENOMEM; |
| 441 | } | 477 | } |
| 442 | 478 | ||
| 443 | pr_debug("%s: new buffer %p\n", __FUNCTION__, buf); | 479 | //pr_debug("%s: new buffer %p\n", __FUNCTION__, buf); |
| 444 | |||
| 445 | //dbg_showchan(chan); | 480 | //dbg_showchan(chan); |
| 446 | 481 | ||
| 447 | buf->next = NULL; | 482 | buf->next = NULL; |
| @@ -537,14 +572,20 @@ s3c2410_dma_lastxfer(s3c2410_dma_chan_t *chan) | |||
| 537 | case S3C2410_DMALOAD_1LOADED: | 572 | case S3C2410_DMALOAD_1LOADED: |
| 538 | if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { | 573 | if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { |
| 539 | /* flag error? */ | 574 | /* flag error? */ |
| 540 | printk(KERN_ERR "dma%d: timeout waiting for load\n", | 575 | printk(KERN_ERR "dma%d: timeout waiting for load (%s)\n", |
| 541 | chan->number); | 576 | chan->number, __FUNCTION__); |
| 542 | return; | 577 | return; |
| 543 | } | 578 | } |
| 544 | break; | 579 | break; |
| 545 | 580 | ||
| 581 | case S3C2410_DMALOAD_1LOADED_1RUNNING: | ||
| 582 | /* I belive in this case we do not have anything to do | ||
| 583 | * until the next buffer comes along, and we turn off the | ||
| 584 | * reload */ | ||
| 585 | return; | ||
| 586 | |||
| 546 | default: | 587 | default: |
| 547 | pr_debug("dma%d: lastxfer: unhandled load_state %d with no next", | 588 | pr_debug("dma%d: lastxfer: unhandled load_state %d with no next\n", |
| 548 | chan->number, chan->load_state); | 589 | chan->number, chan->load_state); |
| 549 | return; | 590 | return; |
| 550 | 591 | ||
| @@ -629,7 +670,14 @@ s3c2410_dma_irq(int irq, void *devpw, struct pt_regs *regs) | |||
| 629 | } else { | 670 | } else { |
| 630 | } | 671 | } |
| 631 | 672 | ||
| 632 | if (chan->next != NULL) { | 673 | /* only reload if the channel is still running... our buffer done |
| 674 | * routine may have altered the state by requesting the dma channel | ||
| 675 | * to stop or shutdown... */ | ||
| 676 | |||
| 677 | /* todo: check that when the channel is shut-down from inside this | ||
| 678 | * function, we cope with unsetting reload, etc */ | ||
| 679 | |||
| 680 | if (chan->next != NULL && chan->state != S3C2410_DMA_IDLE) { | ||
| 633 | unsigned long flags; | 681 | unsigned long flags; |
| 634 | 682 | ||
| 635 | switch (chan->load_state) { | 683 | switch (chan->load_state) { |
| @@ -644,8 +692,8 @@ s3c2410_dma_irq(int irq, void *devpw, struct pt_regs *regs) | |||
| 644 | case S3C2410_DMALOAD_1LOADED: | 692 | case S3C2410_DMALOAD_1LOADED: |
| 645 | if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { | 693 | if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { |
| 646 | /* flag error? */ | 694 | /* flag error? */ |
| 647 | printk(KERN_ERR "dma%d: timeout waiting for load\n", | 695 | printk(KERN_ERR "dma%d: timeout waiting for load (%s)\n", |
| 648 | chan->number); | 696 | chan->number, __FUNCTION__); |
| 649 | return IRQ_HANDLED; | 697 | return IRQ_HANDLED; |
| 650 | } | 698 | } |
| 651 | 699 | ||
| @@ -678,8 +726,6 @@ s3c2410_dma_irq(int irq, void *devpw, struct pt_regs *regs) | |||
| 678 | return IRQ_HANDLED; | 726 | return IRQ_HANDLED; |
| 679 | } | 727 | } |
| 680 | 728 | ||
| 681 | |||
| 682 | |||
| 683 | /* s3c2410_request_dma | 729 | /* s3c2410_request_dma |
| 684 | * | 730 | * |
| 685 | * get control of an dma channel | 731 | * get control of an dma channel |
| @@ -718,11 +764,17 @@ int s3c2410_dma_request(unsigned int channel, s3c2410_dma_client_t *client, | |||
| 718 | pr_debug("dma%d: %s : requesting irq %d\n", | 764 | pr_debug("dma%d: %s : requesting irq %d\n", |
| 719 | channel, __FUNCTION__, chan->irq); | 765 | channel, __FUNCTION__, chan->irq); |
| 720 | 766 | ||
| 767 | chan->irq_claimed = 1; | ||
| 768 | local_irq_restore(flags); | ||
| 769 | |||
| 721 | err = request_irq(chan->irq, s3c2410_dma_irq, IRQF_DISABLED, | 770 | err = request_irq(chan->irq, s3c2410_dma_irq, IRQF_DISABLED, |
| 722 | client->name, (void *)chan); | 771 | client->name, (void *)chan); |
| 723 | 772 | ||
| 773 | local_irq_save(flags); | ||
| 774 | |||
| 724 | if (err) { | 775 | if (err) { |
| 725 | chan->in_use = 0; | 776 | chan->in_use = 0; |
| 777 | chan->irq_claimed = 0; | ||
| 726 | local_irq_restore(flags); | 778 | local_irq_restore(flags); |
| 727 | 779 | ||
| 728 | printk(KERN_ERR "%s: cannot get IRQ %d for DMA %d\n", | 780 | printk(KERN_ERR "%s: cannot get IRQ %d for DMA %d\n", |
| @@ -730,7 +782,6 @@ int s3c2410_dma_request(unsigned int channel, s3c2410_dma_client_t *client, | |||
| 730 | return err; | 782 | return err; |
| 731 | } | 783 | } |
| 732 | 784 | ||
| 733 | chan->irq_claimed = 1; | ||
| 734 | chan->irq_enabled = 1; | 785 | chan->irq_enabled = 1; |
| 735 | } | 786 | } |
| 736 | 787 | ||
| @@ -810,6 +861,7 @@ static int s3c2410_dma_dostop(s3c2410_dma_chan_t *chan) | |||
| 810 | 861 | ||
| 811 | tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); | 862 | tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); |
| 812 | tmp |= S3C2410_DMASKTRIG_STOP; | 863 | tmp |= S3C2410_DMASKTRIG_STOP; |
| 864 | //tmp &= ~S3C2410_DMASKTRIG_ON; | ||
| 813 | dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); | 865 | dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); |
| 814 | 866 | ||
| 815 | #if 0 | 867 | #if 0 |
| @@ -819,6 +871,7 @@ static int s3c2410_dma_dostop(s3c2410_dma_chan_t *chan) | |||
| 819 | dma_wrreg(chan, S3C2410_DMA_DCON, tmp); | 871 | dma_wrreg(chan, S3C2410_DMA_DCON, tmp); |
| 820 | #endif | 872 | #endif |
| 821 | 873 | ||
| 874 | /* should stop do this, or should we wait for flush? */ | ||
| 822 | chan->state = S3C2410_DMA_IDLE; | 875 | chan->state = S3C2410_DMA_IDLE; |
| 823 | chan->load_state = S3C2410_DMALOAD_NONE; | 876 | chan->load_state = S3C2410_DMALOAD_NONE; |
| 824 | 877 | ||
| @@ -827,6 +880,22 @@ static int s3c2410_dma_dostop(s3c2410_dma_chan_t *chan) | |||
| 827 | return 0; | 880 | return 0; |
| 828 | } | 881 | } |
| 829 | 882 | ||
| 883 | void s3c2410_dma_waitforstop(s3c2410_dma_chan_t *chan) | ||
| 884 | { | ||
| 885 | unsigned long tmp; | ||
| 886 | unsigned int timeout = 0x10000; | ||
| 887 | |||
| 888 | while (timeout-- > 0) { | ||
| 889 | tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); | ||
| 890 | |||
| 891 | if (!(tmp & S3C2410_DMASKTRIG_ON)) | ||
| 892 | return; | ||
| 893 | } | ||
| 894 | |||
| 895 | pr_debug("dma%d: failed to stop?\n", chan->number); | ||
| 896 | } | ||
| 897 | |||
| 898 | |||
| 830 | /* s3c2410_dma_flush | 899 | /* s3c2410_dma_flush |
| 831 | * | 900 | * |
| 832 | * stop the channel, and remove all current and pending transfers | 901 | * stop the channel, and remove all current and pending transfers |
| @@ -837,7 +906,9 @@ static int s3c2410_dma_flush(s3c2410_dma_chan_t *chan) | |||
| 837 | s3c2410_dma_buf_t *buf, *next; | 906 | s3c2410_dma_buf_t *buf, *next; |
| 838 | unsigned long flags; | 907 | unsigned long flags; |
| 839 | 908 | ||
| 840 | pr_debug("%s:\n", __FUNCTION__); | 909 | pr_debug("%s: chan %p (%d)\n", __FUNCTION__, chan, chan->number); |
| 910 | |||
| 911 | dbg_showchan(chan); | ||
| 841 | 912 | ||
| 842 | local_irq_save(flags); | 913 | local_irq_save(flags); |
| 843 | 914 | ||
| @@ -864,11 +935,64 @@ static int s3c2410_dma_flush(s3c2410_dma_chan_t *chan) | |||
| 864 | } | 935 | } |
| 865 | } | 936 | } |
| 866 | 937 | ||
| 938 | dbg_showregs(chan); | ||
| 939 | |||
| 940 | s3c2410_dma_waitforstop(chan); | ||
| 941 | |||
| 942 | #if 0 | ||
| 943 | /* should also clear interrupts, according to WinCE BSP */ | ||
| 944 | { | ||
| 945 | unsigned long tmp; | ||
| 946 | |||
| 947 | tmp = dma_rdreg(chan, S3C2410_DMA_DCON); | ||
| 948 | tmp |= S3C2410_DCON_NORELOAD; | ||
| 949 | dma_wrreg(chan, S3C2410_DMA_DCON, tmp); | ||
| 950 | } | ||
| 951 | #endif | ||
| 952 | |||
| 953 | dbg_showregs(chan); | ||
| 954 | |||
| 867 | local_irq_restore(flags); | 955 | local_irq_restore(flags); |
| 868 | 956 | ||
| 869 | return 0; | 957 | return 0; |
| 870 | } | 958 | } |
| 871 | 959 | ||
| 960 | int | ||
| 961 | s3c2410_dma_started(s3c2410_dma_chan_t *chan) | ||
| 962 | { | ||
| 963 | unsigned long flags; | ||
| 964 | |||
| 965 | local_irq_save(flags); | ||
| 966 | |||
| 967 | dbg_showchan(chan); | ||
| 968 | |||
| 969 | /* if we've only loaded one buffer onto the channel, then chec | ||
| 970 | * to see if we have another, and if so, try and load it so when | ||
| 971 | * the first buffer is finished, the new one will be loaded onto | ||
| 972 | * the channel */ | ||
| 973 | |||
| 974 | if (chan->next != NULL) { | ||
| 975 | if (chan->load_state == S3C2410_DMALOAD_1LOADED) { | ||
| 976 | |||
| 977 | if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { | ||
| 978 | pr_debug("%s: buff not yet loaded, no more todo\n", | ||
| 979 | __FUNCTION__); | ||
| 980 | } else { | ||
| 981 | chan->load_state = S3C2410_DMALOAD_1RUNNING; | ||
| 982 | s3c2410_dma_loadbuffer(chan, chan->next); | ||
| 983 | } | ||
| 984 | |||
| 985 | } else if (chan->load_state == S3C2410_DMALOAD_1RUNNING) { | ||
| 986 | s3c2410_dma_loadbuffer(chan, chan->next); | ||
| 987 | } | ||
| 988 | } | ||
| 989 | |||
| 990 | |||
| 991 | local_irq_restore(flags); | ||
| 992 | |||
| 993 | return 0; | ||
| 994 | |||
| 995 | } | ||
| 872 | 996 | ||
| 873 | int | 997 | int |
| 874 | s3c2410_dma_ctrl(dmach_t channel, s3c2410_chan_op_t op) | 998 | s3c2410_dma_ctrl(dmach_t channel, s3c2410_chan_op_t op) |
| @@ -885,14 +1009,15 @@ s3c2410_dma_ctrl(dmach_t channel, s3c2410_chan_op_t op) | |||
| 885 | return s3c2410_dma_dostop(chan); | 1009 | return s3c2410_dma_dostop(chan); |
| 886 | 1010 | ||
| 887 | case S3C2410_DMAOP_PAUSE: | 1011 | case S3C2410_DMAOP_PAUSE: |
| 888 | return -ENOENT; | ||
| 889 | |||
| 890 | case S3C2410_DMAOP_RESUME: | 1012 | case S3C2410_DMAOP_RESUME: |
| 891 | return -ENOENT; | 1013 | return -ENOENT; |
| 892 | 1014 | ||
| 893 | case S3C2410_DMAOP_FLUSH: | 1015 | case S3C2410_DMAOP_FLUSH: |
| 894 | return s3c2410_dma_flush(chan); | 1016 | return s3c2410_dma_flush(chan); |
| 895 | 1017 | ||
| 1018 | case S3C2410_DMAOP_STARTED: | ||
| 1019 | return s3c2410_dma_started(chan); | ||
| 1020 | |||
| 896 | case S3C2410_DMAOP_TIMEOUT: | 1021 | case S3C2410_DMAOP_TIMEOUT: |
| 897 | return 0; | 1022 | return 0; |
| 898 | 1023 | ||
