2020-05-01 09:58:50 -05:00
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2019-04-12 11:05:19 -05:00
//
// This file is provided under a dual BSD/GPLv2 license. When using or
// redistributing this file, you may do so under either license.
//
// Copyright(c) 2018 Intel Corporation. All rights reserved.
//
// Author: Keyon Jie <yang.jie@linux.intel.com>
//
# include <linux/io-64-nonatomic-lo-hi.h>
# include <linux/platform_device.h>
2019-12-04 15:15:48 -06:00
# include <asm/unaligned.h>
2019-04-12 11:05:19 -05:00
# include <sound/soc.h>
# include <sound/sof.h>
# include "sof-priv.h"
2021-09-15 15:21:11 +03:00
# include "ops.h"
2019-04-12 11:05:19 -05:00
/*
* Register IO
*
* The sof_io_xyz ( ) wrappers are typically referenced in snd_sof_dsp_ops
* structures and cannot be inlined .
*/
void sof_io_write ( struct snd_sof_dev * sdev , void __iomem * addr , u32 value )
{
writel ( value , addr ) ;
}
EXPORT_SYMBOL ( sof_io_write ) ;
u32 sof_io_read ( struct snd_sof_dev * sdev , void __iomem * addr )
{
return readl ( addr ) ;
}
EXPORT_SYMBOL ( sof_io_read ) ;
void sof_io_write64 ( struct snd_sof_dev * sdev , void __iomem * addr , u64 value )
{
writeq ( value , addr ) ;
}
EXPORT_SYMBOL ( sof_io_write64 ) ;
u64 sof_io_read64 ( struct snd_sof_dev * sdev , void __iomem * addr )
{
return readq ( addr ) ;
}
EXPORT_SYMBOL ( sof_io_read64 ) ;
/*
* IPC Mailbox IO
*/
void sof_mailbox_write ( struct snd_sof_dev * sdev , u32 offset ,
void * message , size_t bytes )
{
void __iomem * dest = sdev - > bar [ sdev - > mailbox_bar ] + offset ;
memcpy_toio ( dest , message , bytes ) ;
}
EXPORT_SYMBOL ( sof_mailbox_write ) ;
void sof_mailbox_read ( struct snd_sof_dev * sdev , u32 offset ,
void * message , size_t bytes )
{
void __iomem * src = sdev - > bar [ sdev - > mailbox_bar ] + offset ;
memcpy_fromio ( message , src , bytes ) ;
}
EXPORT_SYMBOL ( sof_mailbox_read ) ;
/*
* Memory copy .
*/
2021-09-15 15:21:11 +03:00
int sof_block_write ( struct snd_sof_dev * sdev , enum snd_sof_fw_blk_type blk_type ,
u32 offset , void * src , size_t size )
2019-04-12 11:05:19 -05:00
{
2021-09-15 15:21:11 +03:00
int bar = snd_sof_dsp_get_bar_index ( sdev , blk_type ) ;
2019-04-12 11:05:19 -05:00
const u8 * src_byte = src ;
2021-09-15 15:21:11 +03:00
void __iomem * dest ;
2019-04-12 11:05:19 -05:00
u32 affected_mask ;
u32 tmp ;
int m , n ;
2021-09-15 15:21:11 +03:00
if ( bar < 0 )
return bar ;
dest = sdev - > bar [ bar ] + offset ;
2019-04-12 11:05:19 -05:00
m = size / 4 ;
n = size % 4 ;
/* __iowrite32_copy use 32bit size values so divide by 4 */
__iowrite32_copy ( dest , src , m ) ;
if ( n ) {
affected_mask = ( 1 < < ( 8 * n ) ) - 1 ;
/* first read the 32bit data of dest, then change affected
* bytes , and write back to dest . For unaffected bytes , it
* should not be changed
*/
tmp = ioread32 ( dest + m * 4 ) ;
tmp & = ~ affected_mask ;
tmp | = * ( u32 * ) ( src_byte + m * 4 ) & affected_mask ;
iowrite32 ( tmp , dest + m * 4 ) ;
}
2021-09-15 15:21:11 +03:00
return 0 ;
2019-04-12 11:05:19 -05:00
}
EXPORT_SYMBOL ( sof_block_write ) ;
2021-09-15 15:21:11 +03:00
int sof_block_read ( struct snd_sof_dev * sdev , enum snd_sof_fw_blk_type blk_type ,
u32 offset , void * dest , size_t size )
2019-04-12 11:05:19 -05:00
{
2021-09-15 15:21:11 +03:00
int bar = snd_sof_dsp_get_bar_index ( sdev , blk_type ) ;
if ( bar < 0 )
return bar ;
memcpy_fromio ( dest , sdev - > bar [ bar ] + offset , size ) ;
2019-04-12 11:05:19 -05:00
2021-09-15 15:21:11 +03:00
return 0 ;
2019-04-12 11:05:19 -05:00
}
EXPORT_SYMBOL ( sof_block_read ) ;
2019-12-04 15:15:48 -06:00
/*
* Generic buffer page table creation .
* Take the each physical page address and drop the least significant unused
* bits from each ( based on PAGE_SIZE ) . Then pack valid page address bits
* into compressed page table .
*/
int snd_sof_create_page_table ( struct device * dev ,
struct snd_dma_buffer * dmab ,
unsigned char * page_table , size_t size )
{
int i , pages ;
pages = snd_sgbuf_aligned_pages ( size ) ;
dev_dbg ( dev , " generating page table for %p size 0x%zx pages %d \n " ,
dmab - > area , size , pages ) ;
for ( i = 0 ; i < pages ; i + + ) {
/*
* The number of valid address bits for each page is 20.
* idx determines the byte position within page_table
* where the current page ' s address is stored
* in the compressed page_table .
* This can be calculated by multiplying the page number by 2.5 .
*/
u32 idx = ( 5 * i ) > > 1 ;
u32 pfn = snd_sgbuf_get_addr ( dmab , i * PAGE_SIZE ) > > PAGE_SHIFT ;
u8 * pg_table ;
dev_vdbg ( dev , " pfn i %i idx %d pfn %x \n " , i , idx , pfn ) ;
pg_table = ( u8 * ) ( page_table + idx ) ;
/*
* pagetable compression :
* byte 0 byte 1 byte 2 byte 3 byte 4 byte 5
* ___________pfn 0 __________ __________pfn 1 ___________ _pfn 2. . .
* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
* It is created by :
* 1. set current location to 0 , PFN index i to 0
* 2. put pfn [ i ] at current location in Little Endian byte order
* 3. calculate an intermediate value as
* x = ( pfn [ i + 1 ] < < 4 ) | ( pfn [ i ] & 0xf )
* 4. put x at offset ( current location + 2 ) in LE byte order
* 5. increment current location by 5 bytes , increment i by 2
* 6. continue to ( 2 )
*/
if ( i & 1 )
put_unaligned_le32 ( ( pg_table [ 0 ] & 0xf ) | pfn < < 4 ,
pg_table ) ;
else
put_unaligned_le32 ( pfn , pg_table ) ;
}
return pages ;
}
EXPORT_SYMBOL ( snd_sof_create_page_table ) ;