xref: /linux/drivers/staging/media/atomisp/pci/hmm/hmm.c (revision 594ce0b8a998aa4d05827cd7c0d0dcec9a1e3ae2)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Support for Medifield PNW Camera Imaging ISP subsystem.
4  *
5  * Copyright (c) 2010-2017 Intel Corporation. All Rights Reserved.
6  *
7  * Copyright (c) 2010 Silicon Hive www.siliconhive.com.
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License version
11  * 2 as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  *
19  */
20 /*
21  * This file contains entry functions for memory management of ISP driver
22  */
23 #include <linux/kernel.h>
24 #include <linux/types.h>
25 #include <linux/mm.h>
26 #include <linux/highmem.h>	/* for kmap */
27 #include <linux/io.h>		/* for page_to_phys */
28 #include <linux/sysfs.h>
29 
30 #include "hmm/hmm.h"
31 #include "hmm/hmm_bo.h"
32 
33 #include "atomisp_internal.h"
34 #include "asm/cacheflush.h"
35 #include "mmu/isp_mmu.h"
36 #include "mmu/sh_mmu_mrfld.h"
37 
38 struct hmm_bo_device bo_device;
39 static ia_css_ptr dummy_ptr = mmgr_EXCEPTION;
40 static bool hmm_initialized;
41 
42 /*
43  * p: private
44  * v: vmalloc
45  */
46 static const char hmm_bo_type_string[] = "pv";
47 
48 static ssize_t bo_show(struct device *dev, struct device_attribute *attr,
49 		       char *buf, struct list_head *bo_list, bool active)
50 {
51 	ssize_t ret = 0;
52 	struct hmm_buffer_object *bo;
53 	unsigned long flags;
54 	int i;
55 	long total[HMM_BO_LAST] = { 0 };
56 	long count[HMM_BO_LAST] = { 0 };
57 	int index1 = 0;
58 	int index2 = 0;
59 
60 	ret = scnprintf(buf, PAGE_SIZE, "type pgnr\n");
61 	if (ret <= 0)
62 		return 0;
63 
64 	index1 += ret;
65 
66 	spin_lock_irqsave(&bo_device.list_lock, flags);
67 	list_for_each_entry(bo, bo_list, list) {
68 		if ((active && (bo->status & HMM_BO_ALLOCED)) ||
69 		    (!active && !(bo->status & HMM_BO_ALLOCED))) {
70 			ret = scnprintf(buf + index1, PAGE_SIZE - index1,
71 					"%c %d\n",
72 					hmm_bo_type_string[bo->type], bo->pgnr);
73 
74 			total[bo->type] += bo->pgnr;
75 			count[bo->type]++;
76 			if (ret > 0)
77 				index1 += ret;
78 		}
79 	}
80 	spin_unlock_irqrestore(&bo_device.list_lock, flags);
81 
82 	for (i = 0; i < HMM_BO_LAST; i++) {
83 		if (count[i]) {
84 			ret = scnprintf(buf + index1 + index2,
85 					PAGE_SIZE - index1 - index2,
86 					"%ld %c buffer objects: %ld KB\n",
87 					count[i], hmm_bo_type_string[i],
88 					total[i] * 4);
89 			if (ret > 0)
90 				index2 += ret;
91 		}
92 	}
93 
94 	/* Add trailing zero, not included by scnprintf */
95 	return index1 + index2 + 1;
96 }
97 
98 static ssize_t active_bo_show(struct device *dev, struct device_attribute *attr,
99 			      char *buf)
100 {
101 	return bo_show(dev, attr, buf, &bo_device.entire_bo_list, true);
102 }
103 
104 static ssize_t free_bo_show(struct device *dev, struct device_attribute *attr,
105 			    char *buf)
106 {
107 	return bo_show(dev, attr, buf, &bo_device.entire_bo_list, false);
108 }
109 
110 
111 static DEVICE_ATTR_RO(active_bo);
112 static DEVICE_ATTR_RO(free_bo);
113 
114 static struct attribute *sysfs_attrs_ctrl[] = {
115 	&dev_attr_active_bo.attr,
116 	&dev_attr_free_bo.attr,
117 	NULL
118 };
119 
120 static struct attribute_group atomisp_attribute_group[] = {
121 	{.attrs = sysfs_attrs_ctrl },
122 };
123 
124 int hmm_init(void)
125 {
126 	int ret;
127 
128 	ret = hmm_bo_device_init(&bo_device, &sh_mmu_mrfld,
129 				 ISP_VM_START, ISP_VM_SIZE);
130 	if (ret)
131 		dev_err(atomisp_dev, "hmm_bo_device_init failed.\n");
132 
133 	hmm_initialized = true;
134 
135 	/*
136 	 * As hmm use NULL to indicate invalid ISP virtual address,
137 	 * and ISP_VM_START is defined to 0 too, so we allocate
138 	 * one piece of dummy memory, which should return value 0,
139 	 * at the beginning, to avoid hmm_alloc return 0 in the
140 	 * further allocation.
141 	 */
142 	dummy_ptr = hmm_alloc(1);
143 
144 	if (!ret) {
145 		ret = sysfs_create_group(&atomisp_dev->kobj,
146 					 atomisp_attribute_group);
147 		if (ret)
148 			dev_err(atomisp_dev,
149 				"%s Failed to create sysfs\n", __func__);
150 	}
151 
152 	return ret;
153 }
154 
155 void hmm_cleanup(void)
156 {
157 	if (dummy_ptr == mmgr_EXCEPTION)
158 		return;
159 	sysfs_remove_group(&atomisp_dev->kobj, atomisp_attribute_group);
160 
161 	/* free dummy memory first */
162 	hmm_free(dummy_ptr);
163 	dummy_ptr = 0;
164 
165 	hmm_bo_device_exit(&bo_device);
166 	hmm_initialized = false;
167 }
168 
169 static ia_css_ptr __hmm_alloc(size_t bytes, enum hmm_bo_type type,
170 			      void *vmalloc_addr)
171 {
172 	unsigned int pgnr;
173 	struct hmm_buffer_object *bo;
174 	int ret;
175 
176 	/*
177 	 * Check if we are initialized. In the ideal world we wouldn't need
178 	 * this but we can tackle it once the driver is a lot cleaner
179 	 */
180 
181 	if (!hmm_initialized)
182 		hmm_init();
183 	/* Get page number from size */
184 	pgnr = size_to_pgnr_ceil(bytes);
185 
186 	/* Buffer object structure init */
187 	bo = hmm_bo_alloc(&bo_device, pgnr);
188 	if (!bo) {
189 		dev_err(atomisp_dev, "hmm_bo_create failed.\n");
190 		goto create_bo_err;
191 	}
192 
193 	/* Allocate pages for memory */
194 	ret = hmm_bo_alloc_pages(bo, type, vmalloc_addr);
195 	if (ret) {
196 		dev_err(atomisp_dev, "hmm_bo_alloc_pages failed.\n");
197 		goto alloc_page_err;
198 	}
199 
200 	/* Combine the virtual address and pages together */
201 	ret = hmm_bo_bind(bo);
202 	if (ret) {
203 		dev_err(atomisp_dev, "hmm_bo_bind failed.\n");
204 		goto bind_err;
205 	}
206 
207 	dev_dbg(atomisp_dev, "pages: 0x%08x (%zu bytes), type: %d, vmalloc %p\n",
208 		bo->start, bytes, type, vmalloc_noprof);
209 
210 	return bo->start;
211 
212 bind_err:
213 	hmm_bo_free_pages(bo);
214 alloc_page_err:
215 	hmm_bo_unref(bo);
216 create_bo_err:
217 	return 0;
218 }
219 
220 ia_css_ptr hmm_alloc(size_t bytes)
221 {
222 	return __hmm_alloc(bytes, HMM_BO_PRIVATE, NULL);
223 }
224 
225 ia_css_ptr hmm_create_from_vmalloc_buf(size_t bytes, void *vmalloc_addr)
226 {
227 	return __hmm_alloc(bytes, HMM_BO_VMALLOC, vmalloc_addr);
228 }
229 
230 void hmm_free(ia_css_ptr virt)
231 {
232 	struct hmm_buffer_object *bo;
233 
234 	dev_dbg(atomisp_dev, "%s: free 0x%08x\n", __func__, virt);
235 
236 	if (WARN_ON(virt == mmgr_EXCEPTION))
237 		return;
238 
239 	bo = hmm_bo_device_search_start(&bo_device, (unsigned int)virt);
240 
241 	if (!bo) {
242 		dev_err(atomisp_dev,
243 			"can not find buffer object start with address 0x%x\n",
244 			(unsigned int)virt);
245 		return;
246 	}
247 
248 	hmm_bo_unbind(bo);
249 	hmm_bo_free_pages(bo);
250 	hmm_bo_unref(bo);
251 }
252 
253 static inline int hmm_check_bo(struct hmm_buffer_object *bo, unsigned int ptr)
254 {
255 	if (!bo) {
256 		dev_err(atomisp_dev,
257 			"can not find buffer object contains address 0x%x\n",
258 			ptr);
259 		return -EINVAL;
260 	}
261 
262 	if (!hmm_bo_page_allocated(bo)) {
263 		dev_err(atomisp_dev,
264 			"buffer object has no page allocated.\n");
265 		return -EINVAL;
266 	}
267 
268 	if (!hmm_bo_allocated(bo)) {
269 		dev_err(atomisp_dev,
270 			"buffer object has no virtual address space allocated.\n");
271 		return -EINVAL;
272 	}
273 
274 	return 0;
275 }
276 
277 /* Read function in ISP memory management */
278 static int load_and_flush_by_kmap(ia_css_ptr virt, void *data,
279 				  unsigned int bytes)
280 {
281 	struct hmm_buffer_object *bo;
282 	unsigned int idx, offset, len;
283 	char *src, *des;
284 	int ret;
285 
286 	bo = hmm_bo_device_search_in_range(&bo_device, virt);
287 	ret = hmm_check_bo(bo, virt);
288 	if (ret)
289 		return ret;
290 
291 	des = (char *)data;
292 	while (bytes) {
293 		idx = (virt - bo->start) >> PAGE_SHIFT;
294 		offset = (virt - bo->start) - (idx << PAGE_SHIFT);
295 
296 		src = (char *)kmap_local_page(bo->pages[idx]) + offset;
297 
298 		if ((bytes + offset) >= PAGE_SIZE) {
299 			len = PAGE_SIZE - offset;
300 			bytes -= len;
301 		} else {
302 			len = bytes;
303 			bytes = 0;
304 		}
305 
306 		virt += len;	/* update virt for next loop */
307 
308 		if (des) {
309 			memcpy(des, src, len);
310 			des += len;
311 		}
312 
313 		clflush_cache_range(src, len);
314 
315 		kunmap_local(src);
316 	}
317 
318 	return 0;
319 }
320 
321 /* Read function in ISP memory management */
322 static int load_and_flush(ia_css_ptr virt, void *data, unsigned int bytes)
323 {
324 	struct hmm_buffer_object *bo;
325 	int ret;
326 
327 	bo = hmm_bo_device_search_in_range(&bo_device, virt);
328 	ret = hmm_check_bo(bo, virt);
329 	if (ret)
330 		return ret;
331 
332 	if (bo->status & HMM_BO_VMAPED || bo->status & HMM_BO_VMAPED_CACHED) {
333 		void *src = bo->vmap_addr;
334 
335 		src += (virt - bo->start);
336 		memcpy(data, src, bytes);
337 		if (bo->status & HMM_BO_VMAPED_CACHED)
338 			clflush_cache_range(src, bytes);
339 	} else {
340 		void *vptr;
341 
342 		vptr = hmm_bo_vmap(bo, true);
343 		if (!vptr)
344 			return load_and_flush_by_kmap(virt, data, bytes);
345 		else
346 			vptr = vptr + (virt - bo->start);
347 
348 		memcpy(data, vptr, bytes);
349 		clflush_cache_range(vptr, bytes);
350 		hmm_bo_vunmap(bo);
351 	}
352 
353 	return 0;
354 }
355 
356 /* Read function in ISP memory management */
357 int hmm_load(ia_css_ptr virt, void *data, unsigned int bytes)
358 {
359 	if (!virt) {
360 		dev_warn(atomisp_dev,
361 			"hmm_store: address is NULL\n");
362 		return -EINVAL;
363 	}
364 	if (!data) {
365 		dev_err(atomisp_dev,
366 			"hmm_store: data is a NULL argument\n");
367 		return -EINVAL;
368 	}
369 	return load_and_flush(virt, data, bytes);
370 }
371 
372 /* Flush hmm data from the data cache */
373 int hmm_flush(ia_css_ptr virt, unsigned int bytes)
374 {
375 	return load_and_flush(virt, NULL, bytes);
376 }
377 
378 /* Write function in ISP memory management */
379 int hmm_store(ia_css_ptr virt, const void *data, unsigned int bytes)
380 {
381 	struct hmm_buffer_object *bo;
382 	unsigned int idx, offset, len;
383 	char *src, *des;
384 	int ret;
385 
386 	if (!virt) {
387 		dev_warn(atomisp_dev,
388 			"hmm_store: address is NULL\n");
389 		return -EINVAL;
390 	}
391 	if (!data) {
392 		dev_err(atomisp_dev,
393 			"hmm_store: data is a NULL argument\n");
394 		return -EINVAL;
395 	}
396 
397 	bo = hmm_bo_device_search_in_range(&bo_device, virt);
398 	ret = hmm_check_bo(bo, virt);
399 	if (ret)
400 		return ret;
401 
402 	if (bo->status & HMM_BO_VMAPED || bo->status & HMM_BO_VMAPED_CACHED) {
403 		void *dst = bo->vmap_addr;
404 
405 		dst += (virt - bo->start);
406 		memcpy(dst, data, bytes);
407 		if (bo->status & HMM_BO_VMAPED_CACHED)
408 			clflush_cache_range(dst, bytes);
409 	} else {
410 		void *vptr;
411 
412 		vptr = hmm_bo_vmap(bo, true);
413 		if (vptr) {
414 			vptr = vptr + (virt - bo->start);
415 
416 			memcpy(vptr, data, bytes);
417 			clflush_cache_range(vptr, bytes);
418 			hmm_bo_vunmap(bo);
419 			return 0;
420 		}
421 	}
422 
423 	src = (char *)data;
424 	while (bytes) {
425 		idx = (virt - bo->start) >> PAGE_SHIFT;
426 		offset = (virt - bo->start) - (idx << PAGE_SHIFT);
427 
428 		des = (char *)kmap_local_page(bo->pages[idx]);
429 
430 		if (!des) {
431 			dev_err(atomisp_dev,
432 				"kmap buffer object page failed: pg_idx = %d\n",
433 				idx);
434 			return -EINVAL;
435 		}
436 
437 		des += offset;
438 
439 		if ((bytes + offset) >= PAGE_SIZE) {
440 			len = PAGE_SIZE - offset;
441 			bytes -= len;
442 		} else {
443 			len = bytes;
444 			bytes = 0;
445 		}
446 
447 		virt += len;
448 
449 		memcpy(des, src, len);
450 
451 		src += len;
452 
453 		clflush_cache_range(des, len);
454 
455 		kunmap_local(des);
456 	}
457 
458 	return 0;
459 }
460 
461 /* memset function in ISP memory management */
462 int hmm_set(ia_css_ptr virt, int c, unsigned int bytes)
463 {
464 	struct hmm_buffer_object *bo;
465 	unsigned int idx, offset, len;
466 	char *des;
467 	int ret;
468 
469 	bo = hmm_bo_device_search_in_range(&bo_device, virt);
470 	ret = hmm_check_bo(bo, virt);
471 	if (ret)
472 		return ret;
473 
474 	if (bo->status & HMM_BO_VMAPED || bo->status & HMM_BO_VMAPED_CACHED) {
475 		void *dst = bo->vmap_addr;
476 
477 		dst += (virt - bo->start);
478 		memset(dst, c, bytes);
479 
480 		if (bo->status & HMM_BO_VMAPED_CACHED)
481 			clflush_cache_range(dst, bytes);
482 	} else {
483 		void *vptr;
484 
485 		vptr = hmm_bo_vmap(bo, true);
486 		if (vptr) {
487 			vptr = vptr + (virt - bo->start);
488 			memset(vptr, c, bytes);
489 			clflush_cache_range(vptr, bytes);
490 			hmm_bo_vunmap(bo);
491 			return 0;
492 		}
493 	}
494 
495 	while (bytes) {
496 		idx = (virt - bo->start) >> PAGE_SHIFT;
497 		offset = (virt - bo->start) - (idx << PAGE_SHIFT);
498 
499 		des = (char *)kmap_local_page(bo->pages[idx]) + offset;
500 
501 		if ((bytes + offset) >= PAGE_SIZE) {
502 			len = PAGE_SIZE - offset;
503 			bytes -= len;
504 		} else {
505 			len = bytes;
506 			bytes = 0;
507 		}
508 
509 		virt += len;
510 
511 		memset(des, c, len);
512 
513 		clflush_cache_range(des, len);
514 
515 		kunmap_local(des);
516 	}
517 
518 	return 0;
519 }
520 
521 /* Virtual address to physical address convert */
522 phys_addr_t hmm_virt_to_phys(ia_css_ptr virt)
523 {
524 	unsigned int idx, offset;
525 	struct hmm_buffer_object *bo;
526 
527 	bo = hmm_bo_device_search_in_range(&bo_device, virt);
528 	if (!bo) {
529 		dev_err(atomisp_dev,
530 			"can not find buffer object contains address 0x%x\n",
531 			virt);
532 		return -1;
533 	}
534 
535 	idx = (virt - bo->start) >> PAGE_SHIFT;
536 	offset = (virt - bo->start) - (idx << PAGE_SHIFT);
537 
538 	return page_to_phys(bo->pages[idx]) + offset;
539 }
540 
541 int hmm_mmap(struct vm_area_struct *vma, ia_css_ptr virt)
542 {
543 	struct hmm_buffer_object *bo;
544 
545 	bo = hmm_bo_device_search_start(&bo_device, virt);
546 	if (!bo) {
547 		dev_err(atomisp_dev,
548 			"can not find buffer object start with address 0x%x\n",
549 			virt);
550 		return -EINVAL;
551 	}
552 
553 	return hmm_bo_mmap(vma, bo);
554 }
555 
556 /* Map ISP virtual address into IA virtual address */
557 void *hmm_vmap(ia_css_ptr virt, bool cached)
558 {
559 	struct hmm_buffer_object *bo;
560 	void *ptr;
561 
562 	bo = hmm_bo_device_search_in_range(&bo_device, virt);
563 	if (!bo) {
564 		dev_err(atomisp_dev,
565 			"can not find buffer object contains address 0x%x\n",
566 			virt);
567 		return NULL;
568 	}
569 
570 	ptr = hmm_bo_vmap(bo, cached);
571 	if (ptr)
572 		return ptr + (virt - bo->start);
573 	else
574 		return NULL;
575 }
576 
577 /* Flush the memory which is mapped as cached memory through hmm_vmap */
578 void hmm_flush_vmap(ia_css_ptr virt)
579 {
580 	struct hmm_buffer_object *bo;
581 
582 	bo = hmm_bo_device_search_in_range(&bo_device, virt);
583 	if (!bo) {
584 		dev_warn(atomisp_dev,
585 			 "can not find buffer object contains address 0x%x\n",
586 			 virt);
587 		return;
588 	}
589 
590 	hmm_bo_flush_vmap(bo);
591 }
592 
593 void hmm_vunmap(ia_css_ptr virt)
594 {
595 	struct hmm_buffer_object *bo;
596 
597 	bo = hmm_bo_device_search_in_range(&bo_device, virt);
598 	if (!bo) {
599 		dev_warn(atomisp_dev,
600 			 "can not find buffer object contains address 0x%x\n",
601 			 virt);
602 		return;
603 	}
604 
605 	hmm_bo_vunmap(bo);
606 }
607