diff options
Diffstat (limited to 'drivers/dma/dmaengine.c')
-rw-r--r-- | drivers/dma/dmaengine.c | 35 |
1 files changed, 28 insertions, 7 deletions
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 97b329e76798..dc003a3a787d 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c | |||
@@ -169,12 +169,18 @@ static void dma_client_chan_alloc(struct dma_client *client) | |||
169 | enum dma_state_client ack; | 169 | enum dma_state_client ack; |
170 | 170 | ||
171 | /* Find a channel */ | 171 | /* Find a channel */ |
172 | list_for_each_entry(device, &dma_device_list, global_node) | 172 | list_for_each_entry(device, &dma_device_list, global_node) { |
173 | /* Does the client require a specific DMA controller? */ | ||
174 | if (client->slave && client->slave->dma_dev | ||
175 | && client->slave->dma_dev != device->dev) | ||
176 | continue; | ||
177 | |||
173 | list_for_each_entry(chan, &device->channels, device_node) { | 178 | list_for_each_entry(chan, &device->channels, device_node) { |
174 | if (!dma_chan_satisfies_mask(chan, client->cap_mask)) | 179 | if (!dma_chan_satisfies_mask(chan, client->cap_mask)) |
175 | continue; | 180 | continue; |
176 | 181 | ||
177 | desc = chan->device->device_alloc_chan_resources(chan); | 182 | desc = chan->device->device_alloc_chan_resources( |
183 | chan, client); | ||
178 | if (desc >= 0) { | 184 | if (desc >= 0) { |
179 | ack = client->event_callback(client, | 185 | ack = client->event_callback(client, |
180 | chan, | 186 | chan, |
@@ -183,12 +189,14 @@ static void dma_client_chan_alloc(struct dma_client *client) | |||
183 | /* we are done once this client rejects | 189 | /* we are done once this client rejects |
184 | * an available resource | 190 | * an available resource |
185 | */ | 191 | */ |
186 | if (ack == DMA_ACK) | 192 | if (ack == DMA_ACK) { |
187 | dma_chan_get(chan); | 193 | dma_chan_get(chan); |
188 | else if (ack == DMA_NAK) | 194 | chan->client_count++; |
195 | } else if (ack == DMA_NAK) | ||
189 | return; | 196 | return; |
190 | } | 197 | } |
191 | } | 198 | } |
199 | } | ||
192 | } | 200 | } |
193 | 201 | ||
194 | enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie) | 202 | enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie) |
@@ -272,8 +280,10 @@ static void dma_clients_notify_removed(struct dma_chan *chan) | |||
272 | /* client was holding resources for this channel so | 280 | /* client was holding resources for this channel so |
273 | * free it | 281 | * free it |
274 | */ | 282 | */ |
275 | if (ack == DMA_ACK) | 283 | if (ack == DMA_ACK) { |
276 | dma_chan_put(chan); | 284 | dma_chan_put(chan); |
285 | chan->client_count--; | ||
286 | } | ||
277 | } | 287 | } |
278 | 288 | ||
279 | mutex_unlock(&dma_list_mutex); | 289 | mutex_unlock(&dma_list_mutex); |
@@ -285,6 +295,10 @@ static void dma_clients_notify_removed(struct dma_chan *chan) | |||
285 | */ | 295 | */ |
286 | void dma_async_client_register(struct dma_client *client) | 296 | void dma_async_client_register(struct dma_client *client) |
287 | { | 297 | { |
298 | /* validate client data */ | ||
299 | BUG_ON(dma_has_cap(DMA_SLAVE, client->cap_mask) && | ||
300 | !client->slave); | ||
301 | |||
288 | mutex_lock(&dma_list_mutex); | 302 | mutex_lock(&dma_list_mutex); |
289 | list_add_tail(&client->global_node, &dma_client_list); | 303 | list_add_tail(&client->global_node, &dma_client_list); |
290 | mutex_unlock(&dma_list_mutex); | 304 | mutex_unlock(&dma_list_mutex); |
@@ -313,8 +327,10 @@ void dma_async_client_unregister(struct dma_client *client) | |||
313 | ack = client->event_callback(client, chan, | 327 | ack = client->event_callback(client, chan, |
314 | DMA_RESOURCE_REMOVED); | 328 | DMA_RESOURCE_REMOVED); |
315 | 329 | ||
316 | if (ack == DMA_ACK) | 330 | if (ack == DMA_ACK) { |
317 | dma_chan_put(chan); | 331 | dma_chan_put(chan); |
332 | chan->client_count--; | ||
333 | } | ||
318 | } | 334 | } |
319 | 335 | ||
320 | list_del(&client->global_node); | 336 | list_del(&client->global_node); |
@@ -359,6 +375,10 @@ int dma_async_device_register(struct dma_device *device) | |||
359 | !device->device_prep_dma_memset); | 375 | !device->device_prep_dma_memset); |
360 | BUG_ON(dma_has_cap(DMA_INTERRUPT, device->cap_mask) && | 376 | BUG_ON(dma_has_cap(DMA_INTERRUPT, device->cap_mask) && |
361 | !device->device_prep_dma_interrupt); | 377 | !device->device_prep_dma_interrupt); |
378 | BUG_ON(dma_has_cap(DMA_SLAVE, device->cap_mask) && | ||
379 | !device->device_prep_slave_sg); | ||
380 | BUG_ON(dma_has_cap(DMA_SLAVE, device->cap_mask) && | ||
381 | !device->device_terminate_all); | ||
362 | 382 | ||
363 | BUG_ON(!device->device_alloc_chan_resources); | 383 | BUG_ON(!device->device_alloc_chan_resources); |
364 | BUG_ON(!device->device_free_chan_resources); | 384 | BUG_ON(!device->device_free_chan_resources); |
@@ -378,7 +398,7 @@ int dma_async_device_register(struct dma_device *device) | |||
378 | 398 | ||
379 | chan->chan_id = chancnt++; | 399 | chan->chan_id = chancnt++; |
380 | chan->dev.class = &dma_devclass; | 400 | chan->dev.class = &dma_devclass; |
381 | chan->dev.parent = NULL; | 401 | chan->dev.parent = device->dev; |
382 | snprintf(chan->dev.bus_id, BUS_ID_SIZE, "dma%dchan%d", | 402 | snprintf(chan->dev.bus_id, BUS_ID_SIZE, "dma%dchan%d", |
383 | device->dev_id, chan->chan_id); | 403 | device->dev_id, chan->chan_id); |
384 | 404 | ||
@@ -394,6 +414,7 @@ int dma_async_device_register(struct dma_device *device) | |||
394 | kref_get(&device->refcount); | 414 | kref_get(&device->refcount); |
395 | kref_get(&device->refcount); | 415 | kref_get(&device->refcount); |
396 | kref_init(&chan->refcount); | 416 | kref_init(&chan->refcount); |
417 | chan->client_count = 0; | ||
397 | chan->slow_ref = 0; | 418 | chan->slow_ref = 0; |
398 | INIT_RCU_HEAD(&chan->rcu); | 419 | INIT_RCU_HEAD(&chan->rcu); |
399 | } | 420 | } |