diff options
| -rw-r--r-- | drivers/dma/s3c24xx-dma.c | 112 |
1 files changed, 111 insertions, 1 deletions
diff --git a/drivers/dma/s3c24xx-dma.c b/drivers/dma/s3c24xx-dma.c index 6528eeda1575..012520c9fd79 100644 --- a/drivers/dma/s3c24xx-dma.c +++ b/drivers/dma/s3c24xx-dma.c | |||
| @@ -164,6 +164,7 @@ struct s3c24xx_sg { | |||
| 164 | * @disrcc: value for source control register | 164 | * @disrcc: value for source control register |
| 165 | * @didstc: value for destination control register | 165 | * @didstc: value for destination control register |
| 166 | * @dcon: base value for dcon register | 166 | * @dcon: base value for dcon register |
| 167 | * @cyclic: indicate cyclic transfer | ||
| 167 | */ | 168 | */ |
| 168 | struct s3c24xx_txd { | 169 | struct s3c24xx_txd { |
| 169 | struct virt_dma_desc vd; | 170 | struct virt_dma_desc vd; |
| @@ -173,6 +174,7 @@ struct s3c24xx_txd { | |||
| 173 | u32 disrcc; | 174 | u32 disrcc; |
| 174 | u32 didstc; | 175 | u32 didstc; |
| 175 | u32 dcon; | 176 | u32 dcon; |
| 177 | bool cyclic; | ||
| 176 | }; | 178 | }; |
| 177 | 179 | ||
| 178 | struct s3c24xx_dma_chan; | 180 | struct s3c24xx_dma_chan; |
| @@ -669,8 +671,10 @@ static irqreturn_t s3c24xx_dma_irq(int irq, void *data) | |||
| 669 | /* when more sg's are in this txd, start the next one */ | 671 | /* when more sg's are in this txd, start the next one */ |
| 670 | if (!list_is_last(txd->at, &txd->dsg_list)) { | 672 | if (!list_is_last(txd->at, &txd->dsg_list)) { |
| 671 | txd->at = txd->at->next; | 673 | txd->at = txd->at->next; |
| 674 | if (txd->cyclic) | ||
| 675 | vchan_cyclic_callback(&txd->vd); | ||
| 672 | s3c24xx_dma_start_next_sg(s3cchan, txd); | 676 | s3c24xx_dma_start_next_sg(s3cchan, txd); |
| 673 | } else { | 677 | } else if (!txd->cyclic) { |
| 674 | s3cchan->at = NULL; | 678 | s3cchan->at = NULL; |
| 675 | vchan_cookie_complete(&txd->vd); | 679 | vchan_cookie_complete(&txd->vd); |
| 676 | 680 | ||
| @@ -682,6 +686,12 @@ static irqreturn_t s3c24xx_dma_irq(int irq, void *data) | |||
| 682 | s3c24xx_dma_start_next_txd(s3cchan); | 686 | s3c24xx_dma_start_next_txd(s3cchan); |
| 683 | else | 687 | else |
| 684 | s3c24xx_dma_phy_free(s3cchan); | 688 | s3c24xx_dma_phy_free(s3cchan); |
| 689 | } else { | ||
| 690 | vchan_cyclic_callback(&txd->vd); | ||
| 691 | |||
| 692 | /* Cyclic: reset at beginning */ | ||
| 693 | txd->at = txd->dsg_list.next; | ||
| 694 | s3c24xx_dma_start_next_sg(s3cchan, txd); | ||
| 685 | } | 695 | } |
| 686 | } | 696 | } |
| 687 | spin_unlock(&s3cchan->vc.lock); | 697 | spin_unlock(&s3cchan->vc.lock); |
| @@ -877,6 +887,104 @@ static struct dma_async_tx_descriptor *s3c24xx_dma_prep_memcpy( | |||
| 877 | return vchan_tx_prep(&s3cchan->vc, &txd->vd, flags); | 887 | return vchan_tx_prep(&s3cchan->vc, &txd->vd, flags); |
| 878 | } | 888 | } |
| 879 | 889 | ||
| 890 | static struct dma_async_tx_descriptor *s3c24xx_dma_prep_dma_cyclic( | ||
| 891 | struct dma_chan *chan, dma_addr_t addr, size_t size, size_t period, | ||
| 892 | enum dma_transfer_direction direction, unsigned long flags, | ||
| 893 | void *context) | ||
| 894 | { | ||
| 895 | struct s3c24xx_dma_chan *s3cchan = to_s3c24xx_dma_chan(chan); | ||
| 896 | struct s3c24xx_dma_engine *s3cdma = s3cchan->host; | ||
| 897 | const struct s3c24xx_dma_platdata *pdata = s3cdma->pdata; | ||
| 898 | struct s3c24xx_dma_channel *cdata = &pdata->channels[s3cchan->id]; | ||
| 899 | struct s3c24xx_txd *txd; | ||
| 900 | struct s3c24xx_sg *dsg; | ||
| 901 | unsigned sg_len; | ||
| 902 | dma_addr_t slave_addr; | ||
| 903 | u32 hwcfg = 0; | ||
| 904 | int i; | ||
| 905 | |||
| 906 | dev_dbg(&s3cdma->pdev->dev, | ||
| 907 | "prepare cyclic transaction of %zu bytes with period %zu from %s\n", | ||
| 908 | size, period, s3cchan->name); | ||
| 909 | |||
| 910 | if (!is_slave_direction(direction)) { | ||
| 911 | dev_err(&s3cdma->pdev->dev, | ||
| 912 | "direction %d unsupported\n", direction); | ||
| 913 | return NULL; | ||
| 914 | } | ||
| 915 | |||
| 916 | txd = s3c24xx_dma_get_txd(); | ||
| 917 | if (!txd) | ||
| 918 | return NULL; | ||
| 919 | |||
| 920 | txd->cyclic = 1; | ||
| 921 | |||
| 922 | if (cdata->handshake) | ||
| 923 | txd->dcon |= S3C24XX_DCON_HANDSHAKE; | ||
| 924 | |||
| 925 | switch (cdata->bus) { | ||
| 926 | case S3C24XX_DMA_APB: | ||
| 927 | txd->dcon |= S3C24XX_DCON_SYNC_PCLK; | ||
| 928 | hwcfg |= S3C24XX_DISRCC_LOC_APB; | ||
| 929 | break; | ||
| 930 | case S3C24XX_DMA_AHB: | ||
| 931 | txd->dcon |= S3C24XX_DCON_SYNC_HCLK; | ||
| 932 | hwcfg |= S3C24XX_DISRCC_LOC_AHB; | ||
| 933 | break; | ||
| 934 | } | ||
| 935 | |||
| 936 | /* | ||
| 937 | * Always assume our peripheral desintation is a fixed | ||
| 938 | * address in memory. | ||
| 939 | */ | ||
| 940 | hwcfg |= S3C24XX_DISRCC_INC_FIXED; | ||
| 941 | |||
| 942 | /* | ||
| 943 | * Individual dma operations are requested by the slave, | ||
| 944 | * so serve only single atomic operations (S3C24XX_DCON_SERV_SINGLE). | ||
| 945 | */ | ||
| 946 | txd->dcon |= S3C24XX_DCON_SERV_SINGLE; | ||
| 947 | |||
| 948 | if (direction == DMA_MEM_TO_DEV) { | ||
| 949 | txd->disrcc = S3C24XX_DISRCC_LOC_AHB | | ||
| 950 | S3C24XX_DISRCC_INC_INCREMENT; | ||
| 951 | txd->didstc = hwcfg; | ||
| 952 | slave_addr = s3cchan->cfg.dst_addr; | ||
| 953 | txd->width = s3cchan->cfg.dst_addr_width; | ||
| 954 | } else { | ||
| 955 | txd->disrcc = hwcfg; | ||
| 956 | txd->didstc = S3C24XX_DIDSTC_LOC_AHB | | ||
| 957 | S3C24XX_DIDSTC_INC_INCREMENT; | ||
| 958 | slave_addr = s3cchan->cfg.src_addr; | ||
| 959 | txd->width = s3cchan->cfg.src_addr_width; | ||
| 960 | } | ||
| 961 | |||
| 962 | sg_len = size / period; | ||
| 963 | |||
| 964 | for (i = 0; i < sg_len; i++) { | ||
| 965 | dsg = kzalloc(sizeof(*dsg), GFP_NOWAIT); | ||
| 966 | if (!dsg) { | ||
| 967 | s3c24xx_dma_free_txd(txd); | ||
| 968 | return NULL; | ||
| 969 | } | ||
| 970 | list_add_tail(&dsg->node, &txd->dsg_list); | ||
| 971 | |||
| 972 | dsg->len = period; | ||
| 973 | /* Check last period length */ | ||
| 974 | if (i == sg_len - 1) | ||
| 975 | dsg->len = size - period * i; | ||
| 976 | if (direction == DMA_MEM_TO_DEV) { | ||
| 977 | dsg->src_addr = addr + period * i; | ||
| 978 | dsg->dst_addr = slave_addr; | ||
| 979 | } else { /* DMA_DEV_TO_MEM */ | ||
| 980 | dsg->src_addr = slave_addr; | ||
| 981 | dsg->dst_addr = addr + period * i; | ||
| 982 | } | ||
| 983 | } | ||
| 984 | |||
| 985 | return vchan_tx_prep(&s3cchan->vc, &txd->vd, flags); | ||
| 986 | } | ||
| 987 | |||
| 880 | static struct dma_async_tx_descriptor *s3c24xx_dma_prep_slave_sg( | 988 | static struct dma_async_tx_descriptor *s3c24xx_dma_prep_slave_sg( |
| 881 | struct dma_chan *chan, struct scatterlist *sgl, | 989 | struct dma_chan *chan, struct scatterlist *sgl, |
| 882 | unsigned int sg_len, enum dma_transfer_direction direction, | 990 | unsigned int sg_len, enum dma_transfer_direction direction, |
| @@ -1197,6 +1305,7 @@ static int s3c24xx_dma_probe(struct platform_device *pdev) | |||
| 1197 | 1305 | ||
| 1198 | /* Initialize slave engine for SoC internal dedicated peripherals */ | 1306 | /* Initialize slave engine for SoC internal dedicated peripherals */ |
| 1199 | dma_cap_set(DMA_SLAVE, s3cdma->slave.cap_mask); | 1307 | dma_cap_set(DMA_SLAVE, s3cdma->slave.cap_mask); |
| 1308 | dma_cap_set(DMA_CYCLIC, s3cdma->slave.cap_mask); | ||
| 1200 | dma_cap_set(DMA_PRIVATE, s3cdma->slave.cap_mask); | 1309 | dma_cap_set(DMA_PRIVATE, s3cdma->slave.cap_mask); |
| 1201 | s3cdma->slave.dev = &pdev->dev; | 1310 | s3cdma->slave.dev = &pdev->dev; |
| 1202 | s3cdma->slave.device_alloc_chan_resources = | 1311 | s3cdma->slave.device_alloc_chan_resources = |
| @@ -1206,6 +1315,7 @@ static int s3c24xx_dma_probe(struct platform_device *pdev) | |||
| 1206 | s3cdma->slave.device_tx_status = s3c24xx_dma_tx_status; | 1315 | s3cdma->slave.device_tx_status = s3c24xx_dma_tx_status; |
| 1207 | s3cdma->slave.device_issue_pending = s3c24xx_dma_issue_pending; | 1316 | s3cdma->slave.device_issue_pending = s3c24xx_dma_issue_pending; |
| 1208 | s3cdma->slave.device_prep_slave_sg = s3c24xx_dma_prep_slave_sg; | 1317 | s3cdma->slave.device_prep_slave_sg = s3c24xx_dma_prep_slave_sg; |
| 1318 | s3cdma->slave.device_prep_dma_cyclic = s3c24xx_dma_prep_dma_cyclic; | ||
| 1209 | s3cdma->slave.device_control = s3c24xx_dma_control; | 1319 | s3cdma->slave.device_control = s3c24xx_dma_control; |
| 1210 | 1320 | ||
| 1211 | /* Register as many memcpy channels as there are physical channels */ | 1321 | /* Register as many memcpy channels as there are physical channels */ |
