aboutsummaryrefslogtreecommitdiffstats
path: root/sound/i2c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2007-03-30 09:38:39 -0400
committerJaroslav Kysela <perex@suse.cz>2007-05-11 10:55:55 -0400
commit51354ae3b8fdbeaf96e23ddf787432a38eba31f5 (patch)
treebcc9b700ca83c5e874ce02f206775630b452f047 /sound/i2c
parenta971c3d42524afc5619fa271d59d29be3c1661e3 (diff)
[ALSA] ak4114 - Fix possible Oops with callbacks
ak4114 code may trigger Oops when the parameters are changed without call of snd_ak4114_build(). Now it checks the existence of kctl element, and the workq is triggered after building the necessary kcontrols. Also, did some code clean up. Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Jaroslav Kysela <perex@suse.cz>
Diffstat (limited to 'sound/i2c')
-rw-r--r--sound/i2c/other/ak4114.c95
1 files changed, 62 insertions, 33 deletions
diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c
index adbfd5884d06..8f7c42c036eb 100644
--- a/sound/i2c/other/ak4114.c
+++ b/sound/i2c/other/ak4114.c
@@ -36,6 +36,7 @@ MODULE_LICENSE("GPL");
36#define AK4114_ADDR 0x00 /* fixed address */ 36#define AK4114_ADDR 0x00 /* fixed address */
37 37
38static void ak4114_stats(struct work_struct *work); 38static void ak4114_stats(struct work_struct *work);
39static void ak4114_init_regs(struct ak4114 *chip);
39 40
40static void reg_write(struct ak4114 *ak4114, unsigned char reg, unsigned char val) 41static void reg_write(struct ak4114 *ak4114, unsigned char reg, unsigned char val)
41{ 42{
@@ -105,7 +106,7 @@ int snd_ak4114_create(struct snd_card *card,
105 for (reg = 0; reg < 5; reg++) 106 for (reg = 0; reg < 5; reg++)
106 chip->txcsb[reg] = txcsb[reg]; 107 chip->txcsb[reg] = txcsb[reg];
107 108
108 snd_ak4114_reinit(chip); 109 ak4114_init_regs(chip);
109 110
110 chip->rcs0 = reg_read(chip, AK4114_REG_RCS0) & ~(AK4114_QINT | AK4114_CINT); 111 chip->rcs0 = reg_read(chip, AK4114_REG_RCS0) & ~(AK4114_QINT | AK4114_CINT);
111 chip->rcs1 = reg_read(chip, AK4114_REG_RCS1); 112 chip->rcs1 = reg_read(chip, AK4114_REG_RCS1);
@@ -131,13 +132,10 @@ void snd_ak4114_reg_write(struct ak4114 *chip, unsigned char reg, unsigned char
131 (chip->txcsb[reg-AK4114_REG_TXCSB0] & ~mask) | val); 132 (chip->txcsb[reg-AK4114_REG_TXCSB0] & ~mask) | val);
132} 133}
133 134
134void snd_ak4114_reinit(struct ak4114 *chip) 135static void ak4114_init_regs(struct ak4114 *chip)
135{ 136{
136 unsigned char old = chip->regmap[AK4114_REG_PWRDN], reg; 137 unsigned char old = chip->regmap[AK4114_REG_PWRDN], reg;
137 138
138 chip->init = 1;
139 mb();
140 flush_scheduled_work();
141 /* bring the chip to reset state and powerdown state */ 139 /* bring the chip to reset state and powerdown state */
142 reg_write(chip, AK4114_REG_PWRDN, old & ~(AK4114_RST|AK4114_PWN)); 140 reg_write(chip, AK4114_REG_PWRDN, old & ~(AK4114_RST|AK4114_PWN));
143 udelay(200); 141 udelay(200);
@@ -150,9 +148,18 @@ void snd_ak4114_reinit(struct ak4114 *chip)
150 reg_write(chip, reg + AK4114_REG_TXCSB0, chip->txcsb[reg]); 148 reg_write(chip, reg + AK4114_REG_TXCSB0, chip->txcsb[reg]);
151 /* release powerdown, everything is initialized now */ 149 /* release powerdown, everything is initialized now */
152 reg_write(chip, AK4114_REG_PWRDN, old | AK4114_RST | AK4114_PWN); 150 reg_write(chip, AK4114_REG_PWRDN, old | AK4114_RST | AK4114_PWN);
151}
152
153void snd_ak4114_reinit(struct ak4114 *chip)
154{
155 chip->init = 1;
156 mb();
157 flush_scheduled_work();
158 ak4114_init_regs(chip);
153 /* bring up statistics / event queing */ 159 /* bring up statistics / event queing */
154 chip->init = 0; 160 chip->init = 0;
155 schedule_delayed_work(&chip->work, HZ / 10); 161 if (chip->kctls[0])
162 schedule_delayed_work(&chip->work, HZ / 10);
156} 163}
157 164
158static unsigned int external_rate(unsigned char rcs1) 165static unsigned int external_rate(unsigned char rcs1)
@@ -472,9 +479,55 @@ int snd_ak4114_build(struct ak4114 *ak4114,
472 return err; 479 return err;
473 ak4114->kctls[idx] = kctl; 480 ak4114->kctls[idx] = kctl;
474 } 481 }
482 /* trigger workq */
483 schedule_delayed_work(&ak4114->work, HZ / 10);
475 return 0; 484 return 0;
476} 485}
477 486
487/* notify kcontrols if any parameters are changed */
488static void ak4114_notify(struct ak4114 *ak4114,
489 unsigned char rcs0, unsigned char rcs1,
490 unsigned char c0, unsigned char c1)
491{
492 if (!ak4114->kctls[0])
493 return;
494
495 if (rcs0 & AK4114_PAR)
496 snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
497 &ak4114->kctls[0]->id);
498 if (rcs0 & AK4114_V)
499 snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
500 &ak4114->kctls[1]->id);
501 if (rcs1 & AK4114_CCRC)
502 snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
503 &ak4114->kctls[2]->id);
504 if (rcs1 & AK4114_QCRC)
505 snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
506 &ak4114->kctls[3]->id);
507
508 /* rate change */
509 if (c1 & 0xf0)
510 snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
511 &ak4114->kctls[4]->id);
512
513 if ((c0 & AK4114_PEM) | (c0 & AK4114_CINT))
514 snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
515 &ak4114->kctls[9]->id);
516 if (c0 & AK4114_QINT)
517 snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
518 &ak4114->kctls[10]->id);
519
520 if (c0 & AK4114_AUDION)
521 snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
522 &ak4114->kctls[11]->id);
523 if (c0 & AK4114_AUTO)
524 snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
525 &ak4114->kctls[12]->id);
526 if (c0 & AK4114_DTSCD)
527 snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
528 &ak4114->kctls[13]->id);
529}
530
478int snd_ak4114_external_rate(struct ak4114 *ak4114) 531int snd_ak4114_external_rate(struct ak4114 *ak4114)
479{ 532{
480 unsigned char rcs1; 533 unsigned char rcs1;
@@ -511,31 +564,7 @@ int snd_ak4114_check_rate_and_errors(struct ak4114 *ak4114, unsigned int flags)
511 ak4114->rcs1 = rcs1; 564 ak4114->rcs1 = rcs1;
512 spin_unlock_irqrestore(&ak4114->lock, _flags); 565 spin_unlock_irqrestore(&ak4114->lock, _flags);
513 566
514 if (rcs0 & AK4114_PAR) 567 ak4114_notify(ak4114, rcs0, rcs1, c0, c1);
515 snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[0]->id);
516 if (rcs0 & AK4114_V)
517 snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[1]->id);
518 if (rcs1 & AK4114_CCRC)
519 snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[2]->id);
520 if (rcs1 & AK4114_QCRC)
521 snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[3]->id);
522
523 /* rate change */
524 if (c1 & 0xf0)
525 snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[4]->id);
526
527 if ((c0 & AK4114_PEM) | (c0 & AK4114_CINT))
528 snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[9]->id);
529 if (c0 & AK4114_QINT)
530 snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[10]->id);
531
532 if (c0 & AK4114_AUDION)
533 snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[11]->id);
534 if (c0 & AK4114_AUTO)
535 snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[12]->id);
536 if (c0 & AK4114_DTSCD)
537 snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[13]->id);
538
539 if (ak4114->change_callback && (c0 | c1) != 0) 568 if (ak4114->change_callback && (c0 | c1) != 0)
540 ak4114->change_callback(ak4114, c0, c1); 569 ak4114->change_callback(ak4114, c0, c1);
541 570
@@ -559,8 +588,8 @@ static void ak4114_stats(struct work_struct *work)
559 struct ak4114 *chip = container_of(work, struct ak4114, work.work); 588 struct ak4114 *chip = container_of(work, struct ak4114, work.work);
560 589
561 if (chip->init) 590 if (chip->init)
562 return; 591 snd_ak4114_check_rate_and_errors(chip, 0);
563 snd_ak4114_check_rate_and_errors(chip, 0); 592
564 schedule_delayed_work(&chip->work, HZ / 10); 593 schedule_delayed_work(&chip->work, HZ / 10);
565} 594}
566 595