Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 1 | /* |
Gopikrishnaiah Anandan | 11e633b | 2014-01-20 16:01:24 -0800 | [diff] [blame] | 2 | * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 3 | * |
| 4 | * This program is free software; you can redistribute it and/or modify |
| 5 | * it under the terms of the GNU General Public License version 2 and |
| 6 | * only version 2 as published by the Free Software Foundation. |
| 7 | * |
| 8 | * This program is distributed in the hope that it will be useful, |
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 11 | * GNU General Public License for more details. |
| 12 | */ |
| 13 | |
| 14 | #include <linux/init.h> |
| 15 | #include <linux/kernel.h> |
| 16 | #include <linux/module.h> |
| 17 | #include <linux/err.h> |
| 18 | #include <linux/delay.h> |
| 19 | #include <linux/platform_device.h> |
| 20 | #include <mach/subsystem_restart.h> |
| 21 | #include <mach/qdsp6v2/apr.h> |
| 22 | #include <linux/of_device.h> |
| 23 | #include <linux/msm_audio_ion.h> |
| 24 | |
Fred Oh | c274a7a | 2013-03-25 13:59:17 -0700 | [diff] [blame] | 25 | #include <linux/iommu.h> |
| 26 | #include <mach/iommu_domains.h> |
| 27 | |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 28 | struct msm_audio_ion_private { |
| 29 | bool smmu_enabled; |
Fred Oh | c274a7a | 2013-03-25 13:59:17 -0700 | [diff] [blame] | 30 | bool audioheap_enabled; |
| 31 | struct iommu_group *group; |
| 32 | u32 domain_id; |
| 33 | struct iommu_domain *domain; |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 34 | }; |
| 35 | |
| 36 | static struct msm_audio_ion_private msm_audio_ion_data = {0,}; |
| 37 | |
| 38 | |
| 39 | static int msm_audio_ion_get_phys(struct ion_client *client, |
| 40 | struct ion_handle *handle, |
| 41 | ion_phys_addr_t *addr, size_t *len); |
| 42 | |
| 43 | |
| 44 | |
| 45 | int msm_audio_ion_alloc(const char *name, struct ion_client **client, |
| 46 | struct ion_handle **handle, size_t bufsz, |
| 47 | ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr) |
| 48 | { |
| 49 | int rc = 0; |
| 50 | |
Fred Oh | 7d0ed03 | 2013-06-21 10:34:12 -0700 | [diff] [blame] | 51 | if ((msm_audio_ion_data.smmu_enabled == true) && |
| 52 | (msm_audio_ion_data.group == NULL)) { |
| 53 | pr_debug("%s:probe is not done, deferred\n", __func__); |
| 54 | return -EPROBE_DEFER; |
| 55 | } |
Gopikrishnaiah Anandan | 6eb6d18 | 2013-08-16 17:34:21 -0400 | [diff] [blame] | 56 | if (!name || !client || !handle || !paddr || !vaddr |
| 57 | || !bufsz || !pa_len) { |
| 58 | pr_err("%s: Invalid params\n", __func__); |
| 59 | return -EINVAL; |
| 60 | } |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 61 | *client = msm_audio_ion_client_create(UINT_MAX, name); |
| 62 | if (IS_ERR_OR_NULL((void *)(*client))) { |
| 63 | pr_err("%s: ION create client for AUDIO failed\n", __func__); |
| 64 | goto err; |
| 65 | } |
| 66 | |
Fred Oh | c274a7a | 2013-03-25 13:59:17 -0700 | [diff] [blame] | 67 | *handle = ion_alloc(*client, bufsz, SZ_4K, |
| 68 | ION_HEAP(ION_AUDIO_HEAP_ID), 0); |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 69 | if (IS_ERR_OR_NULL((void *) (*handle))) { |
Fred Oh | c274a7a | 2013-03-25 13:59:17 -0700 | [diff] [blame] | 70 | pr_debug("system heap is used"); |
| 71 | msm_audio_ion_data.audioheap_enabled = 0; |
| 72 | *handle = ion_alloc(*client, bufsz, SZ_4K, |
| 73 | ION_HEAP(ION_SYSTEM_HEAP_ID), 0); |
| 74 | |
| 75 | } else { |
| 76 | pr_debug("audio heap is used"); |
| 77 | msm_audio_ion_data.audioheap_enabled = 1; |
| 78 | } |
| 79 | |
| 80 | if (IS_ERR_OR_NULL((void *) (*handle))) { |
| 81 | pr_err("%s: ION memory allocation for AUDIO failed rc=%d, smmu_enabled=%d\n", |
| 82 | __func__, rc, msm_audio_ion_data.smmu_enabled); |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 83 | goto err_ion_client; |
| 84 | } |
| 85 | |
| 86 | rc = msm_audio_ion_get_phys(*client, *handle, paddr, pa_len); |
| 87 | if (rc) { |
| 88 | pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n", |
| 89 | __func__, rc); |
| 90 | goto err_ion_handle; |
| 91 | } |
| 92 | |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 93 | *vaddr = ion_map_kernel(*client, *handle); |
| 94 | if (IS_ERR_OR_NULL((void *)*vaddr)) { |
| 95 | pr_err("%s: ION memory mapping for AUDIO failed\n", __func__); |
| 96 | goto err_ion_handle; |
| 97 | } |
Fred Oh | c274a7a | 2013-03-25 13:59:17 -0700 | [diff] [blame] | 98 | pr_debug("%s: mapped address = %p, size=%d\n", __func__, *vaddr, bufsz); |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 99 | |
Fred Oh | c274a7a | 2013-03-25 13:59:17 -0700 | [diff] [blame] | 100 | if (bufsz != 0) { |
| 101 | pr_debug("%s: memset to 0 %p %d\n", __func__, *vaddr, bufsz); |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 102 | memset((void *)*vaddr, 0, bufsz); |
Fred Oh | c274a7a | 2013-03-25 13:59:17 -0700 | [diff] [blame] | 103 | } |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 104 | |
| 105 | return 0; |
| 106 | |
| 107 | err_ion_handle: |
| 108 | ion_free(*client, *handle); |
| 109 | err_ion_client: |
| 110 | msm_audio_ion_client_destroy(*client); |
Gopikrishnaiah Anandan | 6eb6d18 | 2013-08-16 17:34:21 -0400 | [diff] [blame] | 111 | *handle = NULL; |
Fred Oh | 7d0ed03 | 2013-06-21 10:34:12 -0700 | [diff] [blame] | 112 | *client = NULL; |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 113 | err: |
| 114 | return -EINVAL; |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 115 | } |
| 116 | |
| 117 | int msm_audio_ion_import(const char *name, struct ion_client **client, |
| 118 | struct ion_handle **handle, int fd, |
| 119 | unsigned long *ionflag, size_t bufsz, |
| 120 | ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr) |
| 121 | { |
| 122 | int rc = 0; |
Gopikrishnaiah Anandan | 6eb6d18 | 2013-08-16 17:34:21 -0400 | [diff] [blame] | 123 | if (!name || !client || !handle || !paddr || !vaddr || !pa_len) { |
| 124 | pr_err("%s: Invalid params\n", __func__); |
| 125 | rc = -EINVAL; |
| 126 | goto err; |
| 127 | } |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 128 | |
| 129 | *client = msm_audio_ion_client_create(UINT_MAX, name); |
| 130 | if (IS_ERR_OR_NULL((void *)(*client))) { |
| 131 | pr_err("%s: ION create client for AUDIO failed\n", __func__); |
Gopikrishnaiah Anandan | 6eb6d18 | 2013-08-16 17:34:21 -0400 | [diff] [blame] | 132 | rc = -EINVAL; |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 133 | goto err; |
| 134 | } |
| 135 | |
| 136 | /* name should be audio_acdb_client or Audio_Dec_Client, |
| 137 | bufsz should be 0 and fd shouldn't be 0 as of now |
| 138 | */ |
| 139 | *handle = ion_import_dma_buf(*client, fd); |
| 140 | pr_err("%s: DMA Buf name=%s, fd=%d handle=%p\n", __func__, |
| 141 | name, fd, *handle); |
| 142 | if (IS_ERR_OR_NULL((void *) (*handle))) { |
| 143 | pr_err("%s: ion import dma buffer failed\n", |
| 144 | __func__); |
Gopikrishnaiah Anandan | 6eb6d18 | 2013-08-16 17:34:21 -0400 | [diff] [blame] | 145 | rc = -EINVAL; |
| 146 | goto err_destroy_client; |
| 147 | } |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 148 | |
| 149 | if (ionflag != NULL) { |
| 150 | rc = ion_handle_get_flags(*client, *handle, ionflag); |
| 151 | if (rc) { |
| 152 | pr_err("%s: could not get flags for the handle\n", |
| 153 | __func__); |
| 154 | goto err_ion_handle; |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | rc = msm_audio_ion_get_phys(*client, *handle, paddr, pa_len); |
| 159 | if (rc) { |
| 160 | pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n", |
| 161 | __func__, rc); |
| 162 | goto err_ion_handle; |
| 163 | } |
| 164 | |
Fred Oh | 1303907 | 2013-06-18 19:11:48 -0700 | [diff] [blame] | 165 | *vaddr = ion_map_kernel(*client, *handle); |
| 166 | if (IS_ERR_OR_NULL((void *)*vaddr)) { |
| 167 | pr_err("%s: ION memory mapping for AUDIO failed\n", __func__); |
Gopikrishnaiah Anandan | 6eb6d18 | 2013-08-16 17:34:21 -0400 | [diff] [blame] | 168 | rc = -ENOMEM; |
Fred Oh | 1303907 | 2013-06-18 19:11:48 -0700 | [diff] [blame] | 169 | goto err_ion_handle; |
| 170 | } |
| 171 | pr_debug("%s: mapped address = %p, size=%d\n", __func__, *vaddr, bufsz); |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 172 | |
| 173 | return 0; |
| 174 | |
| 175 | err_ion_handle: |
| 176 | ion_free(*client, *handle); |
Gopikrishnaiah Anandan | 6eb6d18 | 2013-08-16 17:34:21 -0400 | [diff] [blame] | 177 | err_destroy_client: |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 178 | msm_audio_ion_client_destroy(*client); |
Gopikrishnaiah Anandan | 6eb6d18 | 2013-08-16 17:34:21 -0400 | [diff] [blame] | 179 | *client = NULL; |
| 180 | *handle = NULL; |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 181 | err: |
Gopikrishnaiah Anandan | 6eb6d18 | 2013-08-16 17:34:21 -0400 | [diff] [blame] | 182 | return rc; |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 183 | } |
| 184 | |
| 185 | int msm_audio_ion_free(struct ion_client *client, struct ion_handle *handle) |
| 186 | { |
Gopikrishnaiah Anandan | 6eb6d18 | 2013-08-16 17:34:21 -0400 | [diff] [blame] | 187 | if (!client || !handle) { |
| 188 | pr_err("%s Invalid params\n", __func__); |
| 189 | return -EINVAL; |
| 190 | } |
Fred Oh | c274a7a | 2013-03-25 13:59:17 -0700 | [diff] [blame] | 191 | if (msm_audio_ion_data.smmu_enabled) { |
| 192 | /* Need to populate book kept infomation */ |
| 193 | pr_debug("client=%p, domain=%p, domain_id=%d, group=%p", |
| 194 | client, msm_audio_ion_data.domain, |
| 195 | msm_audio_ion_data.domain_id, msm_audio_ion_data.group); |
| 196 | |
| 197 | ion_unmap_iommu(client, handle, |
| 198 | msm_audio_ion_data.domain_id, 0); |
| 199 | } |
| 200 | |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 201 | ion_unmap_kernel(client, handle); |
| 202 | |
| 203 | ion_free(client, handle); |
| 204 | msm_audio_ion_client_destroy(client); |
| 205 | return 0; |
| 206 | } |
| 207 | |
Fred Oh | c274a7a | 2013-03-25 13:59:17 -0700 | [diff] [blame] | 208 | int msm_audio_ion_mmap(struct audio_buffer *ab, |
| 209 | struct vm_area_struct *vma) |
| 210 | { |
| 211 | struct sg_table *table; |
| 212 | unsigned long addr = vma->vm_start; |
| 213 | unsigned long offset = vma->vm_pgoff * PAGE_SIZE; |
| 214 | struct scatterlist *sg; |
| 215 | unsigned int i; |
| 216 | struct page *page; |
| 217 | int ret; |
| 218 | |
| 219 | pr_debug("%s\n", __func__); |
| 220 | |
| 221 | table = ion_sg_table(ab->client, ab->handle); |
| 222 | |
| 223 | if (IS_ERR(table)) { |
| 224 | pr_err("%s: Unable to get sg_table from ion: %ld\n", |
| 225 | __func__, PTR_ERR(table)); |
| 226 | return PTR_ERR(table); |
| 227 | } else if (!table) { |
| 228 | pr_err("%s: sg_list is NULL\n", __func__); |
| 229 | return -EINVAL; |
| 230 | } |
| 231 | |
| 232 | /* uncached */ |
| 233 | vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); |
| 234 | |
| 235 | /* We need to check if a page is associated with this sg list because: |
| 236 | * If the allocation came from a carveout we currently don't have |
| 237 | * pages associated with carved out memory. This might change in the |
| 238 | * future and we can remove this check and the else statement. |
| 239 | */ |
| 240 | page = sg_page(table->sgl); |
| 241 | if (page) { |
| 242 | pr_debug("%s: page is NOT null\n", __func__); |
| 243 | for_each_sg(table->sgl, sg, table->nents, i) { |
| 244 | unsigned long remainder = vma->vm_end - addr; |
| 245 | unsigned long len = sg_dma_len(sg); |
| 246 | |
| 247 | page = sg_page(sg); |
| 248 | |
| 249 | if (offset >= sg_dma_len(sg)) { |
| 250 | offset -= sg_dma_len(sg); |
| 251 | continue; |
| 252 | } else if (offset) { |
| 253 | page += offset / PAGE_SIZE; |
| 254 | len = sg_dma_len(sg) - offset; |
| 255 | offset = 0; |
| 256 | } |
| 257 | len = min(len, remainder); |
| 258 | pr_debug("vma=%p, addr=%x len=%ld vm_start=%x vm_end=%x vm_page_prot=%ld\n", |
| 259 | vma, (unsigned int)addr, len, |
| 260 | (unsigned int)vma->vm_start, |
| 261 | (unsigned int)vma->vm_end, |
| 262 | (unsigned long int)vma->vm_page_prot); |
| 263 | remap_pfn_range(vma, addr, page_to_pfn(page), len, |
| 264 | vma->vm_page_prot); |
| 265 | addr += len; |
| 266 | if (addr >= vma->vm_end) |
| 267 | return 0; |
| 268 | } |
| 269 | } else { |
| 270 | ion_phys_addr_t phys_addr; |
| 271 | size_t phys_len; |
Fred Oh | 0b5f49b | 2013-10-11 15:07:45 -0700 | [diff] [blame] | 272 | size_t va_len = 0; |
Fred Oh | c274a7a | 2013-03-25 13:59:17 -0700 | [diff] [blame] | 273 | pr_debug("%s: page is NULL\n", __func__); |
| 274 | |
| 275 | ret = ion_phys(ab->client, ab->handle, &phys_addr, &phys_len); |
| 276 | if (ret) { |
| 277 | pr_err("%s: Unable to get phys address from ION buffer: %d\n" |
| 278 | , __func__ , ret); |
| 279 | return ret; |
| 280 | } |
| 281 | pr_debug("phys=%x len=%d\n", (unsigned int)phys_addr, phys_len); |
| 282 | pr_debug("vma=%p, vm_start=%x vm_end=%x vm_pgoff=%ld vm_page_prot=%ld\n", |
| 283 | vma, (unsigned int)vma->vm_start, |
| 284 | (unsigned int)vma->vm_end, vma->vm_pgoff, |
| 285 | (unsigned long int)vma->vm_page_prot); |
Fred Oh | 0b5f49b | 2013-10-11 15:07:45 -0700 | [diff] [blame] | 286 | va_len = vma->vm_end - vma->vm_start; |
| 287 | if ((offset > phys_len) || (va_len > phys_len-offset)) { |
| 288 | pr_err("wrong offset size %ld, lens= %d, va_len=%d\n", |
| 289 | offset, phys_len, va_len); |
| 290 | return -EINVAL; |
| 291 | } |
Fred Oh | c274a7a | 2013-03-25 13:59:17 -0700 | [diff] [blame] | 292 | ret = remap_pfn_range(vma, vma->vm_start, |
| 293 | __phys_to_pfn(phys_addr) + vma->vm_pgoff, |
| 294 | vma->vm_end - vma->vm_start, |
| 295 | vma->vm_page_prot); |
| 296 | } |
| 297 | return 0; |
| 298 | } |
| 299 | |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 300 | |
| 301 | bool msm_audio_ion_is_smmu_available(void) |
| 302 | { |
| 303 | return msm_audio_ion_data.smmu_enabled; |
| 304 | } |
| 305 | |
| 306 | /* move to static section again */ |
| 307 | struct ion_client *msm_audio_ion_client_create(unsigned int heap_mask, |
| 308 | const char *name) |
| 309 | { |
Fred Oh | c274a7a | 2013-03-25 13:59:17 -0700 | [diff] [blame] | 310 | struct ion_client *pclient = NULL; |
| 311 | /*IOMMU group and domain are moved to probe()*/ |
| 312 | pclient = msm_ion_client_create(heap_mask, name); |
| 313 | return pclient; |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 314 | } |
| 315 | |
| 316 | |
| 317 | void msm_audio_ion_client_destroy(struct ion_client *client) |
| 318 | { |
Fred Oh | c274a7a | 2013-03-25 13:59:17 -0700 | [diff] [blame] | 319 | pr_debug("%s: client = %p smmu_enabled = %d\n", __func__, |
| 320 | client, msm_audio_ion_data.smmu_enabled); |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 321 | |
| 322 | ion_client_destroy(client); |
| 323 | } |
| 324 | |
| 325 | int msm_audio_ion_import_legacy(const char *name, struct ion_client *client, |
| 326 | struct ion_handle **handle, int fd, |
| 327 | unsigned long *ionflag, size_t bufsz, |
| 328 | ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr) |
| 329 | { |
| 330 | int rc = 0; |
Gopikrishnaiah Anandan | 61dcd66 | 2013-11-07 20:20:39 -0800 | [diff] [blame] | 331 | if (!name || !client || !handle || !paddr || !vaddr || !pa_len) { |
| 332 | pr_err("%s: Invalid params\n", __func__); |
| 333 | rc = -EINVAL; |
| 334 | goto err; |
| 335 | } |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 336 | /* client is already created for legacy and given*/ |
| 337 | /* name should be audio_acdb_client or Audio_Dec_Client, |
| 338 | bufsz should be 0 and fd shouldn't be 0 as of now |
| 339 | */ |
| 340 | *handle = ion_import_dma_buf(client, fd); |
Fred Oh | c274a7a | 2013-03-25 13:59:17 -0700 | [diff] [blame] | 341 | pr_debug("%s: DMA Buf name=%s, fd=%d handle=%p\n", __func__, |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 342 | name, fd, *handle); |
Fred Oh | c274a7a | 2013-03-25 13:59:17 -0700 | [diff] [blame] | 343 | if (IS_ERR_OR_NULL((void *)(*handle))) { |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 344 | pr_err("%s: ion import dma buffer failed\n", |
| 345 | __func__); |
Gopikrishnaiah Anandan | 61dcd66 | 2013-11-07 20:20:39 -0800 | [diff] [blame] | 346 | rc = -EINVAL; |
Anish Kumar | c0149b8 | 2014-03-10 11:48:07 +0530 | [diff] [blame] | 347 | goto err; |
Gopikrishnaiah Anandan | 61dcd66 | 2013-11-07 20:20:39 -0800 | [diff] [blame] | 348 | } |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 349 | |
| 350 | if (ionflag != NULL) { |
| 351 | rc = ion_handle_get_flags(client, *handle, ionflag); |
| 352 | if (rc) { |
| 353 | pr_err("%s: could not get flags for the handle\n", |
| 354 | __func__); |
Gopikrishnaiah Anandan | 61dcd66 | 2013-11-07 20:20:39 -0800 | [diff] [blame] | 355 | rc = -EINVAL; |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 356 | goto err_ion_handle; |
| 357 | } |
| 358 | } |
| 359 | |
| 360 | rc = msm_audio_ion_get_phys(client, *handle, paddr, pa_len); |
| 361 | if (rc) { |
| 362 | pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n", |
| 363 | __func__, rc); |
Gopikrishnaiah Anandan | 61dcd66 | 2013-11-07 20:20:39 -0800 | [diff] [blame] | 364 | rc = -EINVAL; |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 365 | goto err_ion_handle; |
| 366 | } |
| 367 | |
| 368 | /*Need to add condition SMMU enable or not */ |
| 369 | *vaddr = ion_map_kernel(client, *handle); |
| 370 | if (IS_ERR_OR_NULL((void *)*vaddr)) { |
| 371 | pr_err("%s: ION memory mapping for AUDIO failed\n", __func__); |
Gopikrishnaiah Anandan | 61dcd66 | 2013-11-07 20:20:39 -0800 | [diff] [blame] | 372 | rc = -EINVAL; |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 373 | goto err_ion_handle; |
| 374 | } |
| 375 | |
| 376 | if (bufsz != 0) |
| 377 | memset((void *)*vaddr, 0, bufsz); |
| 378 | |
| 379 | return 0; |
| 380 | |
| 381 | err_ion_handle: |
| 382 | ion_free(client, *handle); |
Gopikrishnaiah Anandan | 61dcd66 | 2013-11-07 20:20:39 -0800 | [diff] [blame] | 383 | err: |
| 384 | return rc; |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 385 | } |
| 386 | |
| 387 | int msm_audio_ion_free_legacy(struct ion_client *client, |
| 388 | struct ion_handle *handle) |
| 389 | { |
Gopikrishnaiah Anandan | 11e633b | 2014-01-20 16:01:24 -0800 | [diff] [blame] | 390 | if (msm_audio_ion_data.smmu_enabled) |
| 391 | ion_unmap_iommu(client, handle, |
| 392 | msm_audio_ion_data.domain_id, 0); |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 393 | ion_unmap_kernel(client, handle); |
| 394 | |
| 395 | ion_free(client, handle); |
| 396 | /* no client_destrody in legacy*/ |
| 397 | return 0; |
| 398 | } |
| 399 | |
Fred Oh | c274a7a | 2013-03-25 13:59:17 -0700 | [diff] [blame] | 400 | int msm_audio_ion_cache_operations(struct audio_buffer *abuff, int cache_op) |
| 401 | { |
| 402 | unsigned long ionflag = 0; |
| 403 | int rc = 0; |
| 404 | int msm_cache_ops = 0; |
| 405 | |
| 406 | if (!abuff) { |
| 407 | pr_err("Invalid params: %p, %p\n", __func__, abuff); |
| 408 | return -EINVAL; |
| 409 | } |
| 410 | rc = ion_handle_get_flags(abuff->client, abuff->handle, |
| 411 | &ionflag); |
| 412 | if (rc) { |
| 413 | pr_err("ion_handle_get_flags failed: %d\n", rc); |
| 414 | goto cache_op_failed; |
| 415 | } |
| 416 | |
| 417 | /* has to be CACHED */ |
| 418 | if (ION_IS_CACHED(ionflag)) { |
| 419 | /* ION_IOC_INV_CACHES or ION_IOC_CLEAN_CACHES */ |
| 420 | msm_cache_ops = cache_op; |
| 421 | rc = msm_ion_do_cache_op(abuff->client, |
| 422 | abuff->handle, |
| 423 | (unsigned long *) abuff->data, |
| 424 | (unsigned long)abuff->size, |
| 425 | msm_cache_ops); |
| 426 | if (rc) { |
| 427 | pr_err("cache operation failed %d\n", rc); |
| 428 | goto cache_op_failed; |
| 429 | } |
| 430 | } |
| 431 | cache_op_failed: |
| 432 | return rc; |
| 433 | } |
| 434 | |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 435 | |
| 436 | static int msm_audio_ion_get_phys(struct ion_client *client, |
| 437 | struct ion_handle *handle, |
| 438 | ion_phys_addr_t *addr, size_t *len) |
| 439 | { |
| 440 | int rc = 0; |
| 441 | pr_debug("%s: smmu_enabled = %d\n", __func__, |
| 442 | msm_audio_ion_data.smmu_enabled); |
| 443 | |
| 444 | if (msm_audio_ion_data.smmu_enabled) { |
Fred Oh | c274a7a | 2013-03-25 13:59:17 -0700 | [diff] [blame] | 445 | rc = ion_map_iommu(client, handle, msm_audio_ion_data.domain_id, |
| 446 | 0 /*partition_num*/, SZ_4K /*align*/, 0/*iova_length*/, |
| 447 | addr, (unsigned long *)len, |
| 448 | 0, 0); |
| 449 | if (rc) { |
| 450 | pr_err("%s: ION map iommu failed %d\n", __func__, rc); |
| 451 | return rc; |
| 452 | } |
| 453 | pr_debug("client=%p, domain=%p, domain_id=%d, group=%p", |
| 454 | client, msm_audio_ion_data.domain, |
| 455 | msm_audio_ion_data.domain_id, msm_audio_ion_data.group); |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 456 | } else { |
| 457 | /* SMMU is disabled*/ |
| 458 | rc = ion_phys(client, handle, addr, len); |
| 459 | } |
Fred Oh | c274a7a | 2013-03-25 13:59:17 -0700 | [diff] [blame] | 460 | pr_debug("phys=%x, len=%d, rc=%d\n", (unsigned int)*addr, *len, rc); |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 461 | return rc; |
| 462 | } |
| 463 | |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 464 | static int msm_audio_ion_probe(struct platform_device *pdev) |
| 465 | { |
| 466 | int rc = 0; |
| 467 | const char *msm_audio_ion_dt = "qcom,smmu-enabled"; |
| 468 | bool smmu_enabled; |
| 469 | |
| 470 | if (pdev->dev.of_node == NULL) { |
| 471 | pr_err("%s: device tree is not found\n", __func__); |
| 472 | msm_audio_ion_data.smmu_enabled = 0; |
| 473 | return 0; |
| 474 | } |
| 475 | |
| 476 | smmu_enabled = of_property_read_bool(pdev->dev.of_node, |
| 477 | msm_audio_ion_dt); |
| 478 | msm_audio_ion_data.smmu_enabled = smmu_enabled; |
| 479 | |
Fred Oh | c274a7a | 2013-03-25 13:59:17 -0700 | [diff] [blame] | 480 | if (smmu_enabled) { |
| 481 | msm_audio_ion_data.group = iommu_group_find("lpass_audio"); |
| 482 | if (!msm_audio_ion_data.group) { |
| 483 | pr_debug("Failed to find group lpass_audio deferred\n"); |
| 484 | goto fail_group; |
| 485 | } |
| 486 | msm_audio_ion_data.domain = |
| 487 | iommu_group_get_iommudata(msm_audio_ion_data.group); |
| 488 | if (IS_ERR_OR_NULL(msm_audio_ion_data.domain)) { |
| 489 | pr_err("Failed to get domain data for group %p", |
| 490 | msm_audio_ion_data.group); |
| 491 | goto fail_group; |
| 492 | } |
| 493 | msm_audio_ion_data.domain_id = |
| 494 | msm_find_domain_no(msm_audio_ion_data.domain); |
| 495 | if (msm_audio_ion_data.domain_id < 0) { |
| 496 | pr_err("Failed to get domain index for domain %p", |
| 497 | msm_audio_ion_data.domain); |
| 498 | goto fail_group; |
| 499 | } |
| 500 | pr_debug("domain=%p, domain_id=%d, group=%p", |
| 501 | msm_audio_ion_data.domain, |
| 502 | msm_audio_ion_data.domain_id, msm_audio_ion_data.group); |
| 503 | |
| 504 | /* iommu_attach_group() will make AXI clock ON. For future PL |
| 505 | this will require to be called in once per session */ |
| 506 | rc = iommu_attach_group(msm_audio_ion_data.domain, |
| 507 | msm_audio_ion_data.group); |
| 508 | if (rc) { |
| 509 | pr_err("%s:ION attach group failed %d\n", __func__, rc); |
| 510 | return rc; |
| 511 | } |
| 512 | |
| 513 | } |
| 514 | |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 515 | pr_debug("%s: SMMU-Enabled = %d\n", __func__, smmu_enabled); |
| 516 | return rc; |
Fred Oh | c274a7a | 2013-03-25 13:59:17 -0700 | [diff] [blame] | 517 | |
| 518 | fail_group: |
| 519 | return -EPROBE_DEFER; |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 520 | } |
| 521 | |
| 522 | static int msm_audio_ion_remove(struct platform_device *pdev) |
| 523 | { |
Fred Oh | c274a7a | 2013-03-25 13:59:17 -0700 | [diff] [blame] | 524 | pr_debug("%s: msm audio ion is unloaded, domain=%p, group=%p\n", |
| 525 | __func__, msm_audio_ion_data.domain, msm_audio_ion_data.group); |
| 526 | iommu_detach_group(msm_audio_ion_data.domain, msm_audio_ion_data.group); |
Fred Oh | de9438a | 2013-04-04 11:29:12 -0700 | [diff] [blame] | 527 | |
| 528 | return 0; |
| 529 | } |
| 530 | |
| 531 | static const struct of_device_id msm_audio_ion_dt_match[] = { |
| 532 | { .compatible = "qcom,msm-audio-ion" }, |
| 533 | { } |
| 534 | }; |
| 535 | MODULE_DEVICE_TABLE(of, msm_audio_ion_dt_match); |
| 536 | |
| 537 | static struct platform_driver msm_audio_ion_driver = { |
| 538 | .driver = { |
| 539 | .name = "msm-audio-ion", |
| 540 | .owner = THIS_MODULE, |
| 541 | .of_match_table = msm_audio_ion_dt_match, |
| 542 | }, |
| 543 | .probe = msm_audio_ion_probe, |
| 544 | .remove = __devexit_p(msm_audio_ion_remove), |
| 545 | }; |
| 546 | |
| 547 | static int __init msm_audio_ion_init(void) |
| 548 | { |
| 549 | return platform_driver_register(&msm_audio_ion_driver); |
| 550 | } |
| 551 | module_init(msm_audio_ion_init); |
| 552 | |
| 553 | static void __exit msm_audio_ion_exit(void) |
| 554 | { |
| 555 | platform_driver_unregister(&msm_audio_ion_driver); |
| 556 | } |
| 557 | module_exit(msm_audio_ion_exit); |
| 558 | |
| 559 | MODULE_DESCRIPTION("MSM Audio ION module"); |
| 560 | MODULE_LICENSE("GPL v2"); |