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