1*c087a94bSLad Prabhakar // SPDX-License-Identifier: GPL-2.0 2*c087a94bSLad Prabhakar // 3*c087a94bSLad Prabhakar // Hitachi Audio Controller (AC97) support for SH7760/SH7780 4*c087a94bSLad Prabhakar // 5*c087a94bSLad Prabhakar // Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net> 6*c087a94bSLad Prabhakar // 7*c087a94bSLad Prabhakar // dont forget to set IPSEL/OMSEL register bits (in your board code) to 8*c087a94bSLad Prabhakar // enable HAC output pins! 9*c087a94bSLad Prabhakar 10*c087a94bSLad Prabhakar /* BIG FAT FIXME: although the SH7760 has 2 independent AC97 units, only 11*c087a94bSLad Prabhakar * the FIRST can be used since ASoC does not pass any information to the 12*c087a94bSLad Prabhakar * ac97_read/write() functions regarding WHICH unit to use. You'll have 13*c087a94bSLad Prabhakar * to edit the code a bit to use the other AC97 unit. --mlau 14*c087a94bSLad Prabhakar */ 15*c087a94bSLad Prabhakar 16*c087a94bSLad Prabhakar #include <linux/init.h> 17*c087a94bSLad Prabhakar #include <linux/module.h> 18*c087a94bSLad Prabhakar #include <linux/platform_device.h> 19*c087a94bSLad Prabhakar #include <linux/interrupt.h> 20*c087a94bSLad Prabhakar #include <linux/wait.h> 21*c087a94bSLad Prabhakar #include <linux/delay.h> 22*c087a94bSLad Prabhakar #include <sound/core.h> 23*c087a94bSLad Prabhakar #include <sound/pcm.h> 24*c087a94bSLad Prabhakar #include <sound/ac97_codec.h> 25*c087a94bSLad Prabhakar #include <sound/initval.h> 26*c087a94bSLad Prabhakar #include <sound/soc.h> 27*c087a94bSLad Prabhakar 28*c087a94bSLad Prabhakar /* regs and bits */ 29*c087a94bSLad Prabhakar #define HACCR 0x08 30*c087a94bSLad Prabhakar #define HACCSAR 0x20 31*c087a94bSLad Prabhakar #define HACCSDR 0x24 32*c087a94bSLad Prabhakar #define HACPCML 0x28 33*c087a94bSLad Prabhakar #define HACPCMR 0x2C 34*c087a94bSLad Prabhakar #define HACTIER 0x50 35*c087a94bSLad Prabhakar #define HACTSR 0x54 36*c087a94bSLad Prabhakar #define HACRIER 0x58 37*c087a94bSLad Prabhakar #define HACRSR 0x5C 38*c087a94bSLad Prabhakar #define HACACR 0x60 39*c087a94bSLad Prabhakar 40*c087a94bSLad Prabhakar #define CR_CR (1 << 15) /* "codec-ready" indicator */ 41*c087a94bSLad Prabhakar #define CR_CDRT (1 << 11) /* cold reset */ 42*c087a94bSLad Prabhakar #define CR_WMRT (1 << 10) /* warm reset */ 43*c087a94bSLad Prabhakar #define CR_B9 (1 << 9) /* the mysterious "bit 9" */ 44*c087a94bSLad Prabhakar #define CR_ST (1 << 5) /* AC97 link start bit */ 45*c087a94bSLad Prabhakar 46*c087a94bSLad Prabhakar #define CSAR_RD (1 << 19) /* AC97 data read bit */ 47*c087a94bSLad Prabhakar #define CSAR_WR (0) 48*c087a94bSLad Prabhakar 49*c087a94bSLad Prabhakar #define TSR_CMDAMT (1 << 31) 50*c087a94bSLad Prabhakar #define TSR_CMDDMT (1 << 30) 51*c087a94bSLad Prabhakar 52*c087a94bSLad Prabhakar #define RSR_STARY (1 << 22) 53*c087a94bSLad Prabhakar #define RSR_STDRY (1 << 21) 54*c087a94bSLad Prabhakar 55*c087a94bSLad Prabhakar #define ACR_DMARX16 (1 << 30) 56*c087a94bSLad Prabhakar #define ACR_DMATX16 (1 << 29) 57*c087a94bSLad Prabhakar #define ACR_TX12ATOM (1 << 26) 58*c087a94bSLad Prabhakar #define ACR_DMARX20 ((1 << 24) | (1 << 22)) 59*c087a94bSLad Prabhakar #define ACR_DMATX20 ((1 << 23) | (1 << 21)) 60*c087a94bSLad Prabhakar 61*c087a94bSLad Prabhakar #define CSDR_SHIFT 4 62*c087a94bSLad Prabhakar #define CSDR_MASK (0xffff << CSDR_SHIFT) 63*c087a94bSLad Prabhakar #define CSAR_SHIFT 12 64*c087a94bSLad Prabhakar #define CSAR_MASK (0x7f << CSAR_SHIFT) 65*c087a94bSLad Prabhakar 66*c087a94bSLad Prabhakar #define AC97_WRITE_RETRY 1 67*c087a94bSLad Prabhakar #define AC97_READ_RETRY 5 68*c087a94bSLad Prabhakar 69*c087a94bSLad Prabhakar /* manual-suggested AC97 codec access timeouts (us) */ 70*c087a94bSLad Prabhakar #define TMO_E1 500 /* 21 < E1 < 1000 */ 71*c087a94bSLad Prabhakar #define TMO_E2 13 /* 13 < E2 */ 72*c087a94bSLad Prabhakar #define TMO_E3 21 /* 21 < E3 */ 73*c087a94bSLad Prabhakar #define TMO_E4 500 /* 21 < E4 < 1000 */ 74*c087a94bSLad Prabhakar 75*c087a94bSLad Prabhakar struct hac_priv { 76*c087a94bSLad Prabhakar unsigned long mmio; /* HAC base address */ 77*c087a94bSLad Prabhakar } hac_cpu_data[] = { 78*c087a94bSLad Prabhakar #if defined(CONFIG_CPU_SUBTYPE_SH7760) 79*c087a94bSLad Prabhakar { 80*c087a94bSLad Prabhakar .mmio = 0xFE240000, 81*c087a94bSLad Prabhakar }, 82*c087a94bSLad Prabhakar { 83*c087a94bSLad Prabhakar .mmio = 0xFE250000, 84*c087a94bSLad Prabhakar }, 85*c087a94bSLad Prabhakar #elif defined(CONFIG_CPU_SUBTYPE_SH7780) 86*c087a94bSLad Prabhakar { 87*c087a94bSLad Prabhakar .mmio = 0xFFE40000, 88*c087a94bSLad Prabhakar }, 89*c087a94bSLad Prabhakar #else 90*c087a94bSLad Prabhakar #error "Unsupported SuperH SoC" 91*c087a94bSLad Prabhakar #endif 92*c087a94bSLad Prabhakar }; 93*c087a94bSLad Prabhakar 94*c087a94bSLad Prabhakar #define HACREG(reg) (*(unsigned long *)(hac->mmio + (reg))) 95*c087a94bSLad Prabhakar 96*c087a94bSLad Prabhakar /* 97*c087a94bSLad Prabhakar * AC97 read/write flow as outlined in the SH7760 manual (pages 903-906) 98*c087a94bSLad Prabhakar */ 99*c087a94bSLad Prabhakar static int hac_get_codec_data(struct hac_priv *hac, unsigned short r, 100*c087a94bSLad Prabhakar unsigned short *v) 101*c087a94bSLad Prabhakar { 102*c087a94bSLad Prabhakar unsigned int to1, to2, i; 103*c087a94bSLad Prabhakar unsigned short adr; 104*c087a94bSLad Prabhakar 105*c087a94bSLad Prabhakar for (i = AC97_READ_RETRY; i; i--) { 106*c087a94bSLad Prabhakar *v = 0; 107*c087a94bSLad Prabhakar /* wait for HAC to receive something from the codec */ 108*c087a94bSLad Prabhakar for (to1 = TMO_E4; 109*c087a94bSLad Prabhakar to1 && !(HACREG(HACRSR) & RSR_STARY); 110*c087a94bSLad Prabhakar --to1) 111*c087a94bSLad Prabhakar udelay(1); 112*c087a94bSLad Prabhakar for (to2 = TMO_E4; 113*c087a94bSLad Prabhakar to2 && !(HACREG(HACRSR) & RSR_STDRY); 114*c087a94bSLad Prabhakar --to2) 115*c087a94bSLad Prabhakar udelay(1); 116*c087a94bSLad Prabhakar 117*c087a94bSLad Prabhakar if (!to1 && !to2) 118*c087a94bSLad Prabhakar return 0; /* codec comm is down */ 119*c087a94bSLad Prabhakar 120*c087a94bSLad Prabhakar adr = ((HACREG(HACCSAR) & CSAR_MASK) >> CSAR_SHIFT); 121*c087a94bSLad Prabhakar *v = ((HACREG(HACCSDR) & CSDR_MASK) >> CSDR_SHIFT); 122*c087a94bSLad Prabhakar 123*c087a94bSLad Prabhakar HACREG(HACRSR) &= ~(RSR_STDRY | RSR_STARY); 124*c087a94bSLad Prabhakar 125*c087a94bSLad Prabhakar if (r == adr) 126*c087a94bSLad Prabhakar break; 127*c087a94bSLad Prabhakar 128*c087a94bSLad Prabhakar /* manual says: wait at least 21 usec before retrying */ 129*c087a94bSLad Prabhakar udelay(21); 130*c087a94bSLad Prabhakar } 131*c087a94bSLad Prabhakar HACREG(HACRSR) &= ~(RSR_STDRY | RSR_STARY); 132*c087a94bSLad Prabhakar return i; 133*c087a94bSLad Prabhakar } 134*c087a94bSLad Prabhakar 135*c087a94bSLad Prabhakar static unsigned short hac_read_codec_aux(struct hac_priv *hac, 136*c087a94bSLad Prabhakar unsigned short reg) 137*c087a94bSLad Prabhakar { 138*c087a94bSLad Prabhakar unsigned short val; 139*c087a94bSLad Prabhakar unsigned int i, to; 140*c087a94bSLad Prabhakar 141*c087a94bSLad Prabhakar for (i = AC97_READ_RETRY; i; i--) { 142*c087a94bSLad Prabhakar /* send_read_request */ 143*c087a94bSLad Prabhakar local_irq_disable(); 144*c087a94bSLad Prabhakar HACREG(HACTSR) &= ~(TSR_CMDAMT); 145*c087a94bSLad Prabhakar HACREG(HACCSAR) = (reg << CSAR_SHIFT) | CSAR_RD; 146*c087a94bSLad Prabhakar local_irq_enable(); 147*c087a94bSLad Prabhakar 148*c087a94bSLad Prabhakar for (to = TMO_E3; 149*c087a94bSLad Prabhakar to && !(HACREG(HACTSR) & TSR_CMDAMT); 150*c087a94bSLad Prabhakar --to) 151*c087a94bSLad Prabhakar udelay(1); 152*c087a94bSLad Prabhakar 153*c087a94bSLad Prabhakar HACREG(HACTSR) &= ~TSR_CMDAMT; 154*c087a94bSLad Prabhakar val = 0; 155*c087a94bSLad Prabhakar if (hac_get_codec_data(hac, reg, &val) != 0) 156*c087a94bSLad Prabhakar break; 157*c087a94bSLad Prabhakar } 158*c087a94bSLad Prabhakar 159*c087a94bSLad Prabhakar return i ? val : ~0; 160*c087a94bSLad Prabhakar } 161*c087a94bSLad Prabhakar 162*c087a94bSLad Prabhakar static void hac_ac97_write(struct snd_ac97 *ac97, unsigned short reg, 163*c087a94bSLad Prabhakar unsigned short val) 164*c087a94bSLad Prabhakar { 165*c087a94bSLad Prabhakar int unit_id = 0 /* ac97->private_data */; 166*c087a94bSLad Prabhakar struct hac_priv *hac = &hac_cpu_data[unit_id]; 167*c087a94bSLad Prabhakar unsigned int i, to; 168*c087a94bSLad Prabhakar /* write_codec_aux */ 169*c087a94bSLad Prabhakar for (i = AC97_WRITE_RETRY; i; i--) { 170*c087a94bSLad Prabhakar /* send_write_request */ 171*c087a94bSLad Prabhakar local_irq_disable(); 172*c087a94bSLad Prabhakar HACREG(HACTSR) &= ~(TSR_CMDDMT | TSR_CMDAMT); 173*c087a94bSLad Prabhakar HACREG(HACCSDR) = (val << CSDR_SHIFT); 174*c087a94bSLad Prabhakar HACREG(HACCSAR) = (reg << CSAR_SHIFT) & (~CSAR_RD); 175*c087a94bSLad Prabhakar local_irq_enable(); 176*c087a94bSLad Prabhakar 177*c087a94bSLad Prabhakar /* poll-wait for CMDAMT and CMDDMT */ 178*c087a94bSLad Prabhakar for (to = TMO_E1; 179*c087a94bSLad Prabhakar to && !(HACREG(HACTSR) & (TSR_CMDAMT|TSR_CMDDMT)); 180*c087a94bSLad Prabhakar --to) 181*c087a94bSLad Prabhakar udelay(1); 182*c087a94bSLad Prabhakar 183*c087a94bSLad Prabhakar HACREG(HACTSR) &= ~(TSR_CMDAMT | TSR_CMDDMT); 184*c087a94bSLad Prabhakar if (to) 185*c087a94bSLad Prabhakar break; 186*c087a94bSLad Prabhakar /* timeout, try again */ 187*c087a94bSLad Prabhakar } 188*c087a94bSLad Prabhakar } 189*c087a94bSLad Prabhakar 190*c087a94bSLad Prabhakar static unsigned short hac_ac97_read(struct snd_ac97 *ac97, 191*c087a94bSLad Prabhakar unsigned short reg) 192*c087a94bSLad Prabhakar { 193*c087a94bSLad Prabhakar int unit_id = 0 /* ac97->private_data */; 194*c087a94bSLad Prabhakar struct hac_priv *hac = &hac_cpu_data[unit_id]; 195*c087a94bSLad Prabhakar return hac_read_codec_aux(hac, reg); 196*c087a94bSLad Prabhakar } 197*c087a94bSLad Prabhakar 198*c087a94bSLad Prabhakar static void hac_ac97_warmrst(struct snd_ac97 *ac97) 199*c087a94bSLad Prabhakar { 200*c087a94bSLad Prabhakar int unit_id = 0 /* ac97->private_data */; 201*c087a94bSLad Prabhakar struct hac_priv *hac = &hac_cpu_data[unit_id]; 202*c087a94bSLad Prabhakar unsigned int tmo; 203*c087a94bSLad Prabhakar 204*c087a94bSLad Prabhakar HACREG(HACCR) = CR_WMRT | CR_ST | CR_B9; 205*c087a94bSLad Prabhakar msleep(10); 206*c087a94bSLad Prabhakar HACREG(HACCR) = CR_ST | CR_B9; 207*c087a94bSLad Prabhakar for (tmo = 1000; (tmo > 0) && !(HACREG(HACCR) & CR_CR); tmo--) 208*c087a94bSLad Prabhakar udelay(1); 209*c087a94bSLad Prabhakar 210*c087a94bSLad Prabhakar if (!tmo) 211*c087a94bSLad Prabhakar printk(KERN_INFO "hac: reset: AC97 link down!\n"); 212*c087a94bSLad Prabhakar /* settings this bit lets us have a conversation with codec */ 213*c087a94bSLad Prabhakar HACREG(HACACR) |= ACR_TX12ATOM; 214*c087a94bSLad Prabhakar } 215*c087a94bSLad Prabhakar 216*c087a94bSLad Prabhakar static void hac_ac97_coldrst(struct snd_ac97 *ac97) 217*c087a94bSLad Prabhakar { 218*c087a94bSLad Prabhakar int unit_id = 0 /* ac97->private_data */; 219*c087a94bSLad Prabhakar struct hac_priv *hac; 220*c087a94bSLad Prabhakar hac = &hac_cpu_data[unit_id]; 221*c087a94bSLad Prabhakar 222*c087a94bSLad Prabhakar HACREG(HACCR) = 0; 223*c087a94bSLad Prabhakar HACREG(HACCR) = CR_CDRT | CR_ST | CR_B9; 224*c087a94bSLad Prabhakar msleep(10); 225*c087a94bSLad Prabhakar hac_ac97_warmrst(ac97); 226*c087a94bSLad Prabhakar } 227*c087a94bSLad Prabhakar 228*c087a94bSLad Prabhakar static struct snd_ac97_bus_ops hac_ac97_ops = { 229*c087a94bSLad Prabhakar .read = hac_ac97_read, 230*c087a94bSLad Prabhakar .write = hac_ac97_write, 231*c087a94bSLad Prabhakar .reset = hac_ac97_coldrst, 232*c087a94bSLad Prabhakar .warm_reset = hac_ac97_warmrst, 233*c087a94bSLad Prabhakar }; 234*c087a94bSLad Prabhakar 235*c087a94bSLad Prabhakar static int hac_hw_params(struct snd_pcm_substream *substream, 236*c087a94bSLad Prabhakar struct snd_pcm_hw_params *params, 237*c087a94bSLad Prabhakar struct snd_soc_dai *dai) 238*c087a94bSLad Prabhakar { 239*c087a94bSLad Prabhakar struct hac_priv *hac = &hac_cpu_data[dai->id]; 240*c087a94bSLad Prabhakar int d = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; 241*c087a94bSLad Prabhakar 242*c087a94bSLad Prabhakar switch (params->msbits) { 243*c087a94bSLad Prabhakar case 16: 244*c087a94bSLad Prabhakar HACREG(HACACR) |= d ? ACR_DMARX16 : ACR_DMATX16; 245*c087a94bSLad Prabhakar HACREG(HACACR) &= d ? ~ACR_DMARX20 : ~ACR_DMATX20; 246*c087a94bSLad Prabhakar break; 247*c087a94bSLad Prabhakar case 20: 248*c087a94bSLad Prabhakar HACREG(HACACR) &= d ? ~ACR_DMARX16 : ~ACR_DMATX16; 249*c087a94bSLad Prabhakar HACREG(HACACR) |= d ? ACR_DMARX20 : ACR_DMATX20; 250*c087a94bSLad Prabhakar break; 251*c087a94bSLad Prabhakar default: 252*c087a94bSLad Prabhakar pr_debug("hac: invalid depth %d bit\n", params->msbits); 253*c087a94bSLad Prabhakar return -EINVAL; 254*c087a94bSLad Prabhakar break; 255*c087a94bSLad Prabhakar } 256*c087a94bSLad Prabhakar 257*c087a94bSLad Prabhakar return 0; 258*c087a94bSLad Prabhakar } 259*c087a94bSLad Prabhakar 260*c087a94bSLad Prabhakar #define AC97_RATES \ 261*c087a94bSLad Prabhakar SNDRV_PCM_RATE_8000_192000 262*c087a94bSLad Prabhakar 263*c087a94bSLad Prabhakar #define AC97_FMTS \ 264*c087a94bSLad Prabhakar SNDRV_PCM_FMTBIT_S16_LE 265*c087a94bSLad Prabhakar 266*c087a94bSLad Prabhakar static const struct snd_soc_dai_ops hac_dai_ops = { 267*c087a94bSLad Prabhakar .hw_params = hac_hw_params, 268*c087a94bSLad Prabhakar }; 269*c087a94bSLad Prabhakar 270*c087a94bSLad Prabhakar static struct snd_soc_dai_driver sh4_hac_dai[] = { 271*c087a94bSLad Prabhakar { 272*c087a94bSLad Prabhakar .name = "hac-dai.0", 273*c087a94bSLad Prabhakar .playback = { 274*c087a94bSLad Prabhakar .rates = AC97_RATES, 275*c087a94bSLad Prabhakar .formats = AC97_FMTS, 276*c087a94bSLad Prabhakar .channels_min = 2, 277*c087a94bSLad Prabhakar .channels_max = 2, 278*c087a94bSLad Prabhakar }, 279*c087a94bSLad Prabhakar .capture = { 280*c087a94bSLad Prabhakar .rates = AC97_RATES, 281*c087a94bSLad Prabhakar .formats = AC97_FMTS, 282*c087a94bSLad Prabhakar .channels_min = 2, 283*c087a94bSLad Prabhakar .channels_max = 2, 284*c087a94bSLad Prabhakar }, 285*c087a94bSLad Prabhakar .ops = &hac_dai_ops, 286*c087a94bSLad Prabhakar }, 287*c087a94bSLad Prabhakar #ifdef CONFIG_CPU_SUBTYPE_SH7760 288*c087a94bSLad Prabhakar { 289*c087a94bSLad Prabhakar .name = "hac-dai.1", 290*c087a94bSLad Prabhakar .id = 1, 291*c087a94bSLad Prabhakar .playback = { 292*c087a94bSLad Prabhakar .rates = AC97_RATES, 293*c087a94bSLad Prabhakar .formats = AC97_FMTS, 294*c087a94bSLad Prabhakar .channels_min = 2, 295*c087a94bSLad Prabhakar .channels_max = 2, 296*c087a94bSLad Prabhakar }, 297*c087a94bSLad Prabhakar .capture = { 298*c087a94bSLad Prabhakar .rates = AC97_RATES, 299*c087a94bSLad Prabhakar .formats = AC97_FMTS, 300*c087a94bSLad Prabhakar .channels_min = 2, 301*c087a94bSLad Prabhakar .channels_max = 2, 302*c087a94bSLad Prabhakar }, 303*c087a94bSLad Prabhakar .ops = &hac_dai_ops, 304*c087a94bSLad Prabhakar 305*c087a94bSLad Prabhakar }, 306*c087a94bSLad Prabhakar #endif 307*c087a94bSLad Prabhakar }; 308*c087a94bSLad Prabhakar 309*c087a94bSLad Prabhakar static const struct snd_soc_component_driver sh4_hac_component = { 310*c087a94bSLad Prabhakar .name = "sh4-hac", 311*c087a94bSLad Prabhakar .legacy_dai_naming = 1, 312*c087a94bSLad Prabhakar }; 313*c087a94bSLad Prabhakar 314*c087a94bSLad Prabhakar static int hac_soc_platform_probe(struct platform_device *pdev) 315*c087a94bSLad Prabhakar { 316*c087a94bSLad Prabhakar int ret; 317*c087a94bSLad Prabhakar 318*c087a94bSLad Prabhakar ret = snd_soc_set_ac97_ops(&hac_ac97_ops); 319*c087a94bSLad Prabhakar if (ret != 0) 320*c087a94bSLad Prabhakar return ret; 321*c087a94bSLad Prabhakar 322*c087a94bSLad Prabhakar return devm_snd_soc_register_component(&pdev->dev, &sh4_hac_component, 323*c087a94bSLad Prabhakar sh4_hac_dai, ARRAY_SIZE(sh4_hac_dai)); 324*c087a94bSLad Prabhakar } 325*c087a94bSLad Prabhakar 326*c087a94bSLad Prabhakar static void hac_soc_platform_remove(struct platform_device *pdev) 327*c087a94bSLad Prabhakar { 328*c087a94bSLad Prabhakar snd_soc_set_ac97_ops(NULL); 329*c087a94bSLad Prabhakar } 330*c087a94bSLad Prabhakar 331*c087a94bSLad Prabhakar static struct platform_driver hac_pcm_driver = { 332*c087a94bSLad Prabhakar .driver = { 333*c087a94bSLad Prabhakar .name = "hac-pcm-audio", 334*c087a94bSLad Prabhakar }, 335*c087a94bSLad Prabhakar 336*c087a94bSLad Prabhakar .probe = hac_soc_platform_probe, 337*c087a94bSLad Prabhakar .remove = hac_soc_platform_remove, 338*c087a94bSLad Prabhakar }; 339*c087a94bSLad Prabhakar 340*c087a94bSLad Prabhakar module_platform_driver(hac_pcm_driver); 341*c087a94bSLad Prabhakar 342*c087a94bSLad Prabhakar MODULE_LICENSE("GPL v2"); 343*c087a94bSLad Prabhakar MODULE_DESCRIPTION("SuperH onchip HAC (AC97) audio driver"); 344*c087a94bSLad Prabhakar MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>"); 345