/* * ASoC SA11x0 SSP DAI driver * * Copyright (C) 2012 Russell King * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * The SA11x0 SSP interface gives us four signals: TXD, RXD, SFRM and SCLK, * and SFRM pulses for each and every word transmitted. These signals can * be routed to GPIO10(TXD), GPIO11(RXD), GPIO12(SCLK), and GPIO13(SFRM) * when the PPAR_SPR bit it set, along with the appropriate GAFR and GPDR * configuration. If PPAR_SPR is clear, the SSP shares the same pins with * the MCP. */ #include #include #include #include #include #include #include #include #include #include #include #include "ssp.h" #define SA11X0_SSP_CHANNELS_MIN 1 #define SA11X0_SSP_CHANNELS_MAX 8 /* * This isn't really up to us - it depends how the board implements the * clocking, whether the board uses the on-board clock source, or whether * the SSP is clocked via GPIO19. */ #define SA11X0_SSP_RATES \ (SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_CONTINUOUS) #define SA11X0_SSP_FORMATS \ (SNDRV_PCM_FORMAT_S8 | SNDRV_PCM_FORMAT_U8 | \ SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_U16_LE) #define BUFFER_SIZE_MAX 65536 #define PERIOD_SIZE_MIN 32 #define PERIOD_SIZE_MAX 16384 #define PERIODS_MIN 2 #define PERIODS_MAX 256 #define SSCR0 0x60 #define SSCR0_DSS (15 << 0) /* s */ #define SSCR0_FRF_MOT (0 << 4) #define SSCR0_FRF_TI (1 << 4) #define SSCR0_FRF_NAT (2 << 4) #define SSCR0_SSE (1 << 7) /* s */ #define SSCR0_SCR (0xff << 8) /* s */ #define SSCR1 0x64 #define SSCR1_RIE (1 << 0) #define SSCR1_TIE (1 << 1) #define SSCR1_LBM (1 << 2) #define SSCR1_SPO (1 << 3) #define SSCR1_SP (1 << 4) #define SSCR1_ECS (1 << 5) /* s */ #define SSDR 0x6c #define SSSR 0x74 struct ssp_priv { void __iomem *base; unsigned long clk_hz; u32 cr0; u32 cr1; struct snd_dmaengine_dai_dma_data tx; struct snd_dmaengine_dai_dma_data rx; }; static const struct snd_pcm_hardware sa11x0_ssp_pcm_hardware = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, .period_bytes_min = PERIOD_SIZE_MIN, .period_bytes_max = PERIOD_SIZE_MAX, .periods_min = PERIODS_MIN, .periods_max = PERIODS_MAX, .buffer_bytes_max = BUFFER_SIZE_MAX, /* This is for playback only */ .fifo_size = 8 * 2, }; /* * Constrain the rate according to the bitrate(s) that the interface can * do, which is determined by its input clock and the range of divisors * that we can program (1 to 256 with an initial /2). */ static int sa11x0_ssp_rate_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_interval *p = hw_param_interval(params, rule->var); struct ssp_priv *ssp = rule->private; snd_pcm_format_t fmt = params_format(params); struct snd_ratnum ratnum; ratnum.num = ssp->clk_hz / (2 * snd_pcm_format_width(fmt) * params_channels(params)); ratnum.den_min = 1; ratnum.den_max = 256; ratnum.den_step = 1; return snd_interval_ratnum(p, 1, &ratnum, ¶ms->rate_num, ¶ms->rate_den); } static int sa11x0_ssp_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct ssp_priv *ssp = snd_soc_dai_get_drvdata(dai); int ret; ret = snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); if (ret) return ret; ret = snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); if (ret) return ret; ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, sa11x0_ssp_rate_constraint, ssp, SNDRV_PCM_HW_PARAM_RATE, SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (ret) return ret; if (!dai->active) { writel_relaxed(ssp->cr0, ssp->base + SSCR0); writel_relaxed(ssp->cr1, ssp->base + SSCR1); ssp->cr0 |= SSCR0_SSE; writel_relaxed(ssp->cr0, ssp->base + SSCR0); } return 0; } static void sa11x0_ssp_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { if (!dai->active) { struct ssp_priv *ssp = snd_soc_dai_get_drvdata(dai); ssp->cr0 &= ~SSCR0_SSE; writel_relaxed(ssp->cr0, ssp->base + SSCR0); } } static int sa11x0_ssp_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct ssp_priv *ssp = snd_soc_dai_get_drvdata(dai); snd_pcm_format_t fmt = params_format(params); int width = snd_pcm_format_width(fmt); unsigned int divisor; u32 cr0; if (width < 0) return width; if (width < 4 || width > 16) return -EINVAL; divisor = ssp->clk_hz / (2 * width * params_channels(params) * params_rate(params)); if (divisor == 0) return -EINVAL; /* Set the bit-width for the format and the divisor */ cr0 = ssp->cr0 & ~(SSCR0_DSS | SSCR0_SCR); /* rate_den is calculated by snd_interval_ratnum() above. */ cr0 |= (width - 1) | (divisor - 1) << 8; if (cr0 != ssp->cr0) { ssp->cr0 = cr0; writel_relaxed(cr0, ssp->base + SSCR0); } writel_relaxed(ssp->cr1, ssp->base + SSCR1); return 0; } static int sa11x0_ssp_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { struct ssp_priv *ssp = snd_soc_dai_get_drvdata(dai); switch (clk_id) { case SA11X0_SSP_CLK_INT: ssp->cr1 &= ~SSCR1_ECS; ssp->clk_hz = 3686400; break; case SA11X0_SSP_CLK_EXT: /* * The SA11x0 developer's manual section 11.12.10.6 says the * external clock can be up to 3.6864MHz, but iPAQs supply * this with up to a 12.288MHz clock, which appears to work. * We trust that the frequency is correct. */ if (dir != SND_SOC_CLOCK_IN) return -EINVAL; ssp->cr1 |= SSCR1_ECS; ssp->clk_hz = freq; break; default: return -EINVAL; } return 0; } static int sa11x0_ssp_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct ssp_priv *ssp = snd_soc_dai_get_drvdata(dai); /* We always generate the clock and frm signals */ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) return -EINVAL; switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: ssp->cr1 &= ~SSCR1_SPO; break; case SND_SOC_DAIFMT_IB_NF: ssp->cr1 |= SSCR1_SPO; break; default: return -EINVAL; } return 0; } static struct snd_soc_dai_ops sa11x0_ssp_ops = { .startup = sa11x0_ssp_startup, .shutdown = sa11x0_ssp_shutdown, .hw_params = sa11x0_ssp_hw_params, .set_sysclk = sa11x0_ssp_set_sysclk, .set_fmt = sa11x0_ssp_set_fmt, }; static int sa11x0_ssp_probe(struct snd_soc_dai *dai) { struct ssp_priv *ssp = snd_soc_dai_get_drvdata(dai); /* Default to TI mode and a bit-width of 16 */ ssp->cr0 = SSCR0_FRF_TI | 15; /* * Set the DMA data now - it's needed for the dmaengine backend to * obtain its DMA channel, in turn its struct device, and therefore * a struct device to allocate DMA memory against. */ dai->playback_dma_data = &ssp->tx; dai->capture_dma_data = &ssp->rx; return 0; } static int sa11x0_ssp_remove(struct snd_soc_dai *dai) { return 0; } #ifdef CONFIG_PM static int sa11x0_ssp_suspend(struct snd_soc_dai *dai) { struct ssp_priv *ssp = snd_soc_dai_get_drvdata(dai); writel_relaxed(ssp->cr0 & ~SSCR0_SSE, ssp->base + SSCR0); return 0; } static int sa11x0_ssp_resume(struct snd_soc_dai *dai) { struct ssp_priv *ssp = snd_soc_dai_get_drvdata(dai); writel_relaxed(ssp->cr0 & ~SSCR0_SSE, ssp->base + SSCR0); writel_relaxed(ssp->cr1, ssp->base + SSCR1); if (ssp->cr0 & SSCR0_SSE) writel_relaxed(ssp->cr0, ssp->base + SSCR0); return 0; } #else #define sa11x0_ssp_suspend NULL #define sa11x0_ssp_resume NULL #endif static struct snd_soc_dai_driver sa11x0_ssp_driver = { .probe = sa11x0_ssp_probe, .remove = sa11x0_ssp_remove, .suspend = sa11x0_ssp_suspend, .resume = sa11x0_ssp_resume, .ops = &sa11x0_ssp_ops, .capture = { .channels_min = SA11X0_SSP_CHANNELS_MIN, .channels_max = SA11X0_SSP_CHANNELS_MAX, .rates = SA11X0_SSP_RATES, .formats = SA11X0_SSP_FORMATS, }, .playback = { .channels_min = SA11X0_SSP_CHANNELS_MIN, .channels_max = SA11X0_SSP_CHANNELS_MAX, .rates = SA11X0_SSP_RATES, .formats = SA11X0_SSP_FORMATS, }, .symmetric_rates = 1, }; static const struct snd_soc_component_driver sa11x0_ssp_component = { .name = "sa11x0-ssp", }; static const struct snd_dmaengine_pcm_config sa11x0_ssp_pcm_config = { .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, .pcm_hardware = &sa11x0_ssp_pcm_hardware, .prealloc_buffer_size = SZ_64K, }; static int sa11x0_ssp_plat_probe(struct platform_device *pdev) { struct ssp_priv *ssp; struct resource *res; int ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -EINVAL; ssp = devm_kzalloc(&pdev->dev, sizeof(*ssp), GFP_KERNEL); if (!ssp) return -ENOMEM; /* Assume internal clock, as that's what we program initially */ ssp->clk_hz = 3686400; /* * FIXME: The generic ASoC dmaengine backend does not allow us to set * a different FIFO size for the transmit and receive sides of the * SSP: the transmit has a 8 x 16bit FIFO, but the receive side has * a 12 x 16bit FIFO. */ ssp->tx.addr = res->start + SSDR; ssp->tx.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; ssp->tx.maxburst = 4; ssp->tx.filter_data = "Ser4SSPTr"; ssp->rx.addr = res->start + SSDR; ssp->rx.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; ssp->rx.maxburst = 4; ssp->rx.filter_data = "Ser4SSPRc"; ssp->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(ssp->base)) return PTR_ERR(ssp->base); writel_relaxed(ssp->cr0, ssp->base + SSCR0); writel_relaxed(ssp->cr1, ssp->base + SSCR1); platform_set_drvdata(pdev, ssp); ret = snd_dmaengine_pcm_register(&pdev->dev, &sa11x0_ssp_pcm_config, SND_DMAENGINE_PCM_FLAG_COMPAT); if (ret) return ret; ret = snd_soc_register_component(&pdev->dev, &sa11x0_ssp_component, &sa11x0_ssp_driver, 1); if (ret) snd_dmaengine_pcm_unregister(&pdev->dev); return ret; } static int sa11x0_ssp_plat_remove(struct platform_device *pdev) { snd_soc_unregister_component(&pdev->dev); snd_dmaengine_pcm_unregister(&pdev->dev); return 0; } static struct platform_driver sa11x0_ssp_plat_driver = { .driver = { .name = "sa11x0-ssp", .owner = THIS_MODULE, }, .probe = sa11x0_ssp_plat_probe, .remove = sa11x0_ssp_plat_remove, }; module_platform_driver(sa11x0_ssp_plat_driver); MODULE_AUTHOR("Russell King "); MODULE_DESCRIPTION("SA11x0 SSP/PCM SoC Interface"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:sa11x0-ssp");