Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
0002 //
0003 // This file is provided under a dual BSD/GPLv2 license.  When using or
0004 // redistributing this file, you may do so under either license.
0005 //
0006 // Copyright(c) 2018-2022 Intel Corporation. All rights reserved.
0007 //
0008 // Author: Keyon Jie <yang.jie@linux.intel.com>
0009 //
0010 
0011 #include <asm/unaligned.h>
0012 #include <linux/io-64-nonatomic-lo-hi.h>
0013 #include <linux/device.h>
0014 #include <sound/memalloc.h>
0015 #include <linux/module.h>
0016 #include "sof-utils.h"
0017 
0018 /*
0019  * Generic buffer page table creation.
0020  * Take the each physical page address and drop the least significant unused
0021  * bits from each (based on PAGE_SIZE). Then pack valid page address bits
0022  * into compressed page table.
0023  */
0024 
0025 int snd_sof_create_page_table(struct device *dev,
0026                   struct snd_dma_buffer *dmab,
0027                   unsigned char *page_table, size_t size)
0028 {
0029     int i, pages;
0030 
0031     pages = snd_sgbuf_aligned_pages(size);
0032 
0033     dev_dbg(dev, "generating page table for %p size 0x%zx pages %d\n",
0034         dmab->area, size, pages);
0035 
0036     for (i = 0; i < pages; i++) {
0037         /*
0038          * The number of valid address bits for each page is 20.
0039          * idx determines the byte position within page_table
0040          * where the current page's address is stored
0041          * in the compressed page_table.
0042          * This can be calculated by multiplying the page number by 2.5.
0043          */
0044         u32 idx = (5 * i) >> 1;
0045         u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT;
0046         u8 *pg_table;
0047 
0048         dev_vdbg(dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn);
0049 
0050         pg_table = (u8 *)(page_table + idx);
0051 
0052         /*
0053          * pagetable compression:
0054          * byte 0     byte 1     byte 2     byte 3     byte 4     byte 5
0055          * ___________pfn 0__________ __________pfn 1___________  _pfn 2...
0056          * .... ....  .... ....  .... ....  .... ....  .... ....  ....
0057          * It is created by:
0058          * 1. set current location to 0, PFN index i to 0
0059          * 2. put pfn[i] at current location in Little Endian byte order
0060          * 3. calculate an intermediate value as
0061          *    x = (pfn[i+1] << 4) | (pfn[i] & 0xf)
0062          * 4. put x at offset (current location + 2) in LE byte order
0063          * 5. increment current location by 5 bytes, increment i by 2
0064          * 6. continue to (2)
0065          */
0066         if (i & 1)
0067             put_unaligned_le32((pg_table[0] & 0xf) | pfn << 4,
0068                        pg_table);
0069         else
0070             put_unaligned_le32(pfn, pg_table);
0071     }
0072 
0073     return pages;
0074 }
0075 EXPORT_SYMBOL(snd_sof_create_page_table);
0076 
0077 MODULE_LICENSE("Dual BSD/GPL");