162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license. When using or 462306a36Sopenharmony_ci// redistributing this file, you may do so under either license. 562306a36Sopenharmony_ci// 662306a36Sopenharmony_ci// Copyright(c) 2018-2022 Intel Corporation. All rights reserved. 762306a36Sopenharmony_ci// 862306a36Sopenharmony_ci// Author: Keyon Jie <yang.jie@linux.intel.com> 962306a36Sopenharmony_ci// 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <asm/unaligned.h> 1262306a36Sopenharmony_ci#include <linux/io-64-nonatomic-lo-hi.h> 1362306a36Sopenharmony_ci#include <linux/device.h> 1462306a36Sopenharmony_ci#include <sound/memalloc.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include "sof-utils.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* 1962306a36Sopenharmony_ci * Generic buffer page table creation. 2062306a36Sopenharmony_ci * Take the each physical page address and drop the least significant unused 2162306a36Sopenharmony_ci * bits from each (based on PAGE_SIZE). Then pack valid page address bits 2262306a36Sopenharmony_ci * into compressed page table. 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ciint snd_sof_create_page_table(struct device *dev, 2662306a36Sopenharmony_ci struct snd_dma_buffer *dmab, 2762306a36Sopenharmony_ci unsigned char *page_table, size_t size) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci int i, pages; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci pages = snd_sgbuf_aligned_pages(size); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci dev_dbg(dev, "generating page table for %p size 0x%zx pages %d\n", 3462306a36Sopenharmony_ci dmab->area, size, pages); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci for (i = 0; i < pages; i++) { 3762306a36Sopenharmony_ci /* 3862306a36Sopenharmony_ci * The number of valid address bits for each page is 20. 3962306a36Sopenharmony_ci * idx determines the byte position within page_table 4062306a36Sopenharmony_ci * where the current page's address is stored 4162306a36Sopenharmony_ci * in the compressed page_table. 4262306a36Sopenharmony_ci * This can be calculated by multiplying the page number by 2.5. 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci u32 idx = (5 * i) >> 1; 4562306a36Sopenharmony_ci u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT; 4662306a36Sopenharmony_ci u8 *pg_table; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci pg_table = (u8 *)(page_table + idx); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci /* 5162306a36Sopenharmony_ci * pagetable compression: 5262306a36Sopenharmony_ci * byte 0 byte 1 byte 2 byte 3 byte 4 byte 5 5362306a36Sopenharmony_ci * ___________pfn 0__________ __________pfn 1___________ _pfn 2... 5462306a36Sopenharmony_ci * .... .... .... .... .... .... .... .... .... .... .... 5562306a36Sopenharmony_ci * It is created by: 5662306a36Sopenharmony_ci * 1. set current location to 0, PFN index i to 0 5762306a36Sopenharmony_ci * 2. put pfn[i] at current location in Little Endian byte order 5862306a36Sopenharmony_ci * 3. calculate an intermediate value as 5962306a36Sopenharmony_ci * x = (pfn[i+1] << 4) | (pfn[i] & 0xf) 6062306a36Sopenharmony_ci * 4. put x at offset (current location + 2) in LE byte order 6162306a36Sopenharmony_ci * 5. increment current location by 5 bytes, increment i by 2 6262306a36Sopenharmony_ci * 6. continue to (2) 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_ci if (i & 1) 6562306a36Sopenharmony_ci put_unaligned_le32((pg_table[0] & 0xf) | pfn << 4, 6662306a36Sopenharmony_ci pg_table); 6762306a36Sopenharmony_ci else 6862306a36Sopenharmony_ci put_unaligned_le32(pfn, pg_table); 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci return pages; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ciEXPORT_SYMBOL(snd_sof_create_page_table); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 76