2008-01-11 18:15:26 +01:00
/**
2010-03-17 20:15:21 +00:00
* Freescale MPC8610HPCD ALSA SoC Machine driver
2008-01-11 18:15:26 +01:00
*
* Author : Timur Tabi < timur @ freescale . com >
*
2010-03-17 20:15:21 +00:00
* Copyright 2007 - 2010 Freescale Semiconductor , Inc .
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed " as is " without any warranty of any
* kind , whether express or implied .
2008-01-11 18:15:26 +01:00
*/
# include <linux/module.h>
# include <linux/interrupt.h>
# include <linux/of_device.h>
2010-08-19 15:26:58 -05:00
# include <linux/slab.h>
2011-12-01 19:52:47 -06:00
# include <linux/of_i2c.h>
2008-01-11 18:15:26 +01:00
# include <sound/soc.h>
2010-07-22 11:33:30 -05:00
# include <asm/fsl_guts.h>
2008-01-11 18:15:26 +01:00
# include "fsl_dma.h"
# include "fsl_ssi.h"
2010-03-17 20:15:21 +00:00
/* There's only one global utilities register */
static phys_addr_t guts_phys ;
# define DAI_NAME_SIZE 32
2008-01-11 18:15:26 +01:00
/**
2010-03-17 20:15:21 +00:00
* mpc8610_hpcd_data : machine - specific ASoC device data
2008-01-11 18:15:26 +01:00
*
* This structure contains data for a single sound platform device on an
* MPC8610 HPCD . Some of the data is taken from the device tree .
*/
struct mpc8610_hpcd_data {
2010-03-17 20:15:21 +00:00
struct snd_soc_dai_link dai [ 2 ] ;
struct snd_soc_card card ;
2008-01-11 18:15:26 +01:00
unsigned int dai_format ;
unsigned int codec_clk_direction ;
unsigned int cpu_clk_direction ;
unsigned int clk_frequency ;
2010-03-17 20:15:21 +00:00
unsigned int ssi_id ; /* 0 = SSI1, 1 = SSI2, etc */
unsigned int dma_id [ 2 ] ; /* 0 = DMA1, 1 = DMA2, etc */
2008-01-11 18:15:26 +01:00
unsigned int dma_channel_id [ 2 ] ; /* 0 = ch 0, 1 = ch 1, etc*/
2010-03-17 20:15:21 +00:00
char codec_dai_name [ DAI_NAME_SIZE ] ;
char codec_name [ DAI_NAME_SIZE ] ;
char platform_name [ 2 ] [ DAI_NAME_SIZE ] ; /* One for each DMA channel */
2008-01-11 18:15:26 +01:00
} ;
/**
2010-03-17 20:15:21 +00:00
* mpc8610_hpcd_machine_probe : initialize the board
2008-01-11 18:15:26 +01:00
*
2010-03-17 20:15:21 +00:00
* This function is used to initialize the board - specific hardware .
2008-01-11 18:15:26 +01:00
*
* Here we program the DMACR and PMUXCR registers .
*/
2011-01-26 14:17:20 +00:00
static int mpc8610_hpcd_machine_probe ( struct snd_soc_card * card )
2008-01-11 18:15:26 +01:00
{
struct mpc8610_hpcd_data * machine_data =
2010-03-17 20:15:21 +00:00
container_of ( card , struct mpc8610_hpcd_data , card ) ;
2010-07-22 11:33:30 -05:00
struct ccsr_guts_86xx __iomem * guts ;
2008-01-11 18:15:26 +01:00
2010-07-22 11:33:30 -05:00
guts = ioremap ( guts_phys , sizeof ( struct ccsr_guts_86xx ) ) ;
2010-03-17 20:15:21 +00:00
if ( ! guts ) {
dev_err ( card - > dev , " could not map global utilities \n " ) ;
return - ENOMEM ;
}
2008-01-11 18:15:26 +01:00
2010-03-17 20:15:21 +00:00
/* Program the signal routing between the SSI and the DMA */
guts_set_dmacr ( guts , machine_data - > dma_id [ 0 ] ,
machine_data - > dma_channel_id [ 0 ] ,
CCSR_GUTS_DMACR_DEV_SSI ) ;
guts_set_dmacr ( guts , machine_data - > dma_id [ 1 ] ,
machine_data - > dma_channel_id [ 1 ] ,
CCSR_GUTS_DMACR_DEV_SSI ) ;
guts_set_pmuxcr_dma ( guts , machine_data - > dma_id [ 0 ] ,
machine_data - > dma_channel_id [ 0 ] , 0 ) ;
guts_set_pmuxcr_dma ( guts , machine_data - > dma_id [ 1 ] ,
machine_data - > dma_channel_id [ 1 ] , 0 ) ;
2008-01-11 18:15:26 +01:00
switch ( machine_data - > ssi_id ) {
case 0 :
2010-03-17 20:15:21 +00:00
clrsetbits_be32 ( & guts - > pmuxcr ,
2008-01-11 18:15:26 +01:00
CCSR_GUTS_PMUXCR_SSI1_MASK , CCSR_GUTS_PMUXCR_SSI1_SSI ) ;
break ;
case 1 :
2010-03-17 20:15:21 +00:00
clrsetbits_be32 ( & guts - > pmuxcr ,
2008-01-11 18:15:26 +01:00
CCSR_GUTS_PMUXCR_SSI2_MASK , CCSR_GUTS_PMUXCR_SSI2_SSI ) ;
break ;
}
2010-03-17 20:15:21 +00:00
iounmap ( guts ) ;
2008-01-11 18:15:26 +01:00
return 0 ;
}
/**
* mpc8610_hpcd_startup : program the board with various hardware parameters
*
* This function takes board - specific information , like clock frequencies
* and serial data formats , and passes that information to the codec and
* transport drivers .
*/
static int mpc8610_hpcd_startup ( struct snd_pcm_substream * substream )
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
struct mpc8610_hpcd_data * machine_data =
2010-03-17 20:15:21 +00:00
container_of ( rtd - > card , struct mpc8610_hpcd_data , card ) ;
struct device * dev = rtd - > card - > dev ;
2008-01-11 18:15:26 +01:00
int ret = 0 ;
/* Tell the codec driver what the serial protocol is. */
2010-03-17 20:15:21 +00:00
ret = snd_soc_dai_set_fmt ( rtd - > codec_dai , machine_data - > dai_format ) ;
2008-07-08 13:19:18 +01:00
if ( ret < 0 ) {
2010-03-17 20:15:21 +00:00
dev_err ( dev , " could not set codec driver audio format \n " ) ;
2008-07-08 13:19:18 +01:00
return ret ;
2008-01-11 18:15:26 +01:00
}
/*
* Tell the codec driver what the MCLK frequency is , and whether it ' s
* a slave or master .
*/
2010-03-17 20:15:21 +00:00
ret = snd_soc_dai_set_sysclk ( rtd - > codec_dai , 0 ,
machine_data - > clk_frequency ,
machine_data - > codec_clk_direction ) ;
2008-07-08 13:19:18 +01:00
if ( ret < 0 ) {
2010-03-17 20:15:21 +00:00
dev_err ( dev , " could not set codec driver clock params \n " ) ;
2008-07-08 13:19:18 +01:00
return ret ;
2008-01-11 18:15:26 +01:00
}
return 0 ;
}
/**
* mpc8610_hpcd_machine_remove : Remove the sound device
*
* This function is called to remove the sound device for one SSI . We
* de - program the DMACR and PMUXCR register .
*/
2011-01-26 14:17:20 +00:00
static int mpc8610_hpcd_machine_remove ( struct snd_soc_card * card )
2008-01-11 18:15:26 +01:00
{
struct mpc8610_hpcd_data * machine_data =
2010-03-17 20:15:21 +00:00
container_of ( card , struct mpc8610_hpcd_data , card ) ;
2010-07-22 11:33:30 -05:00
struct ccsr_guts_86xx __iomem * guts ;
2010-03-17 20:15:21 +00:00
2010-07-22 11:33:30 -05:00
guts = ioremap ( guts_phys , sizeof ( struct ccsr_guts_86xx ) ) ;
2010-03-17 20:15:21 +00:00
if ( ! guts ) {
dev_err ( card - > dev , " could not map global utilities \n " ) ;
return - ENOMEM ;
}
2008-01-11 18:15:26 +01:00
/* Restore the signal routing */
2010-03-17 20:15:21 +00:00
guts_set_dmacr ( guts , machine_data - > dma_id [ 0 ] ,
machine_data - > dma_channel_id [ 0 ] , 0 ) ;
guts_set_dmacr ( guts , machine_data - > dma_id [ 1 ] ,
machine_data - > dma_channel_id [ 1 ] , 0 ) ;
2008-01-11 18:15:26 +01:00
switch ( machine_data - > ssi_id ) {
case 0 :
2010-03-17 20:15:21 +00:00
clrsetbits_be32 ( & guts - > pmuxcr ,
2008-01-11 18:15:26 +01:00
CCSR_GUTS_PMUXCR_SSI1_MASK , CCSR_GUTS_PMUXCR_SSI1_LA ) ;
break ;
case 1 :
2010-03-17 20:15:21 +00:00
clrsetbits_be32 ( & guts - > pmuxcr ,
2008-06-13 14:02:31 -05:00
CCSR_GUTS_PMUXCR_SSI2_MASK , CCSR_GUTS_PMUXCR_SSI2_LA ) ;
2008-01-11 18:15:26 +01:00
break ;
}
2010-03-17 20:15:21 +00:00
iounmap ( guts ) ;
2008-01-11 18:15:26 +01:00
return 0 ;
}
/**
2010-03-17 20:15:21 +00:00
* mpc8610_hpcd_ops : ASoC machine driver operations
2008-01-11 18:15:26 +01:00
*/
static struct snd_soc_ops mpc8610_hpcd_ops = {
. startup = mpc8610_hpcd_startup ,
} ;
/**
2010-03-17 20:15:21 +00:00
* get_node_by_phandle_name - get a node by its phandle name
2008-01-11 18:15:26 +01:00
*
2010-03-17 20:15:21 +00:00
* This function takes a node , the name of a property in that node , and a
* compatible string . Assuming the property is a phandle to another node ,
* it returns that node , ( optionally ) if that node is compatible .
2008-01-11 18:15:26 +01:00
*
2010-03-17 20:15:21 +00:00
* If the property is not a phandle , or the node it points to is not compatible
* with the specific string , then NULL is returned .
*/
static struct device_node * get_node_by_phandle_name ( struct device_node * np ,
const char * name ,
const char * compatible )
{
const phandle * ph ;
int len ;
ph = of_get_property ( np , name , & len ) ;
if ( ! ph | | ( len ! = sizeof ( phandle ) ) )
return NULL ;
np = of_find_node_by_phandle ( * ph ) ;
if ( ! np )
return NULL ;
if ( compatible & & ! of_device_is_compatible ( np , compatible ) ) {
of_node_put ( np ) ;
return NULL ;
}
return np ;
}
/**
* get_parent_cell_index - - return the cell - index of the parent of a node
2008-01-11 18:15:26 +01:00
*
2010-03-17 20:15:21 +00:00
* Return the value of the cell - index property of the parent of the given
* node . This is used for DMA channel nodes that need to know the DMA ID
* of the controller they are on .
*/
static int get_parent_cell_index ( struct device_node * np )
{
struct device_node * parent = of_get_parent ( np ) ;
const u32 * iprop ;
if ( ! parent )
return - 1 ;
iprop = of_get_property ( parent , " cell-index " , NULL ) ;
of_node_put ( parent ) ;
if ( ! iprop )
return - 1 ;
2011-06-08 15:02:55 -05:00
return be32_to_cpup ( iprop ) ;
2010-03-17 20:15:21 +00:00
}
/**
* codec_node_dev_name - determine the dev_name for a codec node
2008-01-11 18:15:26 +01:00
*
2010-03-17 20:15:21 +00:00
* This function determines the dev_name for an I2C node . This is the name
* that would be returned by dev_name ( ) if this device_node were part of a
* ' struct device ' It ' s ugly and hackish , but it works .
*
* The dev_name for such devices include the bus number and I2C address . For
2012-02-24 22:09:38 +08:00
* example , " cs4270.0-004f " .
2008-01-11 18:15:26 +01:00
*/
2010-03-17 20:15:21 +00:00
static int codec_node_dev_name ( struct device_node * np , char * buf , size_t len )
2008-01-11 18:15:26 +01:00
{
const u32 * iprop ;
2011-12-01 19:52:47 -06:00
int addr ;
2010-03-17 20:15:21 +00:00
char temp [ DAI_NAME_SIZE ] ;
2011-12-01 19:52:47 -06:00
struct i2c_client * i2c ;
2010-03-17 20:15:21 +00:00
of_modalias_node ( np , temp , DAI_NAME_SIZE ) ;
iprop = of_get_property ( np , " reg " , NULL ) ;
if ( ! iprop )
return - EINVAL ;
2011-06-08 15:02:55 -05:00
addr = be32_to_cpup ( iprop ) ;
2010-03-17 20:15:21 +00:00
2011-12-01 19:52:47 -06:00
/* We need the adapter number */
i2c = of_find_i2c_device_by_node ( np ) ;
if ( ! i2c )
return - ENODEV ;
2010-03-17 20:15:21 +00:00
2012-02-24 22:09:38 +08:00
snprintf ( buf , len , " %s.%u-%04x " , temp , i2c - > adapter - > nr , addr ) ;
2010-03-17 20:15:21 +00:00
return 0 ;
}
static int get_dma_channel ( struct device_node * ssi_np ,
2012-02-24 22:09:36 +08:00
const char * name ,
2010-03-17 20:15:21 +00:00
struct snd_soc_dai_link * dai ,
unsigned int * dma_channel_id ,
unsigned int * dma_id )
{
2008-01-11 18:15:26 +01:00
struct resource res ;
2010-03-17 20:15:21 +00:00
struct device_node * dma_channel_np ;
const u32 * iprop ;
int ret ;
2012-02-24 22:09:36 +08:00
dma_channel_np = get_node_by_phandle_name ( ssi_np , name ,
2010-03-17 20:15:21 +00:00
" fsl,ssi-dma-channel " ) ;
if ( ! 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 )
return ret ;
snprintf ( ( char * ) dai - > platform_name , DAI_NAME_SIZE , " %llx.%s " ,
( unsigned long long ) res . start , dma_channel_np - > name ) ;
iprop = of_get_property ( dma_channel_np , " cell-index " , NULL ) ;
if ( ! iprop ) {
of_node_put ( dma_channel_np ) ;
return - EINVAL ;
}
2011-06-08 15:02:55 -05:00
* dma_channel_id = be32_to_cpup ( iprop ) ;
2010-03-17 20:15:21 +00:00
* dma_id = get_parent_cell_index ( dma_channel_np ) ;
of_node_put ( dma_channel_np ) ;
return 0 ;
}
/**
* mpc8610_hpcd_probe : platform probe function for the machine driver
*
* Although this is a machine driver , the SSI node is the " master " node with
* respect to audio hardware connections . Therefore , we create a new ASoC
* device for each new SSI node that has a codec attached .
*/
static int mpc8610_hpcd_probe ( struct platform_device * pdev )
{
struct device * dev = pdev - > dev . parent ;
2010-08-19 15:26:58 -05:00
/* ssi_pdev is the platform device for the SSI node that probed us */
struct platform_device * ssi_pdev =
container_of ( dev , struct platform_device , dev ) ;
struct device_node * np = ssi_pdev - > dev . of_node ;
2010-03-17 20:15:21 +00:00
struct device_node * codec_np = NULL ;
2008-01-11 18:15:26 +01:00
struct platform_device * sound_device = NULL ;
struct mpc8610_hpcd_data * machine_data ;
int ret = - ENODEV ;
2010-03-17 20:15:21 +00:00
const char * sprop ;
const u32 * iprop ;
2012-02-24 22:09:37 +08:00
/* Find the codec node for this SSI. */
codec_np = of_parse_phandle ( np , " codec-handle " , 0 ) ;
2010-03-17 20:15:21 +00:00
if ( ! codec_np ) {
dev_err ( dev , " invalid codec node \n " ) ;
return - EINVAL ;
}
2008-01-11 18:15:26 +01:00
machine_data = kzalloc ( sizeof ( struct mpc8610_hpcd_data ) , GFP_KERNEL ) ;
2011-08-20 09:02:01 +02:00
if ( ! machine_data ) {
ret = - ENOMEM ;
goto error_alloc ;
}
2008-01-11 18:15:26 +01:00
2010-08-19 15:26:58 -05:00
machine_data - > dai [ 0 ] . cpu_dai_name = dev_name ( & ssi_pdev - > dev ) ;
2010-03-17 20:15:21 +00:00
machine_data - > dai [ 0 ] . ops = & mpc8610_hpcd_ops ;
2008-01-11 18:15:26 +01:00
2010-03-17 20:15:21 +00:00
/* Determine the codec name, it will be used as the codec DAI name */
ret = codec_node_dev_name ( codec_np , machine_data - > codec_name ,
DAI_NAME_SIZE ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " invalid codec node %s \n " ,
codec_np - > full_name ) ;
ret = - EINVAL ;
2008-01-11 18:15:26 +01:00
goto error ;
2010-03-17 20:15:21 +00:00
}
machine_data - > dai [ 0 ] . codec_name = machine_data - > codec_name ;
2008-01-11 18:15:26 +01:00
2010-03-17 20:15:21 +00:00
/* The DAI name from the codec (snd_soc_dai_driver.name) */
machine_data - > dai [ 0 ] . codec_dai_name = " cs4270-hifi " ;
2008-01-11 18:15:26 +01:00
2010-03-17 20:15:21 +00:00
/* We register two DAIs per SSI, one for playback and the other for
* capture . Currently , we only support codecs that have one DAI for
* both playback and capture .
*/
memcpy ( & machine_data - > dai [ 1 ] , & machine_data - > dai [ 0 ] ,
sizeof ( struct snd_soc_dai_link ) ) ;
2008-01-11 18:15:26 +01:00
/* Get the device ID */
iprop = of_get_property ( np , " cell-index " , NULL ) ;
if ( ! iprop ) {
2010-03-17 20:15:21 +00:00
dev_err ( & pdev - > dev , " cell-index property not found \n " ) ;
2008-01-11 18:15:26 +01:00
ret = - EINVAL ;
goto error ;
}
2011-06-08 15:02:55 -05:00
machine_data - > ssi_id = be32_to_cpup ( iprop ) ;
2008-01-11 18:15:26 +01:00
/* Get the serial format and clock direction. */
sprop = of_get_property ( np , " fsl,mode " , NULL ) ;
if ( ! sprop ) {
2010-03-17 20:15:21 +00:00
dev_err ( & pdev - > dev , " fsl,mode property not found \n " ) ;
2008-01-11 18:15:26 +01:00
ret = - EINVAL ;
goto error ;
}
if ( strcasecmp ( sprop , " i2s-slave " ) = = 0 ) {
2011-11-22 14:38:59 -06:00
machine_data - > dai_format =
SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM ;
2008-01-11 18:15:26 +01:00
machine_data - > codec_clk_direction = SND_SOC_CLOCK_OUT ;
machine_data - > cpu_clk_direction = SND_SOC_CLOCK_IN ;
2010-03-17 20:15:21 +00:00
/* In i2s-slave mode, the codec has its own clock source, so we
2008-01-11 18:15:26 +01:00
* need to get the frequency from the device tree and pass it to
* the codec driver .
*/
iprop = of_get_property ( codec_np , " clock-frequency " , NULL ) ;
if ( ! iprop | | ! * iprop ) {
2010-03-17 20:15:21 +00:00
dev_err ( & pdev - > dev , " codec bus-frequency "
" property is missing or invalid \n " ) ;
2008-01-11 18:15:26 +01:00
ret = - EINVAL ;
goto error ;
}
2011-06-08 15:02:55 -05:00
machine_data - > clk_frequency = be32_to_cpup ( iprop ) ;
2008-01-11 18:15:26 +01:00
} else if ( strcasecmp ( sprop , " i2s-master " ) = = 0 ) {
2011-11-22 14:38:59 -06:00
machine_data - > dai_format =
SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS ;
2008-01-11 18:15:26 +01:00
machine_data - > codec_clk_direction = SND_SOC_CLOCK_IN ;
machine_data - > cpu_clk_direction = SND_SOC_CLOCK_OUT ;
} else if ( strcasecmp ( sprop , " lj-slave " ) = = 0 ) {
2011-11-22 14:38:59 -06:00
machine_data - > dai_format =
SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBM_CFM ;
2008-01-11 18:15:26 +01:00
machine_data - > codec_clk_direction = SND_SOC_CLOCK_OUT ;
machine_data - > cpu_clk_direction = SND_SOC_CLOCK_IN ;
} else if ( strcasecmp ( sprop , " lj-master " ) = = 0 ) {
2011-11-22 14:38:59 -06:00
machine_data - > dai_format =
SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBS_CFS ;
2008-01-11 18:15:26 +01:00
machine_data - > codec_clk_direction = SND_SOC_CLOCK_IN ;
machine_data - > cpu_clk_direction = SND_SOC_CLOCK_OUT ;
2008-02-22 18:41:41 +01:00
} else if ( strcasecmp ( sprop , " rj-slave " ) = = 0 ) {
2011-11-22 14:38:59 -06:00
machine_data - > dai_format =
SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_CBM_CFM ;
2008-01-11 18:15:26 +01:00
machine_data - > codec_clk_direction = SND_SOC_CLOCK_OUT ;
machine_data - > cpu_clk_direction = SND_SOC_CLOCK_IN ;
} else if ( strcasecmp ( sprop , " rj-master " ) = = 0 ) {
2011-11-22 14:38:59 -06:00
machine_data - > dai_format =
SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_CBS_CFS ;
2008-01-11 18:15:26 +01:00
machine_data - > codec_clk_direction = SND_SOC_CLOCK_IN ;
machine_data - > cpu_clk_direction = SND_SOC_CLOCK_OUT ;
} else if ( strcasecmp ( sprop , " ac97-slave " ) = = 0 ) {
2011-11-22 14:38:59 -06:00
machine_data - > dai_format =
SND_SOC_DAIFMT_AC97 | SND_SOC_DAIFMT_CBM_CFM ;
2008-01-11 18:15:26 +01:00
machine_data - > codec_clk_direction = SND_SOC_CLOCK_OUT ;
machine_data - > cpu_clk_direction = SND_SOC_CLOCK_IN ;
} else if ( strcasecmp ( sprop , " ac97-master " ) = = 0 ) {
2011-11-22 14:38:59 -06:00
machine_data - > dai_format =
SND_SOC_DAIFMT_AC97 | SND_SOC_DAIFMT_CBS_CFS ;
2008-01-11 18:15:26 +01:00
machine_data - > codec_clk_direction = SND_SOC_CLOCK_IN ;
machine_data - > cpu_clk_direction = SND_SOC_CLOCK_OUT ;
} else {
2010-03-17 20:15:21 +00:00
dev_err ( & pdev - > dev ,
" unrecognized fsl,mode property '%s' \n " , sprop ) ;
2008-01-11 18:15:26 +01:00
ret = - EINVAL ;
goto error ;
}
if ( ! machine_data - > clk_frequency ) {
2010-03-17 20:15:21 +00:00
dev_err ( & pdev - > dev , " unknown clock frequency \n " ) ;
2008-01-11 18:15:26 +01:00
ret = - EINVAL ;
goto error ;
}
2010-03-17 20:15:21 +00:00
/* Find the playback DMA channel to use. */
machine_data - > dai [ 0 ] . platform_name = machine_data - > platform_name [ 0 ] ;
ret = get_dma_channel ( np , " fsl,playback-dma " , & machine_data - > dai [ 0 ] ,
& machine_data - > dma_channel_id [ 0 ] ,
& machine_data - > dma_id [ 0 ] ) ;
2008-01-11 18:15:26 +01:00
if ( ret ) {
2010-03-17 20:15:21 +00:00
dev_err ( & pdev - > dev , " missing/invalid playback DMA phandle \n " ) ;
2008-01-11 18:15:26 +01:00
goto error ;
}
2010-03-17 20:15:21 +00:00
/* Find the capture DMA channel to use. */
machine_data - > dai [ 1 ] . platform_name = machine_data - > platform_name [ 1 ] ;
ret = get_dma_channel ( np , " fsl,capture-dma " , & machine_data - > dai [ 1 ] ,
& machine_data - > dma_channel_id [ 1 ] ,
& machine_data - > dma_id [ 1 ] ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " missing/invalid capture DMA phandle \n " ) ;
2008-01-11 18:15:26 +01:00
goto error ;
}
2010-03-17 20:15:21 +00:00
/* Initialize our DAI data structure. */
machine_data - > dai [ 0 ] . stream_name = " playback " ;
machine_data - > dai [ 1 ] . stream_name = " capture " ;
machine_data - > dai [ 0 ] . name = machine_data - > dai [ 0 ] . stream_name ;
machine_data - > dai [ 1 ] . name = machine_data - > dai [ 1 ] . stream_name ;
2008-01-11 18:15:26 +01:00
2010-03-17 20:15:21 +00:00
machine_data - > card . probe = mpc8610_hpcd_machine_probe ;
machine_data - > card . remove = mpc8610_hpcd_machine_remove ;
machine_data - > card . name = pdev - > name ; /* The platform driver name */
machine_data - > card . num_links = 2 ;
machine_data - > card . dai_link = machine_data - > dai ;
2008-01-11 18:15:26 +01:00
/* Allocate a new audio platform device structure */
sound_device = platform_device_alloc ( " soc-audio " , - 1 ) ;
if ( ! sound_device ) {
2010-03-17 20:15:21 +00:00
dev_err ( & pdev - > dev , " platform device alloc failed \n " ) ;
2008-01-11 18:15:26 +01:00
ret = - ENOMEM ;
goto error ;
}
2010-03-17 20:15:21 +00:00
/* Associate the card data with the sound device */
platform_set_drvdata ( sound_device , & machine_data - > card ) ;
2008-01-11 18:15:26 +01:00
2010-03-17 20:15:21 +00:00
/* Register with ASoC */
2008-01-11 18:15:26 +01:00
ret = platform_device_add ( sound_device ) ;
if ( ret ) {
2010-03-17 20:15:21 +00:00
dev_err ( & pdev - > dev , " platform device add failed \n " ) ;
2011-08-20 09:02:01 +02:00
goto error_sound ;
2008-01-11 18:15:26 +01:00
}
2010-11-29 14:54:58 +08:00
dev_set_drvdata ( & pdev - > dev , sound_device ) ;
2008-01-11 18:15:26 +01:00
2010-03-17 20:15:21 +00:00
of_node_put ( codec_np ) ;
2008-01-11 18:15:26 +01:00
return 0 ;
2011-08-20 09:02:01 +02:00
error_sound :
2011-09-20 15:09:00 +08:00
platform_device_put ( sound_device ) ;
2008-01-11 18:15:26 +01:00
error :
kfree ( machine_data ) ;
2011-08-20 09:02:01 +02:00
error_alloc :
of_node_put ( codec_np ) ;
2008-01-11 18:15:26 +01:00
return ret ;
}
/**
2010-03-17 20:15:21 +00:00
* mpc8610_hpcd_remove : remove the platform device
2008-01-11 18:15:26 +01:00
*
2010-03-17 20:15:21 +00:00
* This function is called when the platform device is removed .
2008-01-11 18:15:26 +01:00
*/
2010-03-17 20:15:21 +00:00
static int __devexit mpc8610_hpcd_remove ( struct platform_device * pdev )
2008-01-11 18:15:26 +01:00
{
2010-03-17 20:15:21 +00:00
struct platform_device * sound_device = dev_get_drvdata ( & pdev - > dev ) ;
struct snd_soc_card * card = platform_get_drvdata ( sound_device ) ;
2008-01-11 18:15:26 +01:00
struct mpc8610_hpcd_data * machine_data =
2010-03-17 20:15:21 +00:00
container_of ( card , struct mpc8610_hpcd_data , card ) ;
2008-01-11 18:15:26 +01:00
platform_device_unregister ( sound_device ) ;
kfree ( machine_data ) ;
sound_device - > dev . platform_data = NULL ;
2010-03-17 20:15:21 +00:00
dev_set_drvdata ( & pdev - > dev , NULL ) ;
2008-01-11 18:15:26 +01:00
return 0 ;
}
2010-03-17 20:15:21 +00:00
static struct platform_driver mpc8610_hpcd_driver = {
. probe = mpc8610_hpcd_probe ,
. remove = __devexit_p ( mpc8610_hpcd_remove ) ,
2010-04-13 16:13:02 -07:00
. driver = {
2012-03-09 00:59:46 +08:00
/* The name must match 'compatible' property in the device tree,
2010-03-17 20:15:21 +00:00
* in lowercase letters .
*/
. name = " snd-soc-mpc8610hpcd " ,
2010-04-13 16:13:02 -07:00
. owner = THIS_MODULE ,
} ,
2008-01-11 18:15:26 +01:00
} ;
/**
2010-03-17 20:15:21 +00:00
* mpc8610_hpcd_init : machine driver initialization .
2008-01-11 18:15:26 +01:00
*
* This function is called when this module is loaded .
*/
static int __init mpc8610_hpcd_init ( void )
{
2010-03-17 20:15:21 +00:00
struct device_node * guts_np ;
struct resource res ;
2008-01-11 18:15:26 +01:00
2010-03-17 20:15:21 +00:00
pr_info ( " Freescale MPC8610 HPCD ALSA SoC machine driver \n " ) ;
2008-01-11 18:15:26 +01:00
2010-03-17 20:15:21 +00:00
/* Get the physical address of the global utilities registers */
guts_np = of_find_compatible_node ( NULL , NULL , " fsl,mpc8610-guts " ) ;
if ( of_address_to_resource ( guts_np , 0 , & res ) ) {
pr_err ( " mpc8610-hpcd: missing/invalid global utilities node \n " ) ;
return - EINVAL ;
}
guts_phys = res . start ;
2008-01-11 18:15:26 +01:00
2010-03-17 20:15:21 +00:00
return platform_driver_register ( & mpc8610_hpcd_driver ) ;
2008-01-11 18:15:26 +01:00
}
/**
2010-03-17 20:15:21 +00:00
* mpc8610_hpcd_exit : machine driver exit
2008-01-11 18:15:26 +01:00
*
* This function is called when this driver is unloaded .
*/
static void __exit mpc8610_hpcd_exit ( void )
{
2010-03-17 20:15:21 +00:00
platform_driver_unregister ( & mpc8610_hpcd_driver ) ;
2008-01-11 18:15:26 +01:00
}
module_init ( mpc8610_hpcd_init ) ;
module_exit ( mpc8610_hpcd_exit ) ;
MODULE_AUTHOR ( " Timur Tabi <timur@freescale.com> " ) ;
2010-03-17 20:15:21 +00:00
MODULE_DESCRIPTION ( " Freescale MPC8610 HPCD ALSA SoC machine driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;