1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Driver for Sound Core PDAudioCF soundcard |
4 | * |
5 | * Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz> |
6 | */ |
7 | |
8 | #include <linux/delay.h> |
9 | #include <linux/slab.h> |
10 | #include <sound/core.h> |
11 | #include <sound/info.h> |
12 | #include "pdaudiocf.h" |
13 | #include <sound/initval.h> |
14 | |
15 | /* |
16 | * |
17 | */ |
18 | static unsigned char pdacf_ak4117_read(void *private_data, unsigned char reg) |
19 | { |
20 | struct snd_pdacf *chip = private_data; |
21 | unsigned long timeout; |
22 | unsigned long flags; |
23 | unsigned char res; |
24 | |
25 | spin_lock_irqsave(&chip->ak4117_lock, flags); |
26 | timeout = 1000; |
27 | while (pdacf_reg_read(chip, PDAUDIOCF_REG_SCR) & PDAUDIOCF_AK_SBP) { |
28 | udelay(5); |
29 | if (--timeout == 0) { |
30 | spin_unlock_irqrestore(lock: &chip->ak4117_lock, flags); |
31 | snd_printk(KERN_ERR "AK4117 ready timeout (read)\n" ); |
32 | return 0; |
33 | } |
34 | } |
35 | pdacf_reg_write(chip, PDAUDIOCF_REG_AK_IFR, val: (u16)reg << 8); |
36 | timeout = 1000; |
37 | while (pdacf_reg_read(chip, PDAUDIOCF_REG_SCR) & PDAUDIOCF_AK_SBP) { |
38 | udelay(5); |
39 | if (--timeout == 0) { |
40 | spin_unlock_irqrestore(lock: &chip->ak4117_lock, flags); |
41 | snd_printk(KERN_ERR "AK4117 read timeout (read2)\n" ); |
42 | return 0; |
43 | } |
44 | } |
45 | res = (unsigned char)pdacf_reg_read(chip, PDAUDIOCF_REG_AK_IFR); |
46 | spin_unlock_irqrestore(lock: &chip->ak4117_lock, flags); |
47 | return res; |
48 | } |
49 | |
50 | static void pdacf_ak4117_write(void *private_data, unsigned char reg, unsigned char val) |
51 | { |
52 | struct snd_pdacf *chip = private_data; |
53 | unsigned long timeout; |
54 | unsigned long flags; |
55 | |
56 | spin_lock_irqsave(&chip->ak4117_lock, flags); |
57 | timeout = 1000; |
58 | while (inw(port: chip->port + PDAUDIOCF_REG_SCR) & PDAUDIOCF_AK_SBP) { |
59 | udelay(5); |
60 | if (--timeout == 0) { |
61 | spin_unlock_irqrestore(lock: &chip->ak4117_lock, flags); |
62 | snd_printk(KERN_ERR "AK4117 ready timeout (write)\n" ); |
63 | return; |
64 | } |
65 | } |
66 | outw(value: (u16)reg << 8 | val | (1<<13), port: chip->port + PDAUDIOCF_REG_AK_IFR); |
67 | spin_unlock_irqrestore(lock: &chip->ak4117_lock, flags); |
68 | } |
69 | |
70 | #if 0 |
71 | void pdacf_dump(struct snd_pdacf *chip) |
72 | { |
73 | printk(KERN_DEBUG "PDAUDIOCF DUMP (0x%lx):\n" , chip->port); |
74 | printk(KERN_DEBUG "WPD : 0x%x\n" , |
75 | inw(chip->port + PDAUDIOCF_REG_WDP)); |
76 | printk(KERN_DEBUG "RDP : 0x%x\n" , |
77 | inw(chip->port + PDAUDIOCF_REG_RDP)); |
78 | printk(KERN_DEBUG "TCR : 0x%x\n" , |
79 | inw(chip->port + PDAUDIOCF_REG_TCR)); |
80 | printk(KERN_DEBUG "SCR : 0x%x\n" , |
81 | inw(chip->port + PDAUDIOCF_REG_SCR)); |
82 | printk(KERN_DEBUG "ISR : 0x%x\n" , |
83 | inw(chip->port + PDAUDIOCF_REG_ISR)); |
84 | printk(KERN_DEBUG "IER : 0x%x\n" , |
85 | inw(chip->port + PDAUDIOCF_REG_IER)); |
86 | printk(KERN_DEBUG "AK_IFR : 0x%x\n" , |
87 | inw(chip->port + PDAUDIOCF_REG_AK_IFR)); |
88 | } |
89 | #endif |
90 | |
91 | static int pdacf_reset(struct snd_pdacf *chip, int powerdown) |
92 | { |
93 | u16 val; |
94 | |
95 | val = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); |
96 | val |= PDAUDIOCF_PDN; |
97 | val &= ~PDAUDIOCF_RECORD; /* for sure */ |
98 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); |
99 | udelay(5); |
100 | val |= PDAUDIOCF_RST; |
101 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); |
102 | udelay(200); |
103 | val &= ~PDAUDIOCF_RST; |
104 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); |
105 | udelay(5); |
106 | if (!powerdown) { |
107 | val &= ~PDAUDIOCF_PDN; |
108 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); |
109 | udelay(200); |
110 | } |
111 | return 0; |
112 | } |
113 | |
114 | void pdacf_reinit(struct snd_pdacf *chip, int resume) |
115 | { |
116 | pdacf_reset(chip, powerdown: 0); |
117 | if (resume) |
118 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val: chip->suspend_reg_scr); |
119 | snd_ak4117_reinit(ak4117: chip->ak4117); |
120 | pdacf_reg_write(chip, PDAUDIOCF_REG_TCR, val: chip->regmap[PDAUDIOCF_REG_TCR>>1]); |
121 | pdacf_reg_write(chip, PDAUDIOCF_REG_IER, val: chip->regmap[PDAUDIOCF_REG_IER>>1]); |
122 | } |
123 | |
124 | static void pdacf_proc_read(struct snd_info_entry * entry, |
125 | struct snd_info_buffer *buffer) |
126 | { |
127 | struct snd_pdacf *chip = entry->private_data; |
128 | u16 tmp; |
129 | |
130 | snd_iprintf(buffer, "PDAudioCF\n\n" ); |
131 | tmp = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); |
132 | snd_iprintf(buffer, "FPGA revision : 0x%x\n" , PDAUDIOCF_FPGAREV(tmp)); |
133 | |
134 | } |
135 | |
136 | static void pdacf_proc_init(struct snd_pdacf *chip) |
137 | { |
138 | snd_card_ro_proc_new(card: chip->card, name: "pdaudiocf" , private_data: chip, read: pdacf_proc_read); |
139 | } |
140 | |
141 | struct snd_pdacf *snd_pdacf_create(struct snd_card *card) |
142 | { |
143 | struct snd_pdacf *chip; |
144 | |
145 | chip = kzalloc(size: sizeof(*chip), GFP_KERNEL); |
146 | if (chip == NULL) |
147 | return NULL; |
148 | chip->card = card; |
149 | mutex_init(&chip->reg_lock); |
150 | spin_lock_init(&chip->ak4117_lock); |
151 | card->private_data = chip; |
152 | |
153 | pdacf_proc_init(chip); |
154 | return chip; |
155 | } |
156 | |
157 | static void snd_pdacf_ak4117_change(struct ak4117 *ak4117, unsigned char c0, unsigned char c1) |
158 | { |
159 | struct snd_pdacf *chip = ak4117->change_callback_private; |
160 | u16 val; |
161 | |
162 | if (!(c0 & AK4117_UNLCK)) |
163 | return; |
164 | mutex_lock(&chip->reg_lock); |
165 | val = chip->regmap[PDAUDIOCF_REG_SCR>>1]; |
166 | if (ak4117->rcs0 & AK4117_UNLCK) |
167 | val |= PDAUDIOCF_BLUE_LED_OFF; |
168 | else |
169 | val &= ~PDAUDIOCF_BLUE_LED_OFF; |
170 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); |
171 | mutex_unlock(lock: &chip->reg_lock); |
172 | } |
173 | |
174 | int snd_pdacf_ak4117_create(struct snd_pdacf *chip) |
175 | { |
176 | int err; |
177 | u16 val; |
178 | /* design note: if we unmask PLL unlock, parity, valid, audio or auto bit interrupts */ |
179 | /* from AK4117 then INT1 pin from AK4117 will be high all time, because PCMCIA interrupts are */ |
180 | /* egde based and FPGA does logical OR for all interrupt sources, we cannot use these */ |
181 | /* high-rate sources */ |
182 | static const unsigned char pgm[5] = { |
183 | AK4117_XTL_24_576M | AK4117_EXCT, /* AK4117_REG_PWRDN */ |
184 | AK4117_CM_PLL_XTAL | AK4117_PKCS_128fs | AK4117_XCKS_128fs, /* AK4117_REQ_CLOCK */ |
185 | AK4117_EFH_1024LRCLK | AK4117_DIF_24R | AK4117_IPS, /* AK4117_REG_IO */ |
186 | 0xff, /* AK4117_REG_INT0_MASK */ |
187 | AK4117_MAUTO | AK4117_MAUD | AK4117_MULK | AK4117_MPAR | AK4117_MV, /* AK4117_REG_INT1_MASK */ |
188 | }; |
189 | |
190 | err = pdacf_reset(chip, powerdown: 0); |
191 | if (err < 0) |
192 | return err; |
193 | err = snd_ak4117_create(card: chip->card, read: pdacf_ak4117_read, write: pdacf_ak4117_write, pgm, private_data: chip, r_ak4117: &chip->ak4117); |
194 | if (err < 0) |
195 | return err; |
196 | |
197 | val = pdacf_reg_read(chip, PDAUDIOCF_REG_TCR); |
198 | #if 1 /* normal operation */ |
199 | val &= ~(PDAUDIOCF_ELIMAKMBIT|PDAUDIOCF_TESTDATASEL); |
200 | #else /* debug */ |
201 | val |= PDAUDIOCF_ELIMAKMBIT; |
202 | val &= ~PDAUDIOCF_TESTDATASEL; |
203 | #endif |
204 | pdacf_reg_write(chip, PDAUDIOCF_REG_TCR, val); |
205 | |
206 | /* setup the FPGA to match AK4117 setup */ |
207 | val = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); |
208 | val &= ~(PDAUDIOCF_CLKDIV0 | PDAUDIOCF_CLKDIV1); /* use 24.576Mhz clock */ |
209 | val &= ~(PDAUDIOCF_RED_LED_OFF|PDAUDIOCF_BLUE_LED_OFF); |
210 | val |= PDAUDIOCF_DATAFMT0 | PDAUDIOCF_DATAFMT1; /* 24-bit data */ |
211 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); |
212 | |
213 | /* setup LEDs and IRQ */ |
214 | val = pdacf_reg_read(chip, PDAUDIOCF_REG_IER); |
215 | val &= ~(PDAUDIOCF_IRQLVLEN0 | PDAUDIOCF_IRQLVLEN1); |
216 | val &= ~(PDAUDIOCF_BLUEDUTY0 | PDAUDIOCF_REDDUTY0 | PDAUDIOCF_REDDUTY1); |
217 | val |= PDAUDIOCF_BLUEDUTY1 | PDAUDIOCF_HALFRATE; |
218 | val |= PDAUDIOCF_IRQOVREN | PDAUDIOCF_IRQAKMEN; |
219 | pdacf_reg_write(chip, PDAUDIOCF_REG_IER, val); |
220 | |
221 | chip->ak4117->change_callback_private = chip; |
222 | chip->ak4117->change_callback = snd_pdacf_ak4117_change; |
223 | |
224 | /* update LED status */ |
225 | snd_pdacf_ak4117_change(ak4117: chip->ak4117, AK4117_UNLCK, c1: 0); |
226 | |
227 | return 0; |
228 | } |
229 | |
230 | void snd_pdacf_powerdown(struct snd_pdacf *chip) |
231 | { |
232 | u16 val; |
233 | |
234 | val = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); |
235 | chip->suspend_reg_scr = val; |
236 | val |= PDAUDIOCF_RED_LED_OFF | PDAUDIOCF_BLUE_LED_OFF; |
237 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); |
238 | /* disable interrupts, but use direct write to preserve old register value in chip->regmap */ |
239 | val = inw(port: chip->port + PDAUDIOCF_REG_IER); |
240 | val &= ~(PDAUDIOCF_IRQOVREN|PDAUDIOCF_IRQAKMEN|PDAUDIOCF_IRQLVLEN0|PDAUDIOCF_IRQLVLEN1); |
241 | outw(value: val, port: chip->port + PDAUDIOCF_REG_IER); |
242 | pdacf_reset(chip, powerdown: 1); |
243 | } |
244 | |
245 | #ifdef CONFIG_PM |
246 | |
247 | int snd_pdacf_suspend(struct snd_pdacf *chip) |
248 | { |
249 | u16 val; |
250 | |
251 | snd_power_change_state(card: chip->card, SNDRV_CTL_POWER_D3hot); |
252 | /* disable interrupts, but use direct write to preserve old register value in chip->regmap */ |
253 | val = inw(port: chip->port + PDAUDIOCF_REG_IER); |
254 | val &= ~(PDAUDIOCF_IRQOVREN|PDAUDIOCF_IRQAKMEN|PDAUDIOCF_IRQLVLEN0|PDAUDIOCF_IRQLVLEN1); |
255 | outw(value: val, port: chip->port + PDAUDIOCF_REG_IER); |
256 | chip->chip_status |= PDAUDIOCF_STAT_IS_SUSPENDED; /* ignore interrupts from now */ |
257 | snd_pdacf_powerdown(chip); |
258 | return 0; |
259 | } |
260 | |
261 | static inline int check_signal(struct snd_pdacf *chip) |
262 | { |
263 | return (chip->ak4117->rcs0 & AK4117_UNLCK) == 0; |
264 | } |
265 | |
266 | int snd_pdacf_resume(struct snd_pdacf *chip) |
267 | { |
268 | int timeout = 40; |
269 | |
270 | pdacf_reinit(chip, resume: 1); |
271 | /* wait for AK4117's PLL */ |
272 | while (timeout-- > 0 && |
273 | (snd_ak4117_external_rate(ak4117: chip->ak4117) <= 0 || !check_signal(chip))) |
274 | mdelay(1); |
275 | chip->chip_status &= ~PDAUDIOCF_STAT_IS_SUSPENDED; |
276 | snd_power_change_state(card: chip->card, SNDRV_CTL_POWER_D0); |
277 | return 0; |
278 | } |
279 | #endif |
280 | |