diff options
author | Mark Brown <broonie@linaro.org> | 2014-01-02 08:01:52 -0500 |
---|---|---|
committer | Mark Brown <broonie@linaro.org> | 2014-01-02 08:01:52 -0500 |
commit | 75aac8206006e70859930d356ccfe02543530c27 (patch) | |
tree | 3d7581b0dd992da0661a1eea81737d427ac81eb4 | |
parent | 01ad154ea505aeda249a4db4140a276f3eae509b (diff) | |
parent | 2b67f8ba41ac7acf01c8d5c742c713ead8b589cd (diff) |
Merge remote-tracking branch 'asoc/topic/dma' into asoc-next
-rw-r--r-- | drivers/dma/dmaengine.c | 35 | ||||
-rw-r--r-- | drivers/dma/of-dma.c | 15 | ||||
-rw-r--r-- | include/linux/dmaengine.h | 8 | ||||
-rw-r--r-- | include/sound/dmaengine_pcm.h | 10 | ||||
-rw-r--r-- | sound/soc/soc-devres.c | 41 | ||||
-rw-r--r-- | sound/soc/soc-generic-dmaengine-pcm.c | 56 |
6 files changed, 144 insertions, 21 deletions
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index ef63b9058f3c..92caad629d99 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c | |||
@@ -540,6 +540,8 @@ EXPORT_SYMBOL_GPL(dma_get_slave_channel); | |||
540 | * @mask: capabilities that the channel must satisfy | 540 | * @mask: capabilities that the channel must satisfy |
541 | * @fn: optional callback to disposition available channels | 541 | * @fn: optional callback to disposition available channels |
542 | * @fn_param: opaque parameter to pass to dma_filter_fn | 542 | * @fn_param: opaque parameter to pass to dma_filter_fn |
543 | * | ||
544 | * Returns pointer to appropriate DMA channel on success or NULL. | ||
543 | */ | 545 | */ |
544 | struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask, | 546 | struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask, |
545 | dma_filter_fn fn, void *fn_param) | 547 | dma_filter_fn fn, void *fn_param) |
@@ -591,18 +593,43 @@ EXPORT_SYMBOL_GPL(__dma_request_channel); | |||
591 | * dma_request_slave_channel - try to allocate an exclusive slave channel | 593 | * dma_request_slave_channel - try to allocate an exclusive slave channel |
592 | * @dev: pointer to client device structure | 594 | * @dev: pointer to client device structure |
593 | * @name: slave channel name | 595 | * @name: slave channel name |
596 | * | ||
597 | * Returns pointer to appropriate DMA channel on success or an error pointer. | ||
594 | */ | 598 | */ |
595 | struct dma_chan *dma_request_slave_channel(struct device *dev, const char *name) | 599 | struct dma_chan *dma_request_slave_channel_reason(struct device *dev, |
600 | const char *name) | ||
596 | { | 601 | { |
602 | struct dma_chan *chan; | ||
603 | |||
597 | /* If device-tree is present get slave info from here */ | 604 | /* If device-tree is present get slave info from here */ |
598 | if (dev->of_node) | 605 | if (dev->of_node) |
599 | return of_dma_request_slave_channel(dev->of_node, name); | 606 | return of_dma_request_slave_channel(dev->of_node, name); |
600 | 607 | ||
601 | /* If device was enumerated by ACPI get slave info from here */ | 608 | /* If device was enumerated by ACPI get slave info from here */ |
602 | if (ACPI_HANDLE(dev)) | 609 | if (ACPI_HANDLE(dev)) { |
603 | return acpi_dma_request_slave_chan_by_name(dev, name); | 610 | chan = acpi_dma_request_slave_chan_by_name(dev, name); |
611 | if (chan) | ||
612 | return chan; | ||
613 | } | ||
604 | 614 | ||
605 | return NULL; | 615 | return ERR_PTR(-ENODEV); |
616 | } | ||
617 | EXPORT_SYMBOL_GPL(dma_request_slave_channel_reason); | ||
618 | |||
619 | /** | ||
620 | * dma_request_slave_channel - try to allocate an exclusive slave channel | ||
621 | * @dev: pointer to client device structure | ||
622 | * @name: slave channel name | ||
623 | * | ||
624 | * Returns pointer to appropriate DMA channel on success or NULL. | ||
625 | */ | ||
626 | struct dma_chan *dma_request_slave_channel(struct device *dev, | ||
627 | const char *name) | ||
628 | { | ||
629 | struct dma_chan *ch = dma_request_slave_channel_reason(dev, name); | ||
630 | if (IS_ERR(ch)) | ||
631 | return NULL; | ||
632 | return ch; | ||
606 | } | 633 | } |
607 | EXPORT_SYMBOL_GPL(dma_request_slave_channel); | 634 | EXPORT_SYMBOL_GPL(dma_request_slave_channel); |
608 | 635 | ||
diff --git a/drivers/dma/of-dma.c b/drivers/dma/of-dma.c index 0b88dd3d05f4..e8fe9dc455f4 100644 --- a/drivers/dma/of-dma.c +++ b/drivers/dma/of-dma.c | |||
@@ -143,7 +143,7 @@ static int of_dma_match_channel(struct device_node *np, const char *name, | |||
143 | * @np: device node to get DMA request from | 143 | * @np: device node to get DMA request from |
144 | * @name: name of desired channel | 144 | * @name: name of desired channel |
145 | * | 145 | * |
146 | * Returns pointer to appropriate dma channel on success or NULL on error. | 146 | * Returns pointer to appropriate DMA channel on success or an error pointer. |
147 | */ | 147 | */ |
148 | struct dma_chan *of_dma_request_slave_channel(struct device_node *np, | 148 | struct dma_chan *of_dma_request_slave_channel(struct device_node *np, |
149 | const char *name) | 149 | const char *name) |
@@ -152,17 +152,18 @@ struct dma_chan *of_dma_request_slave_channel(struct device_node *np, | |||
152 | struct of_dma *ofdma; | 152 | struct of_dma *ofdma; |
153 | struct dma_chan *chan; | 153 | struct dma_chan *chan; |
154 | int count, i; | 154 | int count, i; |
155 | int ret_no_channel = -ENODEV; | ||
155 | 156 | ||
156 | if (!np || !name) { | 157 | if (!np || !name) { |
157 | pr_err("%s: not enough information provided\n", __func__); | 158 | pr_err("%s: not enough information provided\n", __func__); |
158 | return NULL; | 159 | return ERR_PTR(-ENODEV); |
159 | } | 160 | } |
160 | 161 | ||
161 | count = of_property_count_strings(np, "dma-names"); | 162 | count = of_property_count_strings(np, "dma-names"); |
162 | if (count < 0) { | 163 | if (count < 0) { |
163 | pr_err("%s: dma-names property of node '%s' missing or empty\n", | 164 | pr_err("%s: dma-names property of node '%s' missing or empty\n", |
164 | __func__, np->full_name); | 165 | __func__, np->full_name); |
165 | return NULL; | 166 | return ERR_PTR(-ENODEV); |
166 | } | 167 | } |
167 | 168 | ||
168 | for (i = 0; i < count; i++) { | 169 | for (i = 0; i < count; i++) { |
@@ -172,10 +173,12 @@ struct dma_chan *of_dma_request_slave_channel(struct device_node *np, | |||
172 | mutex_lock(&of_dma_lock); | 173 | mutex_lock(&of_dma_lock); |
173 | ofdma = of_dma_find_controller(&dma_spec); | 174 | ofdma = of_dma_find_controller(&dma_spec); |
174 | 175 | ||
175 | if (ofdma) | 176 | if (ofdma) { |
176 | chan = ofdma->of_dma_xlate(&dma_spec, ofdma); | 177 | chan = ofdma->of_dma_xlate(&dma_spec, ofdma); |
177 | else | 178 | } else { |
179 | ret_no_channel = -EPROBE_DEFER; | ||
178 | chan = NULL; | 180 | chan = NULL; |
181 | } | ||
179 | 182 | ||
180 | mutex_unlock(&of_dma_lock); | 183 | mutex_unlock(&of_dma_lock); |
181 | 184 | ||
@@ -185,7 +188,7 @@ struct dma_chan *of_dma_request_slave_channel(struct device_node *np, | |||
185 | return chan; | 188 | return chan; |
186 | } | 189 | } |
187 | 190 | ||
188 | return NULL; | 191 | return ERR_PTR(ret_no_channel); |
189 | } | 192 | } |
190 | 193 | ||
191 | /** | 194 | /** |
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 41cf0c399288..ed92b30a02fd 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h | |||
@@ -22,6 +22,7 @@ | |||
22 | #define LINUX_DMAENGINE_H | 22 | #define LINUX_DMAENGINE_H |
23 | 23 | ||
24 | #include <linux/device.h> | 24 | #include <linux/device.h> |
25 | #include <linux/err.h> | ||
25 | #include <linux/uio.h> | 26 | #include <linux/uio.h> |
26 | #include <linux/bug.h> | 27 | #include <linux/bug.h> |
27 | #include <linux/scatterlist.h> | 28 | #include <linux/scatterlist.h> |
@@ -1040,6 +1041,8 @@ enum dma_status dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx); | |||
1040 | void dma_issue_pending_all(void); | 1041 | void dma_issue_pending_all(void); |
1041 | struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask, | 1042 | struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask, |
1042 | dma_filter_fn fn, void *fn_param); | 1043 | dma_filter_fn fn, void *fn_param); |
1044 | struct dma_chan *dma_request_slave_channel_reason(struct device *dev, | ||
1045 | const char *name); | ||
1043 | struct dma_chan *dma_request_slave_channel(struct device *dev, const char *name); | 1046 | struct dma_chan *dma_request_slave_channel(struct device *dev, const char *name); |
1044 | void dma_release_channel(struct dma_chan *chan); | 1047 | void dma_release_channel(struct dma_chan *chan); |
1045 | #else | 1048 | #else |
@@ -1063,6 +1066,11 @@ static inline struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask, | |||
1063 | { | 1066 | { |
1064 | return NULL; | 1067 | return NULL; |
1065 | } | 1068 | } |
1069 | static inline struct dma_chan *dma_request_slave_channel_reason( | ||
1070 | struct device *dev, const char *name) | ||
1071 | { | ||
1072 | return ERR_PTR(-ENODEV); | ||
1073 | } | ||
1066 | static inline struct dma_chan *dma_request_slave_channel(struct device *dev, | 1074 | static inline struct dma_chan *dma_request_slave_channel(struct device *dev, |
1067 | const char *name) | 1075 | const char *name) |
1068 | { | 1076 | { |
diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index 15017311f2e9..eb73a3a39ec2 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h | |||
@@ -114,6 +114,10 @@ void snd_dmaengine_pcm_set_config_from_dai_data( | |||
114 | * @compat_filter_fn: Will be used as the filter function when requesting a | 114 | * @compat_filter_fn: Will be used as the filter function when requesting a |
115 | * channel for platforms which do not use devicetree. The filter parameter | 115 | * channel for platforms which do not use devicetree. The filter parameter |
116 | * will be the DAI's DMA data. | 116 | * will be the DAI's DMA data. |
117 | * @dma_dev: If set, request DMA channel on this device rather than the DAI | ||
118 | * device. | ||
119 | * @chan_names: If set, these custom DMA channel names will be requested at | ||
120 | * registration time. | ||
117 | * @pcm_hardware: snd_pcm_hardware struct to be used for the PCM. | 121 | * @pcm_hardware: snd_pcm_hardware struct to be used for the PCM. |
118 | * @prealloc_buffer_size: Size of the preallocated audio buffer. | 122 | * @prealloc_buffer_size: Size of the preallocated audio buffer. |
119 | * | 123 | * |
@@ -130,6 +134,8 @@ struct snd_dmaengine_pcm_config { | |||
130 | struct snd_soc_pcm_runtime *rtd, | 134 | struct snd_soc_pcm_runtime *rtd, |
131 | struct snd_pcm_substream *substream); | 135 | struct snd_pcm_substream *substream); |
132 | dma_filter_fn compat_filter_fn; | 136 | dma_filter_fn compat_filter_fn; |
137 | struct device *dma_dev; | ||
138 | const char *chan_names[SNDRV_PCM_STREAM_LAST + 1]; | ||
133 | 139 | ||
134 | const struct snd_pcm_hardware *pcm_hardware; | 140 | const struct snd_pcm_hardware *pcm_hardware; |
135 | unsigned int prealloc_buffer_size; | 141 | unsigned int prealloc_buffer_size; |
@@ -140,6 +146,10 @@ int snd_dmaengine_pcm_register(struct device *dev, | |||
140 | unsigned int flags); | 146 | unsigned int flags); |
141 | void snd_dmaengine_pcm_unregister(struct device *dev); | 147 | void snd_dmaengine_pcm_unregister(struct device *dev); |
142 | 148 | ||
149 | int devm_snd_dmaengine_pcm_register(struct device *dev, | ||
150 | const struct snd_dmaengine_pcm_config *config, | ||
151 | unsigned int flags); | ||
152 | |||
143 | int snd_dmaengine_pcm_prepare_slave_config(struct snd_pcm_substream *substream, | 153 | int snd_dmaengine_pcm_prepare_slave_config(struct snd_pcm_substream *substream, |
144 | struct snd_pcm_hw_params *params, | 154 | struct snd_pcm_hw_params *params, |
145 | struct dma_slave_config *slave_config); | 155 | struct dma_slave_config *slave_config); |
diff --git a/sound/soc/soc-devres.c b/sound/soc/soc-devres.c index 3449c1e909ae..7ac745df1412 100644 --- a/sound/soc/soc-devres.c +++ b/sound/soc/soc-devres.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include <linux/module.h> | 12 | #include <linux/module.h> |
13 | #include <linux/moduleparam.h> | 13 | #include <linux/moduleparam.h> |
14 | #include <sound/soc.h> | 14 | #include <sound/soc.h> |
15 | #include <sound/dmaengine_pcm.h> | ||
15 | 16 | ||
16 | static void devm_component_release(struct device *dev, void *res) | 17 | static void devm_component_release(struct device *dev, void *res) |
17 | { | 18 | { |
@@ -84,3 +85,43 @@ int devm_snd_soc_register_card(struct device *dev, struct snd_soc_card *card) | |||
84 | return ret; | 85 | return ret; |
85 | } | 86 | } |
86 | EXPORT_SYMBOL_GPL(devm_snd_soc_register_card); | 87 | EXPORT_SYMBOL_GPL(devm_snd_soc_register_card); |
88 | |||
89 | #ifdef CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM | ||
90 | |||
91 | static void devm_dmaengine_pcm_release(struct device *dev, void *res) | ||
92 | { | ||
93 | snd_dmaengine_pcm_unregister(*(struct device **)res); | ||
94 | } | ||
95 | |||
96 | /** | ||
97 | * devm_snd_dmaengine_pcm_register - resource managed dmaengine PCM registration | ||
98 | * @dev: The parent device for the PCM device | ||
99 | * @config: Platform specific PCM configuration | ||
100 | * @flags: Platform specific quirks | ||
101 | * | ||
102 | * Register a dmaengine based PCM device with automatic unregistration when the | ||
103 | * device is unregistered. | ||
104 | */ | ||
105 | int devm_snd_dmaengine_pcm_register(struct device *dev, | ||
106 | const struct snd_dmaengine_pcm_config *config, unsigned int flags) | ||
107 | { | ||
108 | struct device **ptr; | ||
109 | int ret; | ||
110 | |||
111 | ptr = devres_alloc(devm_dmaengine_pcm_release, sizeof(*ptr), GFP_KERNEL); | ||
112 | if (!ptr) | ||
113 | return -ENOMEM; | ||
114 | |||
115 | ret = snd_dmaengine_pcm_register(dev, config, flags); | ||
116 | if (ret == 0) { | ||
117 | *ptr = dev; | ||
118 | devres_add(dev, ptr); | ||
119 | } else { | ||
120 | devres_free(ptr); | ||
121 | } | ||
122 | |||
123 | return ret; | ||
124 | } | ||
125 | EXPORT_SYMBOL_GPL(devm_snd_dmaengine_pcm_register); | ||
126 | |||
127 | #endif | ||
diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index 41949af3baae..2a6c569d991f 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c | |||
@@ -137,6 +137,9 @@ static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substrea | |||
137 | hw.buffer_bytes_max = SIZE_MAX; | 137 | hw.buffer_bytes_max = SIZE_MAX; |
138 | hw.fifo_size = dma_data->fifo_size; | 138 | hw.fifo_size = dma_data->fifo_size; |
139 | 139 | ||
140 | if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE) | ||
141 | hw.info |= SNDRV_PCM_INFO_BATCH; | ||
142 | |||
140 | ret = dma_get_slave_caps(chan, &dma_caps); | 143 | ret = dma_get_slave_caps(chan, &dma_caps); |
141 | if (ret == 0) { | 144 | if (ret == 0) { |
142 | if (dma_caps.cmd_pause) | 145 | if (dma_caps.cmd_pause) |
@@ -284,25 +287,54 @@ static const char * const dmaengine_pcm_dma_channel_names[] = { | |||
284 | [SNDRV_PCM_STREAM_CAPTURE] = "rx", | 287 | [SNDRV_PCM_STREAM_CAPTURE] = "rx", |
285 | }; | 288 | }; |
286 | 289 | ||
287 | static void dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, | 290 | static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, |
288 | struct device *dev) | 291 | struct device *dev, const struct snd_dmaengine_pcm_config *config) |
289 | { | 292 | { |
290 | unsigned int i; | 293 | unsigned int i; |
294 | const char *name; | ||
295 | struct dma_chan *chan; | ||
291 | 296 | ||
292 | if ((pcm->flags & (SND_DMAENGINE_PCM_FLAG_NO_DT | | 297 | if ((pcm->flags & (SND_DMAENGINE_PCM_FLAG_NO_DT | |
293 | SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME)) || | 298 | SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME)) || |
294 | !dev->of_node) | 299 | !dev->of_node) |
295 | return; | 300 | return 0; |
301 | |||
302 | if (config && config->dma_dev) { | ||
303 | /* | ||
304 | * If this warning is seen, it probably means that your Linux | ||
305 | * device structure does not match your HW device structure. | ||
306 | * It would be best to refactor the Linux device structure to | ||
307 | * correctly match the HW structure. | ||
308 | */ | ||
309 | dev_warn(dev, "DMA channels sourced from device %s", | ||
310 | dev_name(config->dma_dev)); | ||
311 | dev = config->dma_dev; | ||
312 | } | ||
296 | 313 | ||
297 | if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) { | 314 | for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; |
298 | pcm->chan[0] = dma_request_slave_channel(dev, "rx-tx"); | 315 | i++) { |
299 | pcm->chan[1] = pcm->chan[0]; | 316 | if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) |
300 | } else { | 317 | name = "rx-tx"; |
301 | for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) { | 318 | else |
302 | pcm->chan[i] = dma_request_slave_channel(dev, | 319 | name = dmaengine_pcm_dma_channel_names[i]; |
303 | dmaengine_pcm_dma_channel_names[i]); | 320 | if (config && config->chan_names[i]) |
321 | name = config->chan_names[i]; | ||
322 | chan = dma_request_slave_channel_reason(dev, name); | ||
323 | if (IS_ERR(chan)) { | ||
324 | if (PTR_ERR(chan) == -EPROBE_DEFER) | ||
325 | return -EPROBE_DEFER; | ||
326 | pcm->chan[i] = NULL; | ||
327 | } else { | ||
328 | pcm->chan[i] = chan; | ||
304 | } | 329 | } |
330 | if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) | ||
331 | break; | ||
305 | } | 332 | } |
333 | |||
334 | if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) | ||
335 | pcm->chan[1] = pcm->chan[0]; | ||
336 | |||
337 | return 0; | ||
306 | } | 338 | } |
307 | 339 | ||
308 | static void dmaengine_pcm_release_chan(struct dmaengine_pcm *pcm) | 340 | static void dmaengine_pcm_release_chan(struct dmaengine_pcm *pcm) |
@@ -338,7 +370,9 @@ int snd_dmaengine_pcm_register(struct device *dev, | |||
338 | pcm->config = config; | 370 | pcm->config = config; |
339 | pcm->flags = flags; | 371 | pcm->flags = flags; |
340 | 372 | ||
341 | dmaengine_pcm_request_chan_of(pcm, dev); | 373 | ret = dmaengine_pcm_request_chan_of(pcm, dev, config); |
374 | if (ret) | ||
375 | goto err_free_dma; | ||
342 | 376 | ||
343 | if (flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE) | 377 | if (flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE) |
344 | ret = snd_soc_add_platform(dev, &pcm->platform, | 378 | ret = snd_soc_add_platform(dev, &pcm->platform, |