File Index
Files and directories
Sign in
FILE

hmm.c

File preview with syntax highlighting.

linux-6.19.14/drivers/staging/media/atomisp/pci/hmm/hmm.c C
  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/*
 10 * This file contains entry functions for memory management of ISP driver
 11 */
 12#include <linux/kernel.h>
 13#include <linux/types.h>
 14#include <linux/mm.h>
 15#include <linux/highmem.h>	/* for kmap */
 16#include <linux/io.h>		/* for page_to_phys */
 17#include <linux/sysfs.h>
 18
 19#include "hmm/hmm.h"
 20#include "hmm/hmm_bo.h"
 21
 22#include "atomisp_internal.h"
 23#include "asm/cacheflush.h"
 24#include "mmu/isp_mmu.h"
 25#include "mmu/sh_mmu_mrfld.h"
 26
 27struct hmm_bo_device bo_device;
 28static ia_css_ptr dummy_ptr = mmgr_EXCEPTION;
 29static bool hmm_initialized;
 30
 31int hmm_init(void)
 32{
 33	int ret;
 34
 35	ret = hmm_bo_device_init(&bo_device, &sh_mmu_mrfld,
 36				 ISP_VM_START, ISP_VM_SIZE);
 37	if (ret)
 38		dev_err(atomisp_dev, "hmm_bo_device_init failed.\n");
 39
 40	hmm_initialized = true;
 41
 42	/*
 43	 * As hmm use NULL to indicate invalid ISP virtual address,
 44	 * and ISP_VM_START is defined to 0 too, so we allocate
 45	 * one piece of dummy memory, which should return value 0,
 46	 * at the beginning, to avoid hmm_alloc return 0 in the
 47	 * further allocation.
 48	 */
 49	dummy_ptr = hmm_alloc(1);
 50
 51	return ret;
 52}
 53
 54void hmm_cleanup(void)
 55{
 56	if (dummy_ptr == mmgr_EXCEPTION)
 57		return;
 58
 59	/* free dummy memory first */
 60	hmm_free(dummy_ptr);
 61	dummy_ptr = 0;
 62
 63	hmm_bo_device_exit(&bo_device);
 64	hmm_initialized = false;
 65}
 66
 67static ia_css_ptr __hmm_alloc(size_t bytes, enum hmm_bo_type type,
 68			      void *vmalloc_addr)
 69{
 70	unsigned int pgnr;
 71	struct hmm_buffer_object *bo;
 72	int ret;
 73
 74	/*
 75	 * Check if we are initialized. In the ideal world we wouldn't need
 76	 * this but we can tackle it once the driver is a lot cleaner
 77	 */
 78
 79	if (!hmm_initialized)
 80		hmm_init();
 81	/* Get page number from size */
 82	pgnr = size_to_pgnr_ceil(bytes);
 83
 84	/* Buffer object structure init */
 85	bo = hmm_bo_alloc(&bo_device, pgnr);
 86	if (!bo) {
 87		dev_err(atomisp_dev, "hmm_bo_create failed.\n");
 88		goto create_bo_err;
 89	}
 90
 91	/* Allocate pages for memory */
 92	ret = hmm_bo_alloc_pages(bo, type, vmalloc_addr);
 93	if (ret) {
 94		dev_err(atomisp_dev, "hmm_bo_alloc_pages failed.\n");
 95		goto alloc_page_err;
 96	}
 97
 98	/* Combine the virtual address and pages together */
 99	ret = hmm_bo_bind(bo);
100	if (ret) {
101		dev_err(atomisp_dev, "hmm_bo_bind failed.\n");
102		goto bind_err;
103	}
104
105	return bo->start;
106
107bind_err:
108	hmm_bo_free_pages(bo);
109alloc_page_err:
110	hmm_bo_unref(bo);
111create_bo_err:
112	return 0;
113}
114
115ia_css_ptr hmm_alloc(size_t bytes)
116{
117	return __hmm_alloc(bytes, HMM_BO_PRIVATE, NULL);
118}
119
120ia_css_ptr hmm_create_from_vmalloc_buf(size_t bytes, void *vmalloc_addr)
121{
122	return __hmm_alloc(bytes, HMM_BO_VMALLOC, vmalloc_addr);
123}
124
125void hmm_free(ia_css_ptr virt)
126{
127	struct hmm_buffer_object *bo;
128
129	if (WARN_ON(virt == mmgr_EXCEPTION))
130		return;
131
132	bo = hmm_bo_device_search_start(&bo_device, (unsigned int)virt);
133
134	if (!bo) {
135		dev_err(atomisp_dev,
136			"can not find buffer object start with address 0x%x\n",
137			(unsigned int)virt);
138		return;
139	}
140
141	hmm_bo_unbind(bo);
142	hmm_bo_free_pages(bo);
143	hmm_bo_unref(bo);
144}
145
146static inline int hmm_check_bo(struct hmm_buffer_object *bo, unsigned int ptr)
147{
148	if (!bo) {
149		dev_err(atomisp_dev,
150			"can not find buffer object contains address 0x%x\n",
151			ptr);
152		return -EINVAL;
153	}
154
155	if (!hmm_bo_page_allocated(bo)) {
156		dev_err(atomisp_dev,
157			"buffer object has no page allocated.\n");
158		return -EINVAL;
159	}
160
161	if (!hmm_bo_allocated(bo)) {
162		dev_err(atomisp_dev,
163			"buffer object has no virtual address space allocated.\n");
164		return -EINVAL;
165	}
166
167	return 0;
168}
169
170/* Read function in ISP memory management */
171static int load_and_flush_by_kmap(ia_css_ptr virt, void *data,
172				  unsigned int bytes)
173{
174	struct hmm_buffer_object *bo;
175	unsigned int idx, offset, len;
176	char *src, *des;
177	int ret;
178
179	bo = hmm_bo_device_search_in_range(&bo_device, virt);
180	ret = hmm_check_bo(bo, virt);
181	if (ret)
182		return ret;
183
184	des = (char *)data;
185	while (bytes) {
186		idx = (virt - bo->start) >> PAGE_SHIFT;
187		offset = (virt - bo->start) - (idx << PAGE_SHIFT);
188
189		src = (char *)kmap_local_page(bo->pages[idx]) + offset;
190
191		if ((bytes + offset) >= PAGE_SIZE) {
192			len = PAGE_SIZE - offset;
193			bytes -= len;
194		} else {
195			len = bytes;
196			bytes = 0;
197		}
198
199		virt += len;	/* update virt for next loop */
200
201		if (des) {
202			memcpy(des, src, len);
203			des += len;
204		}
205
206		clflush_cache_range(src, len);
207
208		kunmap_local(src);
209	}
210
211	return 0;
212}
213
214/* Read function in ISP memory management */
215static int load_and_flush(ia_css_ptr virt, void *data, unsigned int bytes)
216{
217	struct hmm_buffer_object *bo;
218	int ret;
219
220	bo = hmm_bo_device_search_in_range(&bo_device, virt);
221	ret = hmm_check_bo(bo, virt);
222	if (ret)
223		return ret;
224
225	if (bo->status & HMM_BO_VMAPED || bo->status & HMM_BO_VMAPED_CACHED) {
226		void *src = bo->vmap_addr;
227
228		src += (virt - bo->start);
229		memcpy(data, src, bytes);
230		if (bo->status & HMM_BO_VMAPED_CACHED)
231			clflush_cache_range(src, bytes);
232	} else {
233		void *vptr;
234
235		vptr = hmm_bo_vmap(bo, true);
236		if (!vptr)
237			return load_and_flush_by_kmap(virt, data, bytes);
238		else
239			vptr = vptr + (virt - bo->start);
240
241		memcpy(data, vptr, bytes);
242		clflush_cache_range(vptr, bytes);
243		hmm_bo_vunmap(bo);
244	}
245
246	return 0;
247}
248
249/* Read function in ISP memory management */
250int hmm_load(ia_css_ptr virt, void *data, unsigned int bytes)
251{
252	if (!virt) {
253		dev_warn(atomisp_dev,
254			"hmm_store: address is NULL\n");
255		return -EINVAL;
256	}
257	if (!data) {
258		dev_err(atomisp_dev,
259			"hmm_store: data is a NULL argument\n");
260		return -EINVAL;
261	}
262	return load_and_flush(virt, data, bytes);
263}
264
265/* Flush hmm data from the data cache */
266int hmm_flush(ia_css_ptr virt, unsigned int bytes)
267{
268	return load_and_flush(virt, NULL, bytes);
269}
270
271/* Write function in ISP memory management */
272int hmm_store(ia_css_ptr virt, const void *data, unsigned int bytes)
273{
274	struct hmm_buffer_object *bo;
275	unsigned int idx, offset, len;
276	char *src, *des;
277	int ret;
278
279	if (!virt) {
280		dev_warn(atomisp_dev,
281			"hmm_store: address is NULL\n");
282		return -EINVAL;
283	}
284	if (!data) {
285		dev_err(atomisp_dev,
286			"hmm_store: data is a NULL argument\n");
287		return -EINVAL;
288	}
289
290	bo = hmm_bo_device_search_in_range(&bo_device, virt);
291	ret = hmm_check_bo(bo, virt);
292	if (ret)
293		return ret;
294
295	if (bo->status & HMM_BO_VMAPED || bo->status & HMM_BO_VMAPED_CACHED) {
296		void *dst = bo->vmap_addr;
297
298		dst += (virt - bo->start);
299		memcpy(dst, data, bytes);
300		if (bo->status & HMM_BO_VMAPED_CACHED)
301			clflush_cache_range(dst, bytes);
302	} else {
303		void *vptr;
304
305		vptr = hmm_bo_vmap(bo, true);
306		if (vptr) {
307			vptr = vptr + (virt - bo->start);
308
309			memcpy(vptr, data, bytes);
310			clflush_cache_range(vptr, bytes);
311			hmm_bo_vunmap(bo);
312			return 0;
313		}
314	}
315
316	src = (char *)data;
317	while (bytes) {
318		idx = (virt - bo->start) >> PAGE_SHIFT;
319		offset = (virt - bo->start) - (idx << PAGE_SHIFT);
320
321		des = (char *)kmap_local_page(bo->pages[idx]);
322
323		if (!des) {
324			dev_err(atomisp_dev,
325				"kmap buffer object page failed: pg_idx = %d\n",
326				idx);
327			return -EINVAL;
328		}
329
330		des += offset;
331
332		if ((bytes + offset) >= PAGE_SIZE) {
333			len = PAGE_SIZE - offset;
334			bytes -= len;
335		} else {
336			len = bytes;
337			bytes = 0;
338		}
339
340		virt += len;
341
342		memcpy(des, src, len);
343
344		src += len;
345
346		clflush_cache_range(des, len);
347
348		kunmap_local(des);
349	}
350
351	return 0;
352}
353
354/* memset function in ISP memory management */
355int hmm_set(ia_css_ptr virt, int c, unsigned int bytes)
356{
357	struct hmm_buffer_object *bo;
358	unsigned int idx, offset, len;
359	char *des;
360	int ret;
361
362	bo = hmm_bo_device_search_in_range(&bo_device, virt);
363	ret = hmm_check_bo(bo, virt);
364	if (ret)
365		return ret;
366
367	if (bo->status & HMM_BO_VMAPED || bo->status & HMM_BO_VMAPED_CACHED) {
368		void *dst = bo->vmap_addr;
369
370		dst += (virt - bo->start);
371		memset(dst, c, bytes);
372
373		if (bo->status & HMM_BO_VMAPED_CACHED)
374			clflush_cache_range(dst, bytes);
375	} else {
376		void *vptr;
377
378		vptr = hmm_bo_vmap(bo, true);
379		if (vptr) {
380			vptr = vptr + (virt - bo->start);
381			memset(vptr, c, bytes);
382			clflush_cache_range(vptr, bytes);
383			hmm_bo_vunmap(bo);
384			return 0;
385		}
386	}
387
388	while (bytes) {
389		idx = (virt - bo->start) >> PAGE_SHIFT;
390		offset = (virt - bo->start) - (idx << PAGE_SHIFT);
391
392		des = (char *)kmap_local_page(bo->pages[idx]) + offset;
393
394		if ((bytes + offset) >= PAGE_SIZE) {
395			len = PAGE_SIZE - offset;
396			bytes -= len;
397		} else {
398			len = bytes;
399			bytes = 0;
400		}
401
402		virt += len;
403
404		memset(des, c, len);
405
406		clflush_cache_range(des, len);
407
408		kunmap_local(des);
409	}
410
411	return 0;
412}
413
414/* Virtual address to physical address convert */
415phys_addr_t hmm_virt_to_phys(ia_css_ptr virt)
416{
417	unsigned int idx, offset;
418	struct hmm_buffer_object *bo;
419
420	bo = hmm_bo_device_search_in_range(&bo_device, virt);
421	if (!bo) {
422		dev_err(atomisp_dev,
423			"can not find buffer object contains address 0x%x\n",
424			virt);
425		return -1;
426	}
427
428	idx = (virt - bo->start) >> PAGE_SHIFT;
429	offset = (virt - bo->start) - (idx << PAGE_SHIFT);
430
431	return page_to_phys(bo->pages[idx]) + offset;
432}
433
434int hmm_mmap(struct vm_area_struct *vma, ia_css_ptr virt)
435{
436	struct hmm_buffer_object *bo;
437
438	bo = hmm_bo_device_search_start(&bo_device, virt);
439	if (!bo) {
440		dev_err(atomisp_dev,
441			"can not find buffer object start with address 0x%x\n",
442			virt);
443		return -EINVAL;
444	}
445
446	return hmm_bo_mmap(vma, bo);
447}
448
449/* Map ISP virtual address into IA virtual address */
450void *hmm_vmap(ia_css_ptr virt, bool cached)
451{
452	struct hmm_buffer_object *bo;
453	void *ptr;
454
455	bo = hmm_bo_device_search_in_range(&bo_device, virt);
456	if (!bo) {
457		dev_err(atomisp_dev,
458			"can not find buffer object contains address 0x%x\n",
459			virt);
460		return NULL;
461	}
462
463	ptr = hmm_bo_vmap(bo, cached);
464	if (ptr)
465		return ptr + (virt - bo->start);
466	else
467		return NULL;
468}
469
470/* Flush the memory which is mapped as cached memory through hmm_vmap */
471void hmm_flush_vmap(ia_css_ptr virt)
472{
473	struct hmm_buffer_object *bo;
474
475	bo = hmm_bo_device_search_in_range(&bo_device, virt);
476	if (!bo) {
477		dev_warn(atomisp_dev,
478			 "can not find buffer object contains address 0x%x\n",
479			 virt);
480		return;
481	}
482
483	hmm_bo_flush_vmap(bo);
484}
485
486void hmm_vunmap(ia_css_ptr virt)
487{
488	struct hmm_buffer_object *bo;
489
490	bo = hmm_bo_device_search_in_range(&bo_device, virt);
491	if (!bo) {
492		dev_warn(atomisp_dev,
493			 "can not find buffer object contains address 0x%x\n",
494			 virt);
495		return;
496	}
497
498	hmm_bo_vunmap(bo);
499}
~/ Read-only file preview.