404 lines · c
1// SPDX-License-Identifier: GPL-2.02//3// Serial Sound Interface (I2S) support for SH7760/SH77804//5// Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>6//7// dont forget to set IPSEL/OMSEL register bits (in your board code) to8// enable SSI output pins!9 10/*11 * LIMITATIONS:12 * The SSI unit has only one physical data line, so full duplex is13 * impossible. This can be remedied on the SH7760 by using the14 * other SSI unit for recording; however the SH7780 has only 1 SSI15 * unit, and its pins are shared with the AC97 unit, among others.16 *17 * FEATURES:18 * The SSI features "compressed mode": in this mode it continuously19 * streams PCM data over the I2S lines and uses LRCK as a handshake20 * signal. Can be used to send compressed data (AC3/DTS) to a DSP.21 * The number of bits sent over the wire in a frame can be adjusted22 * and can be independent from the actual sample bit depth. This is23 * useful to support TDM mode codecs like the AD1939 which have a24 * fixed TDM slot size, regardless of sample resolution.25 */26 27#include <linux/init.h>28#include <linux/module.h>29#include <linux/platform_device.h>30#include <sound/core.h>31#include <sound/pcm.h>32#include <sound/initval.h>33#include <sound/soc.h>34#include <asm/io.h>35 36#define SSICR 0x0037#define SSISR 0x0438 39#define CR_DMAEN (1 << 28)40#define CR_CHNL_SHIFT 2241#define CR_CHNL_MASK (3 << CR_CHNL_SHIFT)42#define CR_DWL_SHIFT 1943#define CR_DWL_MASK (7 << CR_DWL_SHIFT)44#define CR_SWL_SHIFT 1645#define CR_SWL_MASK (7 << CR_SWL_SHIFT)46#define CR_SCK_MASTER (1 << 15) /* bitclock master bit */47#define CR_SWS_MASTER (1 << 14) /* wordselect master bit */48#define CR_SCKP (1 << 13) /* I2Sclock polarity */49#define CR_SWSP (1 << 12) /* LRCK polarity */50#define CR_SPDP (1 << 11)51#define CR_SDTA (1 << 10) /* i2s alignment (msb/lsb) */52#define CR_PDTA (1 << 9) /* fifo data alignment */53#define CR_DEL (1 << 8) /* delay data by 1 i2sclk */54#define CR_BREN (1 << 7) /* clock gating in burst mode */55#define CR_CKDIV_SHIFT 456#define CR_CKDIV_MASK (7 << CR_CKDIV_SHIFT) /* bitclock divider */57#define CR_MUTE (1 << 3) /* SSI mute */58#define CR_CPEN (1 << 2) /* compressed mode */59#define CR_TRMD (1 << 1) /* transmit/receive select */60#define CR_EN (1 << 0) /* enable SSI */61 62#define SSIREG(reg) (*(unsigned long *)(ssi->mmio + (reg)))63 64struct ssi_priv {65 unsigned long mmio;66 unsigned long sysclk;67 int inuse;68} ssi_cpu_data[] = {69#if defined(CONFIG_CPU_SUBTYPE_SH7760)70 {71 .mmio = 0xFE680000,72 },73 {74 .mmio = 0xFE690000,75 },76#elif defined(CONFIG_CPU_SUBTYPE_SH7780)77 {78 .mmio = 0xFFE70000,79 },80#else81#error "Unsupported SuperH SoC"82#endif83};84 85/*86 * track usage of the SSI; it is simplex-only so prevent attempts of87 * concurrent playback + capture. FIXME: any locking required?88 */89static int ssi_startup(struct snd_pcm_substream *substream,90 struct snd_soc_dai *dai)91{92 struct ssi_priv *ssi = &ssi_cpu_data[dai->id];93 if (ssi->inuse) {94 pr_debug("ssi: already in use!\n");95 return -EBUSY;96 } else97 ssi->inuse = 1;98 return 0;99}100 101static void ssi_shutdown(struct snd_pcm_substream *substream,102 struct snd_soc_dai *dai)103{104 struct ssi_priv *ssi = &ssi_cpu_data[dai->id];105 106 ssi->inuse = 0;107}108 109static int ssi_trigger(struct snd_pcm_substream *substream, int cmd,110 struct snd_soc_dai *dai)111{112 struct ssi_priv *ssi = &ssi_cpu_data[dai->id];113 114 switch (cmd) {115 case SNDRV_PCM_TRIGGER_START:116 SSIREG(SSICR) |= CR_DMAEN | CR_EN;117 break;118 case SNDRV_PCM_TRIGGER_STOP:119 SSIREG(SSICR) &= ~(CR_DMAEN | CR_EN);120 break;121 default:122 return -EINVAL;123 }124 125 return 0;126}127 128static int ssi_hw_params(struct snd_pcm_substream *substream,129 struct snd_pcm_hw_params *params,130 struct snd_soc_dai *dai)131{132 struct ssi_priv *ssi = &ssi_cpu_data[dai->id];133 unsigned long ssicr = SSIREG(SSICR);134 unsigned int bits, channels, swl, recv, i;135 136 channels = params_channels(params);137 bits = params->msbits;138 recv = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 0 : 1;139 140 pr_debug("ssi_hw_params() enter\nssicr was %08lx\n", ssicr);141 pr_debug("bits: %u channels: %u\n", bits, channels);142 143 ssicr &= ~(CR_TRMD | CR_CHNL_MASK | CR_DWL_MASK | CR_PDTA |144 CR_SWL_MASK);145 146 /* direction (send/receive) */147 if (!recv)148 ssicr |= CR_TRMD; /* transmit */149 150 /* channels */151 if ((channels < 2) || (channels > 8) || (channels & 1)) {152 pr_debug("ssi: invalid number of channels\n");153 return -EINVAL;154 }155 ssicr |= ((channels >> 1) - 1) << CR_CHNL_SHIFT;156 157 /* DATA WORD LENGTH (DWL): databits in audio sample */158 i = 0;159 switch (bits) {160 case 32: ++i;161 case 24: ++i;162 case 22: ++i;163 case 20: ++i;164 case 18: ++i;165 case 16: ++i;166 ssicr |= i << CR_DWL_SHIFT;167 case 8: break;168 default:169 pr_debug("ssi: invalid sample width\n");170 return -EINVAL;171 }172 173 /*174 * SYSTEM WORD LENGTH: size in bits of half a frame over the I2S175 * wires. This is usually bits_per_sample x channels/2; i.e. in176 * Stereo mode the SWL equals DWL. SWL can be bigger than the177 * product of (channels_per_slot x samplebits), e.g. for codecs178 * like the AD1939 which only accept 32bit wide TDM slots. For179 * "standard" I2S operation we set SWL = chans / 2 * DWL here.180 * Waiting for ASoC to get TDM support ;-)181 */182 if ((bits > 16) && (bits <= 24)) {183 bits = 24; /* these are padded by the SSI */184 /*ssicr |= CR_PDTA;*/ /* cpu/data endianness ? */185 }186 i = 0;187 swl = (bits * channels) / 2;188 switch (swl) {189 case 256: ++i;190 case 128: ++i;191 case 64: ++i;192 case 48: ++i;193 case 32: ++i;194 case 16: ++i;195 ssicr |= i << CR_SWL_SHIFT;196 case 8: break;197 default:198 pr_debug("ssi: invalid system word length computed\n");199 return -EINVAL;200 }201 202 SSIREG(SSICR) = ssicr;203 204 pr_debug("ssi_hw_params() leave\nssicr is now %08lx\n", ssicr);205 return 0;206}207 208static int ssi_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,209 unsigned int freq, int dir)210{211 struct ssi_priv *ssi = &ssi_cpu_data[cpu_dai->id];212 213 ssi->sysclk = freq;214 215 return 0;216}217 218/*219 * This divider is used to generate the SSI_SCK (I2S bitclock) from the220 * clock at the HAC_BIT_CLK ("oversampling clock") pin.221 */222static int ssi_set_clkdiv(struct snd_soc_dai *dai, int did, int div)223{224 struct ssi_priv *ssi = &ssi_cpu_data[dai->id];225 unsigned long ssicr;226 int i;227 228 i = 0;229 ssicr = SSIREG(SSICR) & ~CR_CKDIV_MASK;230 switch (div) {231 case 16: ++i;232 case 8: ++i;233 case 4: ++i;234 case 2: ++i;235 SSIREG(SSICR) = ssicr | (i << CR_CKDIV_SHIFT);236 case 1: break;237 default:238 pr_debug("ssi: invalid sck divider %d\n", div);239 return -EINVAL;240 }241 242 return 0;243}244 245static int ssi_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)246{247 struct ssi_priv *ssi = &ssi_cpu_data[dai->id];248 unsigned long ssicr = SSIREG(SSICR);249 250 pr_debug("ssi_set_fmt()\nssicr was 0x%08lx\n", ssicr);251 252 ssicr &= ~(CR_DEL | CR_PDTA | CR_BREN | CR_SWSP | CR_SCKP |253 CR_SWS_MASTER | CR_SCK_MASTER);254 255 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {256 case SND_SOC_DAIFMT_I2S:257 break;258 case SND_SOC_DAIFMT_RIGHT_J:259 ssicr |= CR_DEL | CR_PDTA;260 break;261 case SND_SOC_DAIFMT_LEFT_J:262 ssicr |= CR_DEL;263 break;264 default:265 pr_debug("ssi: unsupported format\n");266 return -EINVAL;267 }268 269 switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {270 case SND_SOC_DAIFMT_CONT:271 break;272 case SND_SOC_DAIFMT_GATED:273 ssicr |= CR_BREN;274 break;275 }276 277 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {278 case SND_SOC_DAIFMT_NB_NF:279 ssicr |= CR_SCKP; /* sample data at low clkedge */280 break;281 case SND_SOC_DAIFMT_NB_IF:282 ssicr |= CR_SCKP | CR_SWSP;283 break;284 case SND_SOC_DAIFMT_IB_NF:285 break;286 case SND_SOC_DAIFMT_IB_IF:287 ssicr |= CR_SWSP; /* word select starts low */288 break;289 default:290 pr_debug("ssi: invalid inversion\n");291 return -EINVAL;292 }293 294 switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {295 case SND_SOC_DAIFMT_BC_FC:296 break;297 case SND_SOC_DAIFMT_BP_FC:298 ssicr |= CR_SCK_MASTER;299 break;300 case SND_SOC_DAIFMT_BC_FP:301 ssicr |= CR_SWS_MASTER;302 break;303 case SND_SOC_DAIFMT_BP_FP:304 ssicr |= CR_SWS_MASTER | CR_SCK_MASTER;305 break;306 default:307 pr_debug("ssi: invalid master/secondary configuration\n");308 return -EINVAL;309 }310 311 SSIREG(SSICR) = ssicr;312 pr_debug("ssi_set_fmt() leave\nssicr is now 0x%08lx\n", ssicr);313 314 return 0;315}316 317/* the SSI depends on an external clocksource (at HAC_BIT_CLK) even in318 * Master mode, so really this is board specific; the SSI can do any319 * rate with the right bitclk and divider settings.320 */321#define SSI_RATES \322 SNDRV_PCM_RATE_8000_192000323 324/* the SSI can do 8-32 bit samples, with 8 possible channels */325#define SSI_FMTS \326 (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | \327 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \328 SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE | \329 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3LE | \330 SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE)331 332static const struct snd_soc_dai_ops ssi_dai_ops = {333 .startup = ssi_startup,334 .shutdown = ssi_shutdown,335 .trigger = ssi_trigger,336 .hw_params = ssi_hw_params,337 .set_sysclk = ssi_set_sysclk,338 .set_clkdiv = ssi_set_clkdiv,339 .set_fmt = ssi_set_fmt,340};341 342static struct snd_soc_dai_driver sh4_ssi_dai[] = {343{344 .name = "ssi-dai.0",345 .playback = {346 .rates = SSI_RATES,347 .formats = SSI_FMTS,348 .channels_min = 2,349 .channels_max = 8,350 },351 .capture = {352 .rates = SSI_RATES,353 .formats = SSI_FMTS,354 .channels_min = 2,355 .channels_max = 8,356 },357 .ops = &ssi_dai_ops,358},359#ifdef CONFIG_CPU_SUBTYPE_SH7760360{361 .name = "ssi-dai.1",362 .playback = {363 .rates = SSI_RATES,364 .formats = SSI_FMTS,365 .channels_min = 2,366 .channels_max = 8,367 },368 .capture = {369 .rates = SSI_RATES,370 .formats = SSI_FMTS,371 .channels_min = 2,372 .channels_max = 8,373 },374 .ops = &ssi_dai_ops,375},376#endif377};378 379static const struct snd_soc_component_driver sh4_ssi_component = {380 .name = "sh4-ssi",381 .legacy_dai_naming = 1,382};383 384static int sh4_soc_dai_probe(struct platform_device *pdev)385{386 return devm_snd_soc_register_component(&pdev->dev, &sh4_ssi_component,387 sh4_ssi_dai,388 ARRAY_SIZE(sh4_ssi_dai));389}390 391static struct platform_driver sh4_ssi_driver = {392 .driver = {393 .name = "sh4-ssi-dai",394 },395 396 .probe = sh4_soc_dai_probe,397};398 399module_platform_driver(sh4_ssi_driver);400 401MODULE_LICENSE("GPL v2");402MODULE_DESCRIPTION("SuperH onchip SSI (I2S) audio driver");403MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");404