162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Freescale MPC8610HPCD ALSA SoC Machine driver 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Author: Timur Tabi <timur@freescale.com> 662306a36Sopenharmony_ci// 762306a36Sopenharmony_ci// Copyright 2007-2010 Freescale Semiconductor, Inc. 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/interrupt.h> 1162306a36Sopenharmony_ci#include <linux/fsl/guts.h> 1262306a36Sopenharmony_ci#include <linux/of_address.h> 1362306a36Sopenharmony_ci#include <linux/of_device.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <sound/soc.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "fsl_dma.h" 1862306a36Sopenharmony_ci#include "fsl_ssi.h" 1962306a36Sopenharmony_ci#include "fsl_utils.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* There's only one global utilities register */ 2262306a36Sopenharmony_cistatic phys_addr_t guts_phys; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/** 2562306a36Sopenharmony_ci * mpc8610_hpcd_data: machine-specific ASoC device data 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * This structure contains data for a single sound platform device on an 2862306a36Sopenharmony_ci * MPC8610 HPCD. Some of the data is taken from the device tree. 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_cistruct mpc8610_hpcd_data { 3162306a36Sopenharmony_ci struct snd_soc_dai_link dai[2]; 3262306a36Sopenharmony_ci struct snd_soc_card card; 3362306a36Sopenharmony_ci unsigned int dai_format; 3462306a36Sopenharmony_ci unsigned int codec_clk_direction; 3562306a36Sopenharmony_ci unsigned int cpu_clk_direction; 3662306a36Sopenharmony_ci unsigned int clk_frequency; 3762306a36Sopenharmony_ci unsigned int ssi_id; /* 0 = SSI1, 1 = SSI2, etc */ 3862306a36Sopenharmony_ci unsigned int dma_id[2]; /* 0 = DMA1, 1 = DMA2, etc */ 3962306a36Sopenharmony_ci unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/ 4062306a36Sopenharmony_ci char codec_dai_name[DAI_NAME_SIZE]; 4162306a36Sopenharmony_ci char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */ 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/** 4562306a36Sopenharmony_ci * mpc8610_hpcd_machine_probe: initialize the board 4662306a36Sopenharmony_ci * 4762306a36Sopenharmony_ci * This function is used to initialize the board-specific hardware. 4862306a36Sopenharmony_ci * 4962306a36Sopenharmony_ci * Here we program the DMACR and PMUXCR registers. 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_cistatic int mpc8610_hpcd_machine_probe(struct snd_soc_card *card) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci struct mpc8610_hpcd_data *machine_data = 5462306a36Sopenharmony_ci container_of(card, struct mpc8610_hpcd_data, card); 5562306a36Sopenharmony_ci struct ccsr_guts __iomem *guts; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); 5862306a36Sopenharmony_ci if (!guts) { 5962306a36Sopenharmony_ci dev_err(card->dev, "could not map global utilities\n"); 6062306a36Sopenharmony_ci return -ENOMEM; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci /* Program the signal routing between the SSI and the DMA */ 6462306a36Sopenharmony_ci guts_set_dmacr(guts, machine_data->dma_id[0], 6562306a36Sopenharmony_ci machine_data->dma_channel_id[0], 6662306a36Sopenharmony_ci CCSR_GUTS_DMACR_DEV_SSI); 6762306a36Sopenharmony_ci guts_set_dmacr(guts, machine_data->dma_id[1], 6862306a36Sopenharmony_ci machine_data->dma_channel_id[1], 6962306a36Sopenharmony_ci CCSR_GUTS_DMACR_DEV_SSI); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci guts_set_pmuxcr_dma(guts, machine_data->dma_id[0], 7262306a36Sopenharmony_ci machine_data->dma_channel_id[0], 0); 7362306a36Sopenharmony_ci guts_set_pmuxcr_dma(guts, machine_data->dma_id[1], 7462306a36Sopenharmony_ci machine_data->dma_channel_id[1], 0); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci switch (machine_data->ssi_id) { 7762306a36Sopenharmony_ci case 0: 7862306a36Sopenharmony_ci clrsetbits_be32(&guts->pmuxcr, 7962306a36Sopenharmony_ci CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_SSI); 8062306a36Sopenharmony_ci break; 8162306a36Sopenharmony_ci case 1: 8262306a36Sopenharmony_ci clrsetbits_be32(&guts->pmuxcr, 8362306a36Sopenharmony_ci CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_SSI); 8462306a36Sopenharmony_ci break; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci iounmap(guts); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci return 0; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/** 9362306a36Sopenharmony_ci * mpc8610_hpcd_startup: program the board with various hardware parameters 9462306a36Sopenharmony_ci * 9562306a36Sopenharmony_ci * This function takes board-specific information, like clock frequencies 9662306a36Sopenharmony_ci * and serial data formats, and passes that information to the codec and 9762306a36Sopenharmony_ci * transport drivers. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_cistatic int mpc8610_hpcd_startup(struct snd_pcm_substream *substream) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 10262306a36Sopenharmony_ci struct mpc8610_hpcd_data *machine_data = 10362306a36Sopenharmony_ci container_of(rtd->card, struct mpc8610_hpcd_data, card); 10462306a36Sopenharmony_ci struct device *dev = rtd->card->dev; 10562306a36Sopenharmony_ci int ret = 0; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* Tell the codec driver what the serial protocol is. */ 10862306a36Sopenharmony_ci ret = snd_soc_dai_set_fmt(asoc_rtd_to_codec(rtd, 0), machine_data->dai_format); 10962306a36Sopenharmony_ci if (ret < 0) { 11062306a36Sopenharmony_ci dev_err(dev, "could not set codec driver audio format\n"); 11162306a36Sopenharmony_ci return ret; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* 11562306a36Sopenharmony_ci * Tell the codec driver what the MCLK frequency is, and whether it's 11662306a36Sopenharmony_ci * a slave or master. 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_ci ret = snd_soc_dai_set_sysclk(asoc_rtd_to_codec(rtd, 0), 0, 11962306a36Sopenharmony_ci machine_data->clk_frequency, 12062306a36Sopenharmony_ci machine_data->codec_clk_direction); 12162306a36Sopenharmony_ci if (ret < 0) { 12262306a36Sopenharmony_ci dev_err(dev, "could not set codec driver clock params\n"); 12362306a36Sopenharmony_ci return ret; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci return 0; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/** 13062306a36Sopenharmony_ci * mpc8610_hpcd_machine_remove: Remove the sound device 13162306a36Sopenharmony_ci * 13262306a36Sopenharmony_ci * This function is called to remove the sound device for one SSI. We 13362306a36Sopenharmony_ci * de-program the DMACR and PMUXCR register. 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_cistatic int mpc8610_hpcd_machine_remove(struct snd_soc_card *card) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct mpc8610_hpcd_data *machine_data = 13862306a36Sopenharmony_ci container_of(card, struct mpc8610_hpcd_data, card); 13962306a36Sopenharmony_ci struct ccsr_guts __iomem *guts; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); 14262306a36Sopenharmony_ci if (!guts) { 14362306a36Sopenharmony_ci dev_err(card->dev, "could not map global utilities\n"); 14462306a36Sopenharmony_ci return -ENOMEM; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /* Restore the signal routing */ 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci guts_set_dmacr(guts, machine_data->dma_id[0], 15062306a36Sopenharmony_ci machine_data->dma_channel_id[0], 0); 15162306a36Sopenharmony_ci guts_set_dmacr(guts, machine_data->dma_id[1], 15262306a36Sopenharmony_ci machine_data->dma_channel_id[1], 0); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci switch (machine_data->ssi_id) { 15562306a36Sopenharmony_ci case 0: 15662306a36Sopenharmony_ci clrsetbits_be32(&guts->pmuxcr, 15762306a36Sopenharmony_ci CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_LA); 15862306a36Sopenharmony_ci break; 15962306a36Sopenharmony_ci case 1: 16062306a36Sopenharmony_ci clrsetbits_be32(&guts->pmuxcr, 16162306a36Sopenharmony_ci CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_LA); 16262306a36Sopenharmony_ci break; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci iounmap(guts); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return 0; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci/** 17162306a36Sopenharmony_ci * mpc8610_hpcd_ops: ASoC machine driver operations 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_cistatic const struct snd_soc_ops mpc8610_hpcd_ops = { 17462306a36Sopenharmony_ci .startup = mpc8610_hpcd_startup, 17562306a36Sopenharmony_ci}; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci/** 17862306a36Sopenharmony_ci * mpc8610_hpcd_probe: platform probe function for the machine driver 17962306a36Sopenharmony_ci * 18062306a36Sopenharmony_ci * Although this is a machine driver, the SSI node is the "master" node with 18162306a36Sopenharmony_ci * respect to audio hardware connections. Therefore, we create a new ASoC 18262306a36Sopenharmony_ci * device for each new SSI node that has a codec attached. 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_cistatic int mpc8610_hpcd_probe(struct platform_device *pdev) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct device *dev = pdev->dev.parent; 18762306a36Sopenharmony_ci /* ssi_pdev is the platform device for the SSI node that probed us */ 18862306a36Sopenharmony_ci struct platform_device *ssi_pdev = to_platform_device(dev); 18962306a36Sopenharmony_ci struct device_node *np = ssi_pdev->dev.of_node; 19062306a36Sopenharmony_ci struct device_node *codec_np = NULL; 19162306a36Sopenharmony_ci struct mpc8610_hpcd_data *machine_data; 19262306a36Sopenharmony_ci struct snd_soc_dai_link_component *comp; 19362306a36Sopenharmony_ci int ret; 19462306a36Sopenharmony_ci const char *sprop; 19562306a36Sopenharmony_ci const u32 *iprop; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* Find the codec node for this SSI. */ 19862306a36Sopenharmony_ci codec_np = of_parse_phandle(np, "codec-handle", 0); 19962306a36Sopenharmony_ci if (!codec_np) { 20062306a36Sopenharmony_ci dev_err(dev, "invalid codec node\n"); 20162306a36Sopenharmony_ci return -EINVAL; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci machine_data = kzalloc(sizeof(struct mpc8610_hpcd_data), GFP_KERNEL); 20562306a36Sopenharmony_ci if (!machine_data) { 20662306a36Sopenharmony_ci ret = -ENOMEM; 20762306a36Sopenharmony_ci goto error_alloc; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci comp = devm_kzalloc(&pdev->dev, 6 * sizeof(*comp), GFP_KERNEL); 21162306a36Sopenharmony_ci if (!comp) { 21262306a36Sopenharmony_ci ret = -ENOMEM; 21362306a36Sopenharmony_ci goto error_alloc; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci machine_data->dai[0].cpus = &comp[0]; 21762306a36Sopenharmony_ci machine_data->dai[0].codecs = &comp[1]; 21862306a36Sopenharmony_ci machine_data->dai[0].platforms = &comp[2]; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci machine_data->dai[0].num_cpus = 1; 22162306a36Sopenharmony_ci machine_data->dai[0].num_codecs = 1; 22262306a36Sopenharmony_ci machine_data->dai[0].num_platforms = 1; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci machine_data->dai[1].cpus = &comp[3]; 22562306a36Sopenharmony_ci machine_data->dai[1].codecs = &comp[4]; 22662306a36Sopenharmony_ci machine_data->dai[1].platforms = &comp[5]; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci machine_data->dai[1].num_cpus = 1; 22962306a36Sopenharmony_ci machine_data->dai[1].num_codecs = 1; 23062306a36Sopenharmony_ci machine_data->dai[1].num_platforms = 1; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci machine_data->dai[0].cpus->dai_name = dev_name(&ssi_pdev->dev); 23362306a36Sopenharmony_ci machine_data->dai[0].ops = &mpc8610_hpcd_ops; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* ASoC core can match codec with device node */ 23662306a36Sopenharmony_ci machine_data->dai[0].codecs->of_node = codec_np; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* The DAI name from the codec (snd_soc_dai_driver.name) */ 23962306a36Sopenharmony_ci machine_data->dai[0].codecs->dai_name = "cs4270-hifi"; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* We register two DAIs per SSI, one for playback and the other for 24262306a36Sopenharmony_ci * capture. Currently, we only support codecs that have one DAI for 24362306a36Sopenharmony_ci * both playback and capture. 24462306a36Sopenharmony_ci */ 24562306a36Sopenharmony_ci memcpy(&machine_data->dai[1], &machine_data->dai[0], 24662306a36Sopenharmony_ci sizeof(struct snd_soc_dai_link)); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* Get the device ID */ 24962306a36Sopenharmony_ci iprop = of_get_property(np, "cell-index", NULL); 25062306a36Sopenharmony_ci if (!iprop) { 25162306a36Sopenharmony_ci dev_err(&pdev->dev, "cell-index property not found\n"); 25262306a36Sopenharmony_ci ret = -EINVAL; 25362306a36Sopenharmony_ci goto error; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci machine_data->ssi_id = be32_to_cpup(iprop); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* Get the serial format and clock direction. */ 25862306a36Sopenharmony_ci sprop = of_get_property(np, "fsl,mode", NULL); 25962306a36Sopenharmony_ci if (!sprop) { 26062306a36Sopenharmony_ci dev_err(&pdev->dev, "fsl,mode property not found\n"); 26162306a36Sopenharmony_ci ret = -EINVAL; 26262306a36Sopenharmony_ci goto error; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (strcasecmp(sprop, "i2s-slave") == 0) { 26662306a36Sopenharmony_ci machine_data->dai_format = 26762306a36Sopenharmony_ci SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBP_CFP; 26862306a36Sopenharmony_ci machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT; 26962306a36Sopenharmony_ci machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* In i2s-slave mode, the codec has its own clock source, so we 27262306a36Sopenharmony_ci * need to get the frequency from the device tree and pass it to 27362306a36Sopenharmony_ci * the codec driver. 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_ci iprop = of_get_property(codec_np, "clock-frequency", NULL); 27662306a36Sopenharmony_ci if (!iprop || !*iprop) { 27762306a36Sopenharmony_ci dev_err(&pdev->dev, "codec bus-frequency " 27862306a36Sopenharmony_ci "property is missing or invalid\n"); 27962306a36Sopenharmony_ci ret = -EINVAL; 28062306a36Sopenharmony_ci goto error; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci machine_data->clk_frequency = be32_to_cpup(iprop); 28362306a36Sopenharmony_ci } else if (strcasecmp(sprop, "i2s-master") == 0) { 28462306a36Sopenharmony_ci machine_data->dai_format = 28562306a36Sopenharmony_ci SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC; 28662306a36Sopenharmony_ci machine_data->codec_clk_direction = SND_SOC_CLOCK_IN; 28762306a36Sopenharmony_ci machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT; 28862306a36Sopenharmony_ci } else if (strcasecmp(sprop, "lj-slave") == 0) { 28962306a36Sopenharmony_ci machine_data->dai_format = 29062306a36Sopenharmony_ci SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBP_CFP; 29162306a36Sopenharmony_ci machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT; 29262306a36Sopenharmony_ci machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN; 29362306a36Sopenharmony_ci } else if (strcasecmp(sprop, "lj-master") == 0) { 29462306a36Sopenharmony_ci machine_data->dai_format = 29562306a36Sopenharmony_ci SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBC_CFC; 29662306a36Sopenharmony_ci machine_data->codec_clk_direction = SND_SOC_CLOCK_IN; 29762306a36Sopenharmony_ci machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT; 29862306a36Sopenharmony_ci } else if (strcasecmp(sprop, "rj-slave") == 0) { 29962306a36Sopenharmony_ci machine_data->dai_format = 30062306a36Sopenharmony_ci SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_CBP_CFP; 30162306a36Sopenharmony_ci machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT; 30262306a36Sopenharmony_ci machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN; 30362306a36Sopenharmony_ci } else if (strcasecmp(sprop, "rj-master") == 0) { 30462306a36Sopenharmony_ci machine_data->dai_format = 30562306a36Sopenharmony_ci SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_CBC_CFC; 30662306a36Sopenharmony_ci machine_data->codec_clk_direction = SND_SOC_CLOCK_IN; 30762306a36Sopenharmony_ci machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT; 30862306a36Sopenharmony_ci } else if (strcasecmp(sprop, "ac97-slave") == 0) { 30962306a36Sopenharmony_ci machine_data->dai_format = 31062306a36Sopenharmony_ci SND_SOC_DAIFMT_AC97 | SND_SOC_DAIFMT_CBP_CFP; 31162306a36Sopenharmony_ci machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT; 31262306a36Sopenharmony_ci machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN; 31362306a36Sopenharmony_ci } else if (strcasecmp(sprop, "ac97-master") == 0) { 31462306a36Sopenharmony_ci machine_data->dai_format = 31562306a36Sopenharmony_ci SND_SOC_DAIFMT_AC97 | SND_SOC_DAIFMT_CBC_CFC; 31662306a36Sopenharmony_ci machine_data->codec_clk_direction = SND_SOC_CLOCK_IN; 31762306a36Sopenharmony_ci machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT; 31862306a36Sopenharmony_ci } else { 31962306a36Sopenharmony_ci dev_err(&pdev->dev, 32062306a36Sopenharmony_ci "unrecognized fsl,mode property '%s'\n", sprop); 32162306a36Sopenharmony_ci ret = -EINVAL; 32262306a36Sopenharmony_ci goto error; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (!machine_data->clk_frequency) { 32662306a36Sopenharmony_ci dev_err(&pdev->dev, "unknown clock frequency\n"); 32762306a36Sopenharmony_ci ret = -EINVAL; 32862306a36Sopenharmony_ci goto error; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* Find the playback DMA channel to use. */ 33262306a36Sopenharmony_ci machine_data->dai[0].platforms->name = machine_data->platform_name[0]; 33362306a36Sopenharmony_ci ret = fsl_asoc_get_dma_channel(np, "fsl,playback-dma", 33462306a36Sopenharmony_ci &machine_data->dai[0], 33562306a36Sopenharmony_ci &machine_data->dma_channel_id[0], 33662306a36Sopenharmony_ci &machine_data->dma_id[0]); 33762306a36Sopenharmony_ci if (ret) { 33862306a36Sopenharmony_ci dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n"); 33962306a36Sopenharmony_ci goto error; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci /* Find the capture DMA channel to use. */ 34362306a36Sopenharmony_ci machine_data->dai[1].platforms->name = machine_data->platform_name[1]; 34462306a36Sopenharmony_ci ret = fsl_asoc_get_dma_channel(np, "fsl,capture-dma", 34562306a36Sopenharmony_ci &machine_data->dai[1], 34662306a36Sopenharmony_ci &machine_data->dma_channel_id[1], 34762306a36Sopenharmony_ci &machine_data->dma_id[1]); 34862306a36Sopenharmony_ci if (ret) { 34962306a36Sopenharmony_ci dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n"); 35062306a36Sopenharmony_ci goto error; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci /* Initialize our DAI data structure. */ 35462306a36Sopenharmony_ci machine_data->dai[0].stream_name = "playback"; 35562306a36Sopenharmony_ci machine_data->dai[1].stream_name = "capture"; 35662306a36Sopenharmony_ci machine_data->dai[0].name = machine_data->dai[0].stream_name; 35762306a36Sopenharmony_ci machine_data->dai[1].name = machine_data->dai[1].stream_name; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci machine_data->card.probe = mpc8610_hpcd_machine_probe; 36062306a36Sopenharmony_ci machine_data->card.remove = mpc8610_hpcd_machine_remove; 36162306a36Sopenharmony_ci machine_data->card.name = pdev->name; /* The platform driver name */ 36262306a36Sopenharmony_ci machine_data->card.owner = THIS_MODULE; 36362306a36Sopenharmony_ci machine_data->card.dev = &pdev->dev; 36462306a36Sopenharmony_ci machine_data->card.num_links = 2; 36562306a36Sopenharmony_ci machine_data->card.dai_link = machine_data->dai; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* Register with ASoC */ 36862306a36Sopenharmony_ci ret = snd_soc_register_card(&machine_data->card); 36962306a36Sopenharmony_ci if (ret) { 37062306a36Sopenharmony_ci dev_err(&pdev->dev, "could not register card\n"); 37162306a36Sopenharmony_ci goto error; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci of_node_put(codec_np); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci return 0; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cierror: 37962306a36Sopenharmony_ci kfree(machine_data); 38062306a36Sopenharmony_cierror_alloc: 38162306a36Sopenharmony_ci of_node_put(codec_np); 38262306a36Sopenharmony_ci return ret; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci/** 38662306a36Sopenharmony_ci * mpc8610_hpcd_remove: remove the platform device 38762306a36Sopenharmony_ci * 38862306a36Sopenharmony_ci * This function is called when the platform device is removed. 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_cistatic void mpc8610_hpcd_remove(struct platform_device *pdev) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci struct snd_soc_card *card = platform_get_drvdata(pdev); 39362306a36Sopenharmony_ci struct mpc8610_hpcd_data *machine_data = 39462306a36Sopenharmony_ci container_of(card, struct mpc8610_hpcd_data, card); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci snd_soc_unregister_card(card); 39762306a36Sopenharmony_ci kfree(machine_data); 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic struct platform_driver mpc8610_hpcd_driver = { 40162306a36Sopenharmony_ci .probe = mpc8610_hpcd_probe, 40262306a36Sopenharmony_ci .remove_new = mpc8610_hpcd_remove, 40362306a36Sopenharmony_ci .driver = { 40462306a36Sopenharmony_ci /* The name must match 'compatible' property in the device tree, 40562306a36Sopenharmony_ci * in lowercase letters. 40662306a36Sopenharmony_ci */ 40762306a36Sopenharmony_ci .name = "snd-soc-mpc8610hpcd", 40862306a36Sopenharmony_ci }, 40962306a36Sopenharmony_ci}; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci/** 41262306a36Sopenharmony_ci * mpc8610_hpcd_init: machine driver initialization. 41362306a36Sopenharmony_ci * 41462306a36Sopenharmony_ci * This function is called when this module is loaded. 41562306a36Sopenharmony_ci */ 41662306a36Sopenharmony_cistatic int __init mpc8610_hpcd_init(void) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci struct device_node *guts_np; 41962306a36Sopenharmony_ci struct resource res; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci pr_info("Freescale MPC8610 HPCD ALSA SoC machine driver\n"); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* Get the physical address of the global utilities registers */ 42462306a36Sopenharmony_ci guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts"); 42562306a36Sopenharmony_ci if (of_address_to_resource(guts_np, 0, &res)) { 42662306a36Sopenharmony_ci pr_err("mpc8610-hpcd: missing/invalid global utilities node\n"); 42762306a36Sopenharmony_ci of_node_put(guts_np); 42862306a36Sopenharmony_ci return -EINVAL; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci guts_phys = res.start; 43162306a36Sopenharmony_ci of_node_put(guts_np); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci return platform_driver_register(&mpc8610_hpcd_driver); 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci/** 43762306a36Sopenharmony_ci * mpc8610_hpcd_exit: machine driver exit 43862306a36Sopenharmony_ci * 43962306a36Sopenharmony_ci * This function is called when this driver is unloaded. 44062306a36Sopenharmony_ci */ 44162306a36Sopenharmony_cistatic void __exit mpc8610_hpcd_exit(void) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci platform_driver_unregister(&mpc8610_hpcd_driver); 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cimodule_init(mpc8610_hpcd_init); 44762306a36Sopenharmony_cimodule_exit(mpc8610_hpcd_exit); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ciMODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); 45062306a36Sopenharmony_ciMODULE_DESCRIPTION("Freescale MPC8610 HPCD ALSA SoC machine driver"); 45162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 452