diff mbox

ESI Juli@ crash with external clock switch - patch

Message ID s5h7fwmskx1.wl-tiwai@suse.de (mailing list archive)
State New, archived
Headers show

Commit Message

Takashi Iwai Jan. 16, 2015, 5:19 p.m. UTC
At Fri, 16 Jan 2015 18:13:09 +0100,
Takashi Iwai wrote:
> 
> At Thu, 15 Jan 2015 22:15:46 +0100,
> Pavel Hofman wrote:
> > 
> > Dne 12.1.2015 v 16:43 Takashi Iwai napsal(a):
> > > At Mon, 12 Jan 2015 09:34:52 +0100,
> > > Pavel Hofman wrote:
> > >>
> > >>
> > >> I wish I could help but unfortunately my practical knowledge of kernel
> > >> workqueues is close to zero :-( Of course I will test the patches and
> > >> will extend them for quartet with testing too.
> > >
> > > How about the patch below?  This is a quick fix for 3.19 (and
> > > stable).  More better fixes will follow later once after it's
> > > confirmed to work.
> > 
> > Hi,
> > 
> > Thanks a lot, the patch seems to run fine. Only ak4114_init_regs has to 
> > be called every samplerate change, otherwise the receiver does not 
> > re-run the samplerate detection and its register with detected 
> > samplerate does not update its value.
> 
> OK, I'm going to send a fix series including the relevant correction.
> Give it a try later.
> 
> > 
> > The following patch seems to run ok:
> > 
> > diff --git a/include/sound/ak4114.h b/include/sound/ak4114.h
> > index 52f02a6..796834b 100644
> > --- a/include/sound/ak4114.h
> > +++ b/include/sound/ak4114.h
> > @@ -169,6 +169,7 @@ struct ak4114 {
> >          ak4114_read_t * read;
> >          void * private_data;
> >          unsigned int init: 1;
> > +       bool in_workq;
> >          spinlock_t lock;
> >          unsigned char regmap[6];
> >          unsigned char txcsb[5];
> > diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c
> > index c7f5633..6d643b7 100644
> > --- a/sound/i2c/other/ak4114.c
> > +++ b/sound/i2c/other/ak4114.c
> > @@ -152,10 +152,12 @@ static void ak4114_init_regs(struct ak4114 *chip)
> > 
> >   void snd_ak4114_reinit(struct ak4114 *chip)
> >   {
> > +       ak4114_init_regs(chip);
> > +       if (chip->in_workq)
> > +               return;
> >          chip->init = 1;
> >          mb();
> > -       flush_delayed_work(&chip->work);
> > -       ak4114_init_regs(chip);
> > +       cancel_delayed_work_sync(&chip->work);
> >          /* bring up statistics / event queing */
> >          chip->init = 0;
> >          if (chip->kctls[0])
> > @@ -612,10 +614,12 @@ static void ak4114_stats(struct work_struct *work)
> >   {
> >          struct ak4114 *chip = container_of(work, struct ak4114, work.work);
> > 
> > -       if (!chip->init)
> > +       chip->in_workq = true;
> > +       if (!chip->init) {
> >                  snd_ak4114_check_rate_and_errors(chip, chip->check_flags);
> > -
> > -       schedule_delayed_work(&chip->work, HZ / 10);
> > +               schedule_delayed_work(&chip->work, HZ / 10);
> > +       }
> > +       chip->in_workq = false;
> >   }
> > 
> >   EXPORT_SYMBOL(snd_ak4114_create);
> > 
> > 
> > >
> > >>> The HZ/10 isn't that bad, but the problem is that it's unconditionally
> > >>> running even if user doesn't need/want.
> > >>
> > >> It is useful only for the external clock mode. In fact the detection of
> > >> incoming SPDIF rate is not reliable for internal clock in Juli (while it
> > >> works just fine in Quartet, its FPGA pins configure the SPDIF receiver
> > >> differently). IMO the thread could be running only when clock is
> > >> switched to external.
> > >
> > > Yeah, we can do some smart task change in addition to manual on/off.
> > > Maybe it's good to have an enum control for that.
> > 
> > I am not sure users would want/need to disable a feature which detects 
> > incoming samplerate. IMO if the work thread is running only in the 
> > external clock mode, nothing more is needed.
> 
> Hm, but you can still see the other attributes of SPDIF input frames,
> right?  Or all these useless when the clock is set to internal?
> If so, it'd be easy to add the dynamic turn on/off per the clock
> mode.

The patch below can be applied on top of the patch series I've sent.


Takashi

---
diff mbox

Patch

diff --git a/include/sound/ak4114.h b/include/sound/ak4114.h
index b6feb7e225f2..9adcd06e4134 100644
--- a/include/sound/ak4114.h
+++ b/include/sound/ak4114.h
@@ -199,6 +199,7 @@  int snd_ak4114_build(struct ak4114 *ak4114,
                      struct snd_pcm_substream *capture_substream);
 int snd_ak4114_external_rate(struct ak4114 *ak4114);
 int snd_ak4114_check_rate_and_errors(struct ak4114 *ak4114, unsigned int flags);
+void snd_ak4114_enable_check_rate(struct ak4114 *chip, bool enable);
 
 #ifdef CONFIG_PM
 void snd_ak4114_suspend(struct ak4114 *chip);
diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c
index 5a4cf3fab4ae..497d50e0a6d5 100644
--- a/sound/i2c/other/ak4114.c
+++ b/sound/i2c/other/ak4114.c
@@ -64,10 +64,21 @@  static void reg_dump(struct ak4114 *ak4114)
 }
 #endif
 
-static void snd_ak4114_free(struct ak4114 *chip)
+static void snd_ak4114_disable_work(struct ak4114 *chip)
 {
 	atomic_inc(&chip->wq_processing);	/* don't schedule new work */
 	cancel_delayed_work_sync(&chip->work);
+}
+
+static void snd_ak4114_enable_work(struct ak4114 *chip)
+{
+	if (atomic_dec_and_test(&chip->wq_processing))
+		schedule_delayed_work(&chip->work, HZ / 10);
+}
+
+static void snd_ak4114_free(struct ak4114 *chip)
+{
+	snd_ak4114_disable_work(chip);
 	kfree(chip);
 }
 
@@ -161,8 +172,7 @@  void snd_ak4114_reinit(struct ak4114 *chip)
 	ak4114_init_regs(chip);
 	mutex_unlock(&chip->reinit_mutex);
 	/* bring up statistics / event queing */
-	if (atomic_dec_and_test(&chip->wq_processing))
-		schedule_delayed_work(&chip->work, HZ / 10);
+	snd_ak4114_enable_work(chip);
 }
 EXPORT_SYMBOL(snd_ak4114_reinit);
 
@@ -506,6 +516,7 @@  int snd_ak4114_build(struct ak4114 *ak4114,
 	}
 	snd_ak4114_proc_init(ak4114);
 	/* trigger workq */
+	ak4114->check_rate_enabled = true;
 	schedule_delayed_work(&ak4114->work, HZ / 10);
 	return 0;
 }
