2018-07-24 09:48:32 -03:00
// SPDX-License-Identifier: GPL-2.0
//
// Freescale ALSA SoC Machine driver utility
//
// Author: Timur Tabi <timur@freescale.com>
//
// Copyright 2010 Freescale Semiconductor, Inc.
2012-03-16 16:56:40 +08:00
2022-07-01 17:32:36 +08:00
# include <linux/clk.h>
# include <linux/clk-provider.h>
2012-03-16 16:56:40 +08:00
# include <linux/module.h>
# include <linux/of_address.h>
# include <sound/soc.h>
# include "fsl_utils.h"
/**
* fsl_asoc_get_dma_channel - determine the dma channel for a SSI node
*
* @ ssi_np : pointer to the SSI device tree node
* @ name : name of the phandle pointing to the dma channel
* @ dai : ASoC DAI link pointer to be filled with platform_name
* @ dma_channel_id : dma channel id to be returned
* @ dma_id : dma id to be returned
*
* This function determines the dma and channel id for given SSI node . It
* also discovers the platform_name for the ASoC DAI link .
*/
int fsl_asoc_get_dma_channel ( struct device_node * ssi_np ,
const char * name ,
struct snd_soc_dai_link * dai ,
unsigned int * dma_channel_id ,
unsigned int * dma_id )
{
struct resource res ;
struct device_node * dma_channel_np , * dma_np ;
2018-02-11 19:53:20 -02:00
const __be32 * iprop ;
2012-03-16 16:56:40 +08:00
int ret ;
dma_channel_np = of_parse_phandle ( ssi_np , name , 0 ) ;
if ( ! dma_channel_np )
return - EINVAL ;
if ( ! of_device_is_compatible ( dma_channel_np , " fsl,ssi-dma-channel " ) ) {
of_node_put ( dma_channel_np ) ;
return - EINVAL ;
}
/* Determine the dev_name for the device_node. This code mimics the
* behavior of of_device_make_bus_id ( ) . We need this because ASoC uses
* the dev_name ( ) of the device to match the platform ( DMA ) device with
* the CPU ( SSI ) device . It ' s all ugly and hackish , but it works ( for
* now ) .
*
* dai - > platform name should already point to an allocated buffer .
*/
ret = of_address_to_resource ( dma_channel_np , 0 , & res ) ;
if ( ret ) {
of_node_put ( dma_channel_np ) ;
return ret ;
}
2019-06-06 13:15:25 +09:00
snprintf ( ( char * ) dai - > platforms - > name , DAI_NAME_SIZE , " %llx.%pOFn " ,
2018-08-28 10:44:28 -05:00
( unsigned long long ) res . start , dma_channel_np ) ;
2012-03-16 16:56:40 +08:00
iprop = of_get_property ( dma_channel_np , " cell-index " , NULL ) ;
if ( ! iprop ) {
of_node_put ( dma_channel_np ) ;
return - EINVAL ;
}
* dma_channel_id = be32_to_cpup ( iprop ) ;
dma_np = of_get_parent ( dma_channel_np ) ;
iprop = of_get_property ( dma_np , " cell-index " , NULL ) ;
if ( ! iprop ) {
of_node_put ( dma_np ) ;
2019-02-26 16:17:50 +08:00
of_node_put ( dma_channel_np ) ;
2012-03-16 16:56:40 +08:00
return - EINVAL ;
}
* dma_id = be32_to_cpup ( iprop ) ;
of_node_put ( dma_np ) ;
of_node_put ( dma_channel_np ) ;
return 0 ;
}
EXPORT_SYMBOL ( fsl_asoc_get_dma_channel ) ;
2022-07-01 17:32:36 +08:00
/**
* fsl_asoc_get_pll_clocks - get two PLL clock source
*
* @ dev : device pointer
* @ pll8k_clk : PLL clock pointer for 8 kHz
* @ pll11k_clk : PLL clock pointer for 11 kHz
*
* This function get two PLL clock source
*/
void fsl_asoc_get_pll_clocks ( struct device * dev , struct clk * * pll8k_clk ,
struct clk * * pll11k_clk )
{
* pll8k_clk = devm_clk_get ( dev , " pll8k " ) ;
if ( IS_ERR ( * pll8k_clk ) )
* pll8k_clk = NULL ;
* pll11k_clk = devm_clk_get ( dev , " pll11k " ) ;
if ( IS_ERR ( * pll11k_clk ) )
* pll11k_clk = NULL ;
}
EXPORT_SYMBOL ( fsl_asoc_get_pll_clocks ) ;
/**
* fsl_asoc_reparent_pll_clocks - set clock parent if necessary
*
* @ dev : device pointer
* @ clk : root clock pointer
* @ pll8k_clk : PLL clock pointer for 8 kHz
* @ pll11k_clk : PLL clock pointer for 11 kHz
* @ ratio : target requency for root clock
*
* This function set root clock parent according to the target ratio
*/
void fsl_asoc_reparent_pll_clocks ( struct device * dev , struct clk * clk ,
struct clk * pll8k_clk ,
struct clk * pll11k_clk , u64 ratio )
{
2022-07-07 19:20:06 +08:00
struct clk * p , * pll = NULL , * npll = NULL ;
2022-07-01 17:32:36 +08:00
bool reparent = false ;
int ret = 0 ;
if ( ! clk | | ! pll8k_clk | | ! pll11k_clk )
return ;
p = clk ;
while ( p & & pll8k_clk & & pll11k_clk ) {
struct clk * pp = clk_get_parent ( p ) ;
if ( clk_is_match ( pp , pll8k_clk ) | |
clk_is_match ( pp , pll11k_clk ) ) {
pll = pp ;
break ;
}
p = pp ;
}
npll = ( do_div ( ratio , 8000 ) ? pll11k_clk : pll8k_clk ) ;
reparent = ( pll & & ! clk_is_match ( pll , npll ) ) ;
if ( reparent ) {
ret = clk_set_parent ( p , npll ) ;
if ( ret < 0 )
2022-07-11 10:39:50 +08:00
dev_warn ( dev , " failed to set parent:%d \n " , ret ) ;
2022-07-01 17:32:36 +08:00
}
}
EXPORT_SYMBOL ( fsl_asoc_reparent_pll_clocks ) ;
2012-03-16 16:56:40 +08:00
MODULE_AUTHOR ( " Timur Tabi <timur@freescale.com> " ) ;
MODULE_DESCRIPTION ( " Freescale ASoC utility code " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;