diff options
author | Florian Schlichting <Florian.Schlichting@gmx.de> | 2006-03-15 08:05:19 -0500 |
---|---|---|
committer | Jaroslav Kysela <perex@suse.cz> | 2006-03-22 04:38:50 -0500 |
commit | a23446c085faed6c1c90fba5cdd21d6990871750 (patch) | |
tree | 4d02131ba11c33fd5fa35c59ef61143b2a6c5d82 /sound/pci | |
parent | 7c5706bb33687ce82f30d9ac06dd1bdf71b2262e (diff) |
[ALSA] Fix NM256 hard lock up
Modules: NM256 driver
Treat the nm256 mixer as a write-only device so as to avoid hangs on
initialisation.
Signed-off-by: Florian Schlichting <Florian.Schlichting@gmx.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci')
-rw-r--r-- | sound/pci/nm256/nm256.c | 120 |
1 files changed, 96 insertions, 24 deletions
diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 3a3ba6f547bc..cc297abc9d11 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c | |||
@@ -228,6 +228,7 @@ struct nm256 { | |||
228 | unsigned int use_cache: 1; /* use one big coef. table */ | 228 | unsigned int use_cache: 1; /* use one big coef. table */ |
229 | unsigned int reset_workaround: 1; /* Workaround for some laptops to avoid freeze */ | 229 | unsigned int reset_workaround: 1; /* Workaround for some laptops to avoid freeze */ |
230 | unsigned int reset_workaround_2: 1; /* Extended workaround for some other laptops to avoid freeze */ | 230 | unsigned int reset_workaround_2: 1; /* Extended workaround for some other laptops to avoid freeze */ |
231 | unsigned int in_resume: 1; | ||
231 | 232 | ||
232 | int mixer_base; /* register offset of ac97 mixer */ | 233 | int mixer_base; /* register offset of ac97 mixer */ |
233 | int mixer_status_offset; /* offset of mixer status reg. */ | 234 | int mixer_status_offset; /* offset of mixer status reg. */ |
@@ -242,6 +243,7 @@ struct nm256 { | |||
242 | struct nm256_stream streams[2]; | 243 | struct nm256_stream streams[2]; |
243 | 244 | ||
244 | struct snd_ac97 *ac97; | 245 | struct snd_ac97 *ac97; |
246 | unsigned short *ac97_regs; /* register caches, only for valid regs */ | ||
245 | 247 | ||
246 | struct snd_pcm *pcm; | 248 | struct snd_pcm *pcm; |
247 | 249 | ||
@@ -1153,23 +1155,63 @@ snd_nm256_ac97_ready(struct nm256 *chip) | |||
1153 | return 0; | 1155 | return 0; |
1154 | } | 1156 | } |
1155 | 1157 | ||
1158 | /* | ||
1159 | * Initial register values to be written to the AC97 mixer. | ||
1160 | * While most of these are identical to the reset values, we do this | ||
1161 | * so that we have most of the register contents cached--this avoids | ||
1162 | * reading from the mixer directly (which seems to be problematic, | ||
1163 | * probably due to ignorance). | ||
1164 | */ | ||
1165 | |||
1166 | struct initialValues { | ||
1167 | unsigned short reg; | ||
1168 | unsigned short value; | ||
1169 | }; | ||
1170 | |||
1171 | static struct initialValues nm256_ac97_init_val[] = | ||
1172 | { | ||
1173 | { AC97_MASTER, 0x8000 }, | ||
1174 | { AC97_HEADPHONE, 0x8000 }, | ||
1175 | { AC97_MASTER_MONO, 0x8000 }, | ||
1176 | { AC97_PC_BEEP, 0x8000 }, | ||
1177 | { AC97_PHONE, 0x8008 }, | ||
1178 | { AC97_MIC, 0x8000 }, | ||
1179 | { AC97_LINE, 0x8808 }, | ||
1180 | { AC97_CD, 0x8808 }, | ||
1181 | { AC97_VIDEO, 0x8808 }, | ||
1182 | { AC97_AUX, 0x8808 }, | ||
1183 | { AC97_PCM, 0x8808 }, | ||
1184 | { AC97_REC_SEL, 0x0000 }, | ||
1185 | { AC97_REC_GAIN, 0x0B0B }, | ||
1186 | { AC97_GENERAL_PURPOSE, 0x0000 }, | ||
1187 | { AC97_3D_CONTROL, 0x8000 }, | ||
1188 | { AC97_VENDOR_ID1, 0x8384 }, | ||
1189 | { AC97_VENDOR_ID2, 0x7609 }, | ||
1190 | }; | ||
1191 | |||
1192 | static int nm256_ac97_idx(unsigned short reg) | ||
1193 | { | ||
1194 | int i; | ||
1195 | for (i = 0; i < ARRAY_SIZE(nm256_ac97_init_val); i++) | ||
1196 | if (nm256_ac97_init_val[i].reg == reg) | ||
1197 | return i; | ||
1198 | return -1; | ||
1199 | } | ||
1200 | |||
1156 | /* | 1201 | /* |
1202 | * some nm256 easily crash when reading from mixer registers | ||
1203 | * thus we're treating it as a write-only mixer and cache the | ||
1204 | * written values | ||
1157 | */ | 1205 | */ |
1158 | static unsigned short | 1206 | static unsigned short |
1159 | snd_nm256_ac97_read(struct snd_ac97 *ac97, unsigned short reg) | 1207 | snd_nm256_ac97_read(struct snd_ac97 *ac97, unsigned short reg) |
1160 | { | 1208 | { |
1161 | struct nm256 *chip = ac97->private_data; | 1209 | struct nm256 *chip = ac97->private_data; |
1162 | int res; | 1210 | int idx = nm256_ac97_idx(reg); |
1163 | |||
1164 | if (reg >= 128) | ||
1165 | return 0; | ||
1166 | 1211 | ||
1167 | if (! snd_nm256_ac97_ready(chip)) | 1212 | if (idx < 0) |
1168 | return 0; | 1213 | return 0; |
1169 | res = snd_nm256_readw(chip, chip->mixer_base + reg); | 1214 | return chip->ac97_regs[idx]; |
1170 | /* Magic delay. Bleah yucky. */ | ||
1171 | msleep(1); | ||
1172 | return res; | ||
1173 | } | 1215 | } |
1174 | 1216 | ||
1175 | /* | 1217 | /* |
@@ -1180,8 +1222,12 @@ snd_nm256_ac97_write(struct snd_ac97 *ac97, | |||
1180 | { | 1222 | { |
1181 | struct nm256 *chip = ac97->private_data; | 1223 | struct nm256 *chip = ac97->private_data; |
1182 | int tries = 2; | 1224 | int tries = 2; |
1225 | int idx = nm256_ac97_idx(reg); | ||
1183 | u32 base; | 1226 | u32 base; |
1184 | 1227 | ||
1228 | if (idx < 0) | ||
1229 | return; | ||
1230 | |||
1185 | base = chip->mixer_base; | 1231 | base = chip->mixer_base; |
1186 | 1232 | ||
1187 | snd_nm256_ac97_ready(chip); | 1233 | snd_nm256_ac97_ready(chip); |
@@ -1190,12 +1236,32 @@ snd_nm256_ac97_write(struct snd_ac97 *ac97, | |||
1190 | while (tries-- > 0) { | 1236 | while (tries-- > 0) { |
1191 | snd_nm256_writew(chip, base + reg, val); | 1237 | snd_nm256_writew(chip, base + reg, val); |
1192 | msleep(1); /* a little delay here seems better.. */ | 1238 | msleep(1); /* a little delay here seems better.. */ |
1193 | if (snd_nm256_ac97_ready(chip)) | 1239 | if (snd_nm256_ac97_ready(chip)) { |
1240 | /* successful write: set cache */ | ||
1241 | chip->ac97_regs[idx] = val; | ||
1194 | return; | 1242 | return; |
1243 | } | ||
1195 | } | 1244 | } |
1196 | snd_printd("nm256: ac97 codec not ready..\n"); | 1245 | snd_printd("nm256: ac97 codec not ready..\n"); |
1197 | } | 1246 | } |
1198 | 1247 | ||
1248 | /* static resolution table */ | ||
1249 | static struct snd_ac97_res_table nm256_res_table[] = { | ||
1250 | { AC97_MASTER, 0x1f1f }, | ||
1251 | { AC97_HEADPHONE, 0x1f1f }, | ||
1252 | { AC97_MASTER_MONO, 0x001f }, | ||
1253 | { AC97_PC_BEEP, 0x001f }, | ||
1254 | { AC97_PHONE, 0x001f }, | ||
1255 | { AC97_MIC, 0x001f }, | ||
1256 | { AC97_LINE, 0x1f1f }, | ||
1257 | { AC97_CD, 0x1f1f }, | ||
1258 | { AC97_VIDEO, 0x1f1f }, | ||
1259 | { AC97_AUX, 0x1f1f }, | ||
1260 | { AC97_PCM, 0x1f1f }, | ||
1261 | { AC97_REC_GAIN, 0x0f0f }, | ||
1262 | { } /* terminator */ | ||
1263 | }; | ||
1264 | |||
1199 | /* initialize the ac97 into a known state */ | 1265 | /* initialize the ac97 into a known state */ |
1200 | static void | 1266 | static void |
1201 | snd_nm256_ac97_reset(struct snd_ac97 *ac97) | 1267 | snd_nm256_ac97_reset(struct snd_ac97 *ac97) |
@@ -1213,6 +1279,16 @@ snd_nm256_ac97_reset(struct snd_ac97 *ac97) | |||
1213 | snd_nm256_writeb(chip, 0x6cc, 0x80); | 1279 | snd_nm256_writeb(chip, 0x6cc, 0x80); |
1214 | snd_nm256_writeb(chip, 0x6cc, 0x0); | 1280 | snd_nm256_writeb(chip, 0x6cc, 0x0); |
1215 | } | 1281 | } |
1282 | if (! chip->in_resume) { | ||
1283 | int i; | ||
1284 | for (i = 0; i < ARRAY_SIZE(nm256_ac97_init_val); i++) { | ||
1285 | /* preload the cache, so as to avoid even a single | ||
1286 | * read of the mixer regs | ||
1287 | */ | ||
1288 | snd_nm256_ac97_write(ac97, nm256_ac97_init_val[i].reg, | ||
1289 | nm256_ac97_init_val[i].value); | ||
1290 | } | ||
1291 | } | ||
1216 | } | 1292 | } |
1217 | 1293 | ||
1218 | /* create an ac97 mixer interface */ | 1294 | /* create an ac97 mixer interface */ |
@@ -1221,32 +1297,25 @@ snd_nm256_mixer(struct nm256 *chip) | |||
1221 | { | 1297 | { |
1222 | struct snd_ac97_bus *pbus; | 1298 | struct snd_ac97_bus *pbus; |
1223 | struct snd_ac97_template ac97; | 1299 | struct snd_ac97_template ac97; |
1224 | int i, err; | 1300 | int err; |
1225 | static struct snd_ac97_bus_ops ops = { | 1301 | static struct snd_ac97_bus_ops ops = { |
1226 | .reset = snd_nm256_ac97_reset, | 1302 | .reset = snd_nm256_ac97_reset, |
1227 | .write = snd_nm256_ac97_write, | 1303 | .write = snd_nm256_ac97_write, |
1228 | .read = snd_nm256_ac97_read, | 1304 | .read = snd_nm256_ac97_read, |
1229 | }; | 1305 | }; |
1230 | /* looks like nm256 hangs up when unexpected registers are touched... */ | 1306 | |
1231 | static int mixer_regs[] = { | 1307 | chip->ac97_regs = kcalloc(sizeof(short), |
1232 | AC97_MASTER, AC97_HEADPHONE, AC97_MASTER_MONO, | 1308 | ARRAY_SIZE(nm256_ac97_init_val), GFP_KERNEL); |
1233 | AC97_PC_BEEP, AC97_PHONE, AC97_MIC, AC97_LINE, AC97_CD, | 1309 | if (! chip->ac97_regs) |
1234 | AC97_VIDEO, AC97_AUX, AC97_PCM, AC97_REC_SEL, | 1310 | return -ENOMEM; |
1235 | AC97_REC_GAIN, AC97_GENERAL_PURPOSE, AC97_3D_CONTROL, | ||
1236 | /*AC97_EXTENDED_ID,*/ | ||
1237 | AC97_VENDOR_ID1, AC97_VENDOR_ID2, | ||
1238 | -1 | ||
1239 | }; | ||
1240 | 1311 | ||
1241 | if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0) | 1312 | if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0) |
1242 | return err; | 1313 | return err; |
1243 | 1314 | ||
1244 | memset(&ac97, 0, sizeof(ac97)); | 1315 | memset(&ac97, 0, sizeof(ac97)); |
1245 | ac97.scaps = AC97_SCAP_AUDIO; /* we support audio! */ | 1316 | ac97.scaps = AC97_SCAP_AUDIO; /* we support audio! */ |
1246 | ac97.limited_regs = 1; | ||
1247 | for (i = 0; mixer_regs[i] >= 0; i++) | ||
1248 | set_bit(mixer_regs[i], ac97.reg_accessed); | ||
1249 | ac97.private_data = chip; | 1317 | ac97.private_data = chip; |
1318 | ac97.res_table = nm256_res_table; | ||
1250 | pbus->no_vra = 1; | 1319 | pbus->no_vra = 1; |
1251 | err = snd_ac97_mixer(pbus, &ac97, &chip->ac97); | 1320 | err = snd_ac97_mixer(pbus, &ac97, &chip->ac97); |
1252 | if (err < 0) | 1321 | if (err < 0) |
@@ -1331,6 +1400,7 @@ static int nm256_resume(struct pci_dev *pci) | |||
1331 | int i; | 1400 | int i; |
1332 | 1401 | ||
1333 | /* Perform a full reset on the hardware */ | 1402 | /* Perform a full reset on the hardware */ |
1403 | chip->in_resume = 1; | ||
1334 | pci_restore_state(pci); | 1404 | pci_restore_state(pci); |
1335 | pci_enable_device(pci); | 1405 | pci_enable_device(pci); |
1336 | snd_nm256_init_chip(chip); | 1406 | snd_nm256_init_chip(chip); |
@@ -1348,6 +1418,7 @@ static int nm256_resume(struct pci_dev *pci) | |||
1348 | } | 1418 | } |
1349 | 1419 | ||
1350 | snd_power_change_state(card, SNDRV_CTL_POWER_D0); | 1420 | snd_power_change_state(card, SNDRV_CTL_POWER_D0); |
1421 | chip->in_resume = 0; | ||
1351 | return 0; | 1422 | return 0; |
1352 | } | 1423 | } |
1353 | #endif /* CONFIG_PM */ | 1424 | #endif /* CONFIG_PM */ |
@@ -1372,6 +1443,7 @@ static int snd_nm256_free(struct nm256 *chip) | |||
1372 | free_irq(chip->irq, chip); | 1443 | free_irq(chip->irq, chip); |
1373 | 1444 | ||
1374 | pci_disable_device(chip->pci); | 1445 | pci_disable_device(chip->pci); |
1446 | kfree(chip->ac97_regs); | ||
1375 | kfree(chip); | 1447 | kfree(chip); |
1376 | return 0; | 1448 | return 0; |
1377 | } | 1449 | } |