diff options
author | Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | 2012-02-03 03:59:33 -0500 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2012-02-19 21:36:16 -0500 |
commit | 7da9ced6066c654a22836c24bae509ef323e10a8 (patch) | |
tree | 82f1a7abad998927ffdf3e93076f9ee0198a2384 /sound/soc/sh/fsi.c | |
parent | fa2c8f401797eee814b7b9fa0b23fa6c4c3f5533 (diff) |
ASoC: fsi: Add DMAEngine support
This patch supports DMAEngine to FSI driver.
It supports only Tx case at this point.
If platform/cpu doesn't support DMAEngine, FSI driver will
use PIO transfer.
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/sh/fsi.c')
-rw-r--r-- | sound/soc/sh/fsi.c | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 13746809c27d..378cc5b056d7 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c | |||
@@ -13,8 +13,11 @@ | |||
13 | */ | 13 | */ |
14 | 14 | ||
15 | #include <linux/delay.h> | 15 | #include <linux/delay.h> |
16 | #include <linux/dma-mapping.h> | ||
16 | #include <linux/pm_runtime.h> | 17 | #include <linux/pm_runtime.h> |
17 | #include <linux/io.h> | 18 | #include <linux/io.h> |
19 | #include <linux/scatterlist.h> | ||
20 | #include <linux/sh_dma.h> | ||
18 | #include <linux/slab.h> | 21 | #include <linux/slab.h> |
19 | #include <linux/module.h> | 22 | #include <linux/module.h> |
20 | #include <sound/soc.h> | 23 | #include <sound/soc.h> |
@@ -53,6 +56,7 @@ | |||
53 | 56 | ||
54 | /* DO_FMT */ | 57 | /* DO_FMT */ |
55 | /* DI_FMT */ | 58 | /* DI_FMT */ |
59 | #define CR_BWS_MASK (0x3 << 20) /* FSI2 */ | ||
56 | #define CR_BWS_24 (0x0 << 20) /* FSI2 */ | 60 | #define CR_BWS_24 (0x0 << 20) /* FSI2 */ |
57 | #define CR_BWS_16 (0x1 << 20) /* FSI2 */ | 61 | #define CR_BWS_16 (0x1 << 20) /* FSI2 */ |
58 | #define CR_BWS_20 (0x2 << 20) /* FSI2 */ | 62 | #define CR_BWS_20 (0x2 << 20) /* FSI2 */ |
@@ -68,6 +72,15 @@ | |||
68 | #define CR_TDM (0x4 << 4) | 72 | #define CR_TDM (0x4 << 4) |
69 | #define CR_TDM_D (0x5 << 4) | 73 | #define CR_TDM_D (0x5 << 4) |
70 | 74 | ||
75 | /* OUT_DMAC */ | ||
76 | /* IN_DMAC */ | ||
77 | #define VDMD_MASK (0x3 << 4) | ||
78 | #define VDMD_FRONT (0x0 << 4) /* Package in front */ | ||
79 | #define VDMD_BACK (0x1 << 4) /* Package in back */ | ||
80 | #define VDMD_STREAM (0x2 << 4) /* Stream mode(16bit * 2) */ | ||
81 | |||
82 | #define DMA_ON (0x1 << 0) | ||
83 | |||
71 | /* DOFF_CTL */ | 84 | /* DOFF_CTL */ |
72 | /* DIFF_CTL */ | 85 | /* DIFF_CTL */ |
73 | #define IRQ_HALF 0x00100000 | 86 | #define IRQ_HALF 0x00100000 |
@@ -180,6 +193,14 @@ struct fsi_stream { | |||
180 | */ | 193 | */ |
181 | struct fsi_stream_handler *handler; | 194 | struct fsi_stream_handler *handler; |
182 | struct fsi_priv *priv; | 195 | struct fsi_priv *priv; |
196 | |||
197 | /* | ||
198 | * these are for DMAEngine | ||
199 | */ | ||
200 | struct dma_chan *chan; | ||
201 | struct sh_dmae_slave slave; /* see fsi_handler_init() */ | ||
202 | struct tasklet_struct tasklet; | ||
203 | dma_addr_t dma; | ||
183 | }; | 204 | }; |
184 | 205 | ||
185 | struct fsi_priv { | 206 | struct fsi_priv { |
@@ -889,6 +910,212 @@ static irqreturn_t fsi_interrupt(int irq, void *data) | |||
889 | } | 910 | } |
890 | 911 | ||
891 | /* | 912 | /* |
913 | * dma data transfer handler | ||
914 | */ | ||
915 | static int fsi_dma_init(struct fsi_priv *fsi, struct fsi_stream *io) | ||
916 | { | ||
917 | struct snd_pcm_runtime *runtime = io->substream->runtime; | ||
918 | struct snd_soc_dai *dai = fsi_get_dai(io->substream); | ||
919 | enum dma_data_direction dir = fsi_stream_is_play(fsi, io) ? | ||
920 | DMA_TO_DEVICE : DMA_FROM_DEVICE; | ||
921 | |||
922 | io->dma = dma_map_single(dai->dev, runtime->dma_area, | ||
923 | snd_pcm_lib_buffer_bytes(io->substream), dir); | ||
924 | return 0; | ||
925 | } | ||
926 | |||
927 | static int fsi_dma_quit(struct fsi_priv *fsi, struct fsi_stream *io) | ||
928 | { | ||
929 | struct snd_soc_dai *dai = fsi_get_dai(io->substream); | ||
930 | enum dma_data_direction dir = fsi_stream_is_play(fsi, io) ? | ||
931 | DMA_TO_DEVICE : DMA_FROM_DEVICE; | ||
932 | |||
933 | dma_unmap_single(dai->dev, io->dma, | ||
934 | snd_pcm_lib_buffer_bytes(io->substream), dir); | ||
935 | return 0; | ||
936 | } | ||
937 | |||
938 | static void fsi_dma_complete(void *data) | ||
939 | { | ||
940 | struct fsi_stream *io = (struct fsi_stream *)data; | ||
941 | struct fsi_priv *fsi = fsi_stream_to_priv(io); | ||
942 | struct snd_pcm_runtime *runtime = io->substream->runtime; | ||
943 | struct snd_soc_dai *dai = fsi_get_dai(io->substream); | ||
944 | enum dma_data_direction dir = fsi_stream_is_play(fsi, io) ? | ||
945 | DMA_TO_DEVICE : DMA_FROM_DEVICE; | ||
946 | |||
947 | dma_sync_single_for_cpu(dai->dev, io->dma, | ||
948 | samples_to_bytes(runtime, io->period_samples), dir); | ||
949 | |||
950 | io->buff_sample_pos += io->period_samples; | ||
951 | io->period_pos++; | ||
952 | |||
953 | if (io->period_pos >= runtime->periods) { | ||
954 | io->period_pos = 0; | ||
955 | io->buff_sample_pos = 0; | ||
956 | } | ||
957 | |||
958 | fsi_count_fifo_err(fsi); | ||
959 | fsi_stream_transfer(io); | ||
960 | |||
961 | snd_pcm_period_elapsed(io->substream); | ||
962 | } | ||
963 | |||
964 | static dma_addr_t fsi_dma_get_area(struct fsi_stream *io) | ||
965 | { | ||
966 | struct snd_pcm_runtime *runtime = io->substream->runtime; | ||
967 | |||
968 | return io->dma + samples_to_bytes(runtime, io->buff_sample_pos); | ||
969 | } | ||
970 | |||
971 | static void fsi_dma_do_tasklet(unsigned long data) | ||
972 | { | ||
973 | struct fsi_stream *io = (struct fsi_stream *)data; | ||
974 | struct fsi_priv *fsi = fsi_stream_to_priv(io); | ||
975 | struct dma_chan *chan; | ||
976 | struct snd_soc_dai *dai; | ||
977 | struct dma_async_tx_descriptor *desc; | ||
978 | struct scatterlist sg; | ||
979 | struct snd_pcm_runtime *runtime; | ||
980 | enum dma_data_direction dir; | ||
981 | dma_cookie_t cookie; | ||
982 | int is_play = fsi_stream_is_play(fsi, io); | ||
983 | int len; | ||
984 | dma_addr_t buf; | ||
985 | |||
986 | if (!fsi_stream_is_working(fsi, io)) | ||
987 | return; | ||
988 | |||
989 | dai = fsi_get_dai(io->substream); | ||
990 | chan = io->chan; | ||
991 | runtime = io->substream->runtime; | ||
992 | dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE; | ||
993 | len = samples_to_bytes(runtime, io->period_samples); | ||
994 | buf = fsi_dma_get_area(io); | ||
995 | |||
996 | dma_sync_single_for_device(dai->dev, io->dma, len, dir); | ||
997 | |||
998 | sg_init_table(&sg, 1); | ||
999 | sg_set_page(&sg, pfn_to_page(PFN_DOWN(buf)), | ||
1000 | len , offset_in_page(buf)); | ||
1001 | sg_dma_address(&sg) = buf; | ||
1002 | sg_dma_len(&sg) = len; | ||
1003 | |||
1004 | desc = chan->device->device_prep_slave_sg(chan, &sg, 1, dir, | ||
1005 | DMA_PREP_INTERRUPT | | ||
1006 | DMA_CTRL_ACK); | ||
1007 | if (!desc) { | ||
1008 | dev_err(dai->dev, "device_prep_slave_sg() fail\n"); | ||
1009 | return; | ||
1010 | } | ||
1011 | |||
1012 | desc->callback = fsi_dma_complete; | ||
1013 | desc->callback_param = io; | ||
1014 | |||
1015 | cookie = desc->tx_submit(desc); | ||
1016 | if (cookie < 0) { | ||
1017 | dev_err(dai->dev, "tx_submit() fail\n"); | ||
1018 | return; | ||
1019 | } | ||
1020 | |||
1021 | dma_async_issue_pending(chan); | ||
1022 | |||
1023 | /* | ||
1024 | * FIXME | ||
1025 | * | ||
1026 | * In DMAEngine case, codec and FSI cannot be started simultaneously | ||
1027 | * since FSI is using tasklet. | ||
1028 | * Therefore, in capture case, probably FSI FIFO will have got | ||
1029 | * overflow error in this point. | ||
1030 | * in that case, DMA cannot start transfer until error was cleared. | ||
1031 | */ | ||
1032 | if (!is_play) { | ||
1033 | if (ERR_OVER & fsi_reg_read(fsi, DIFF_ST)) { | ||
1034 | fsi_reg_mask_set(fsi, DIFF_CTL, FIFO_CLR, FIFO_CLR); | ||
1035 | fsi_reg_write(fsi, DIFF_ST, 0); | ||
1036 | } | ||
1037 | } | ||
1038 | } | ||
1039 | |||
1040 | static bool fsi_dma_filter(struct dma_chan *chan, void *param) | ||
1041 | { | ||
1042 | struct sh_dmae_slave *slave = param; | ||
1043 | |||
1044 | chan->private = slave; | ||
1045 | |||
1046 | return true; | ||
1047 | } | ||
1048 | |||
1049 | static int fsi_dma_transfer(struct fsi_priv *fsi, struct fsi_stream *io) | ||
1050 | { | ||
1051 | tasklet_schedule(&io->tasklet); | ||
1052 | |||
1053 | return 0; | ||
1054 | } | ||
1055 | |||
1056 | static void fsi_dma_push_start_stop(struct fsi_priv *fsi, struct fsi_stream *io, | ||
1057 | int start) | ||
1058 | { | ||
1059 | u32 bws; | ||
1060 | u32 dma; | ||
1061 | |||
1062 | switch (io->sample_width * start) { | ||
1063 | case 2: | ||
1064 | bws = CR_BWS_16; | ||
1065 | dma = VDMD_STREAM | DMA_ON; | ||
1066 | break; | ||
1067 | case 4: | ||
1068 | bws = CR_BWS_24; | ||
1069 | dma = VDMD_BACK | DMA_ON; | ||
1070 | break; | ||
1071 | default: | ||
1072 | bws = 0; | ||
1073 | dma = 0; | ||
1074 | } | ||
1075 | |||
1076 | fsi_reg_mask_set(fsi, DO_FMT, CR_BWS_MASK, bws); | ||
1077 | fsi_reg_write(fsi, OUT_DMAC, dma); | ||
1078 | } | ||
1079 | |||
1080 | static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io) | ||
1081 | { | ||
1082 | dma_cap_mask_t mask; | ||
1083 | |||
1084 | dma_cap_zero(mask); | ||
1085 | dma_cap_set(DMA_SLAVE, mask); | ||
1086 | |||
1087 | io->chan = dma_request_channel(mask, fsi_dma_filter, &io->slave); | ||
1088 | if (!io->chan) | ||
1089 | return -EIO; | ||
1090 | |||
1091 | tasklet_init(&io->tasklet, fsi_dma_do_tasklet, (unsigned long)io); | ||
1092 | |||
1093 | return 0; | ||
1094 | } | ||
1095 | |||
1096 | static int fsi_dma_remove(struct fsi_priv *fsi, struct fsi_stream *io) | ||
1097 | { | ||
1098 | tasklet_kill(&io->tasklet); | ||
1099 | |||
1100 | fsi_stream_stop(fsi, io); | ||
1101 | |||
1102 | if (io->chan) | ||
1103 | dma_release_channel(io->chan); | ||
1104 | |||
1105 | io->chan = NULL; | ||
1106 | return 0; | ||
1107 | } | ||
1108 | |||
1109 | static struct fsi_stream_handler fsi_dma_push_handler = { | ||
1110 | .init = fsi_dma_init, | ||
1111 | .quit = fsi_dma_quit, | ||
1112 | .probe = fsi_dma_probe, | ||
1113 | .transfer = fsi_dma_transfer, | ||
1114 | .remove = fsi_dma_remove, | ||
1115 | .start_stop = fsi_dma_push_start_stop, | ||
1116 | }; | ||
1117 | |||
1118 | /* | ||
892 | * dai ops | 1119 | * dai ops |
893 | */ | 1120 | */ |
894 | static void fsi_fifo_init(struct fsi_priv *fsi, | 1121 | static void fsi_fifo_init(struct fsi_priv *fsi, |
@@ -1304,6 +1531,11 @@ static void fsi_handler_init(struct fsi_priv *fsi) | |||
1304 | fsi->playback.priv = fsi; | 1531 | fsi->playback.priv = fsi; |
1305 | fsi->capture.handler = &fsi_pio_pop_handler; /* default PIO */ | 1532 | fsi->capture.handler = &fsi_pio_pop_handler; /* default PIO */ |
1306 | fsi->capture.priv = fsi; | 1533 | fsi->capture.priv = fsi; |
1534 | |||
1535 | if (fsi->info->tx_id) { | ||
1536 | fsi->playback.slave.slave_id = fsi->info->tx_id; | ||
1537 | fsi->playback.handler = &fsi_dma_push_handler; | ||
1538 | } | ||
1307 | } | 1539 | } |
1308 | 1540 | ||
1309 | static int fsi_probe(struct platform_device *pdev) | 1541 | static int fsi_probe(struct platform_device *pdev) |