@@ -621,15 +632,27 @@  static void ak4114_stats(struct work_struct *work)
 
 	if (atomic_inc_return(&chip->wq_processing) == 1)
 		snd_ak4114_check_rate_and_errors(chip, chip->check_flags);
-	if (atomic_dec_and_test(&chip->wq_processing))
-		schedule_delayed_work(&chip->work, HZ / 10);
+	snd_ak4114_enable_work(chip);
+}
+
+void snd_ak4114_enable_check_rate(struct ak4114 *chip, bool enable)
+{
+	mutex_lock(&chip->reinit_mutex);
+	changed = chip->check_rate_enabled != enable;
+	chip->check_rate_enabled = enable;
+	mutex_unlock(&chip->reinit_mutex);
+	if (!changed)
+		return;
+	if (enable)
+		snd_ak4114_enable_work(chip);
+	else
+		snd_ak4114_disable_work(chip);
 }
 
 #ifdef CONFIG_PM
 void snd_ak4114_suspend(struct ak4114 *chip)
 {
-	atomic_inc(&chip->wq_processing); /* don't schedule new work */
-	cancel_delayed_work_sync(&chip->work);
+	snd_ak4114_disable_work(chip);
 }
 EXPORT_SYMBOL(snd_ak4114_suspend);
 
diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c
index 4f0213427152..5134833d0fa8 100644
--- a/sound/pci/ice1712/juli.c
+++ b/sound/pci/ice1712/juli.c
@@ -475,8 +475,13 @@  static int juli_add_controls(struct snd_ice1712 *ice)
 		return err;
 
 	/* only capture SPDIF over AK4114 */
-	return snd_ak4114_build(spec->ak4114, NULL,
+	err = snd_ak4114_build(spec->ak4114, NULL,
 			ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
+	if (err < 0)
+		return err;
+
+	snd_ak4114_enable_check_rate(spec->ak4114, ice->is_spdif_master(ice));
+	return 0;
 }
 
 /*
@@ -530,6 +535,7 @@  static unsigned int juli_get_rate(struct snd_ice1712 *ice)
 /* setting new rate */
 static void juli_set_rate(struct snd_ice1712 *ice, unsigned int rate)
 {
+	struct juli_spec *spec = ice->spec;
 	unsigned int old, new;
 	unsigned char val;
 
@@ -543,6 +549,7 @@  static void juli_set_rate(struct snd_ice1712 *ice, unsigned int rate)
 	/* switching to external clock - supplied by external circuits */
 	val = inb(ICEMT1724(ice, RATE));
 	outb(val | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE));
+	snd_ak4114_enable_check_rate(spec->ak4114, false);
 }
 
 static inline unsigned char juli_set_mclk(struct snd_ice1712 *ice,
@@ -555,11 +562,13 @@  static inline unsigned char juli_set_mclk(struct snd_ice1712 *ice,
 /* setting clock to external - SPDIF */
 static int juli_set_spdif_clock(struct snd_ice1712 *ice, int type)
 {
+	struct juli_spec *spec = ice->spec;
 	unsigned int old;
 	old = ice->gpio.get_data(ice);
 	/* external clock (= 0), multiply 1x, 48kHz */
 	ice->gpio.set_data(ice, (old & ~GPIO_RATE_MASK) | GPIO_MULTI_1X |
 			GPIO_FREQ_48KHZ);
+	snd_ak4114_enable_check_rate(spec->ak4114, true);
 	return 0;
 }