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