Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ALSA: cs4236: detect chip in one pass

The cs4236 was two step detection with call to the snd_wss_free()
between two steps. The snd_wss_free() did not free a sound device
created in the snd_wss_create(). This caused an OOPS during module
removal as the same sound device was released twice. The same OOPS
happened if the cs4236 module loading failed.

Fix this by adapting the snd_cs4236_create() to correctly work with
chips less capable then cs4236. The snd_cs4236_create() behaves the
same as the snd_wss_create() if the chip is less capable than the cs4236.

Signed-off-by: Krzysztof Helt <krzysztof.h1@wp.pl>
Signed-off-by: Takashi Iwai <tiwai@suse.de>

authored by

Krzysztof Helt and committed by
Takashi Iwai
d114cd84 9dcaa7b2

+35 -32
-1
include/sound/wss.h
··· 154 154 unsigned short hardware, 155 155 unsigned short hwshare, 156 156 struct snd_wss **rchip); 157 - int snd_wss_free(struct snd_wss *chip); 158 157 int snd_wss_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm); 159 158 int snd_wss_timer(struct snd_wss *chip, int device, struct snd_timer **rtimer); 160 159 int snd_wss_mixer(struct snd_wss *chip);
+3 -10
sound/isa/cs423x/cs4236.c
··· 394 394 return -EBUSY; 395 395 } 396 396 397 - err = snd_wss_create(card, port[dev], cport[dev], 397 + err = snd_cs4236_create(card, port[dev], cport[dev], 398 398 irq[dev], 399 399 dma1[dev], dma2[dev], 400 400 WSS_HW_DETECT3, 0, &chip); 401 401 if (err < 0) 402 402 return err; 403 + 404 + acard->chip = chip; 403 405 if (chip->hardware & WSS_HW_CS4236B_MASK) { 404 - snd_wss_free(chip); 405 - err = snd_cs4236_create(card, 406 - port[dev], cport[dev], 407 - irq[dev], dma1[dev], dma2[dev], 408 - WSS_HW_DETECT, 0, &chip); 409 - if (err < 0) 410 - return err; 411 - acard->chip = chip; 412 406 413 407 err = snd_cs4236_pcm(chip, 0, &pcm); 414 408 if (err < 0) ··· 412 418 if (err < 0) 413 419 return err; 414 420 } else { 415 - acard->chip = chip; 416 421 err = snd_wss_pcm(chip, 0, &pcm); 417 422 if (err < 0) 418 423 return err;
+31 -19
sound/isa/cs423x/cs4236_lib.c
··· 87 87 #include <sound/core.h> 88 88 #include <sound/wss.h> 89 89 #include <sound/asoundef.h> 90 + #include <sound/initval.h> 90 91 91 92 /* 92 93 * ··· 265 264 } 266 265 267 266 #endif /* CONFIG_PM */ 268 - 267 + /* 268 + * This function does no fail if the chip is not CS4236B or compatible. 269 + * It just an equivalent to the snd_wss_create() then. 270 + */ 269 271 int snd_cs4236_create(struct snd_card *card, 270 272 unsigned long port, 271 273 unsigned long cport, ··· 285 281 *rchip = NULL; 286 282 if (hardware == WSS_HW_DETECT) 287 283 hardware = WSS_HW_DETECT3; 288 - if (cport < 0x100) { 289 - snd_printk(KERN_ERR "please, specify control port " 290 - "for CS4236+ chips\n"); 291 - return -ENODEV; 292 - } 284 + 293 285 err = snd_wss_create(card, port, cport, 294 286 irq, dma1, dma2, hardware, hwshare, &chip); 295 287 if (err < 0) 296 288 return err; 297 289 298 - if (!(chip->hardware & WSS_HW_CS4236B_MASK)) { 299 - snd_printk(KERN_ERR "CS4236+: MODE3 and extended registers " 300 - "not available, hardware=0x%x\n", chip->hardware); 301 - snd_device_free(card, chip); 302 - return -ENODEV; 290 + if ((chip->hardware & WSS_HW_CS4236B_MASK) == 0) { 291 + snd_printd("chip is not CS4236+, hardware=0x%x\n", 292 + chip->hardware); 293 + *rchip = chip; 294 + return 0; 303 295 } 304 296 #if 0 305 297 { ··· 308 308 idx, snd_cs4236_ctrl_in(chip, idx)); 309 309 } 310 310 #endif 311 + if (cport < 0x100 || cport == SNDRV_AUTO_PORT) { 312 + snd_printk(KERN_ERR "please, specify control port " 313 + "for CS4236+ chips\n"); 314 + snd_device_free(card, chip); 315 + return -ENODEV; 316 + } 311 317 ver1 = snd_cs4236_ctrl_in(chip, 1); 312 318 ver2 = snd_cs4236_ext_in(chip, CS4236_VERSION); 313 - snd_printdd("CS4236: [0x%lx] C1 (version) = 0x%x, ext = 0x%x\n", cport, ver1, ver2); 319 + snd_printdd("CS4236: [0x%lx] C1 (version) = 0x%x, ext = 0x%x\n", 320 + cport, ver1, ver2); 314 321 if (ver1 != ver2) { 315 322 snd_printk(KERN_ERR "CS4236+ chip detected, but " 316 323 "control port 0x%lx is not valid\n", cport); ··· 328 321 snd_cs4236_ctrl_out(chip, 2, 0xff); 329 322 snd_cs4236_ctrl_out(chip, 3, 0x00); 330 323 snd_cs4236_ctrl_out(chip, 4, 0x80); 331 - snd_cs4236_ctrl_out(chip, 5, ((IEC958_AES1_CON_PCM_CODER & 3) << 6) | IEC958_AES0_CON_EMPHASIS_NONE); 324 + reg = ((IEC958_AES1_CON_PCM_CODER & 3) << 6) | 325 + IEC958_AES0_CON_EMPHASIS_NONE; 326 + snd_cs4236_ctrl_out(chip, 5, reg); 332 327 snd_cs4236_ctrl_out(chip, 6, IEC958_AES1_CON_PCM_CODER >> 2); 333 328 snd_cs4236_ctrl_out(chip, 7, 0x00); 334 - /* 0x8c for C8 is valid for Turtle Beach Malibu - the IEC-958 output */ 335 - /* is working with this setup, other hardware should have */ 336 - /* different signal paths and this value should be selectable */ 337 - /* in the future */ 329 + /* 330 + * 0x8c for C8 is valid for Turtle Beach Malibu - the IEC-958 331 + * output is working with this setup, other hardware should 332 + * have different signal paths and this value should be 333 + * selectable in the future 334 + */ 338 335 snd_cs4236_ctrl_out(chip, 8, 0x8c); 339 336 chip->rate_constraint = snd_cs4236_xrate; 340 337 chip->set_playback_format = snd_cs4236_playback_format; ··· 350 339 351 340 /* initialize extended registers */ 352 341 for (reg = 0; reg < sizeof(snd_cs4236_ext_map); reg++) 353 - snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), snd_cs4236_ext_map[reg]); 342 + snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), 343 + snd_cs4236_ext_map[reg]); 354 344 355 - /* initialize compatible but more featured registers */ 345 + /* initialize compatible but more featured registers */ 356 346 snd_wss_out(chip, CS4231_LEFT_INPUT, 0x40); 357 347 snd_wss_out(chip, CS4231_RIGHT_INPUT, 0x40); 358 348 snd_wss_out(chip, CS4231_AUX1_LEFT_INPUT, 0xff);
+1 -2
sound/isa/wss/wss_lib.c
··· 1682 1682 } 1683 1683 #endif /* CONFIG_PM */ 1684 1684 1685 - int snd_wss_free(struct snd_wss *chip) 1685 + static int snd_wss_free(struct snd_wss *chip) 1686 1686 { 1687 1687 release_and_free_resource(chip->res_port); 1688 1688 release_and_free_resource(chip->res_cport); ··· 1705 1705 kfree(chip); 1706 1706 return 0; 1707 1707 } 1708 - EXPORT_SYMBOL(snd_wss_free); 1709 1708 1710 1709 static int snd_wss_dev_free(struct snd_device *device) 1711 1710 {