Merge branch 'topic/hda' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound...
authorJaroslav Kysela <perex@perex.cz>
Fri, 8 Jan 2010 08:11:18 +0000 (09:11 +0100)
committerJaroslav Kysela <perex@perex.cz>
Fri, 8 Jan 2010 08:11:18 +0000 (09:11 +0100)
1  2 
sound/core/pcm_lib.c
sound/pci/hda/patch_conexant.c

diff --combined sound/core/pcm_lib.c
index c7b35b20e65992048f40e616b2812829e1760ee9,a27545b23ee9ff031e8eb98346a492516db42b8b..0ee7e807c964bb74587b7635f84f5d67023463bd
@@@ -126,6 -126,17 +126,6 @@@ void snd_pcm_playback_silence(struct sn
        }
  }
  
 -#ifdef CONFIG_SND_PCM_XRUN_DEBUG
 -#define xrun_debug(substream, mask)   ((substream)->pstr->xrun_debug & (mask))
 -#else
 -#define xrun_debug(substream, mask)   0
 -#endif
 -
 -#define dump_stack_on_xrun(substream) do {            \
 -              if (xrun_debug(substream, 2))           \
 -                      dump_stack();                   \
 -      } while (0)
 -
  static void pcm_debug_name(struct snd_pcm_substream *substream,
                           char *name, size_t len)
  {
                 substream->number);
  }
  
 +#define XRUN_DEBUG_BASIC      (1<<0)
 +#define XRUN_DEBUG_STACK      (1<<1)  /* dump also stack */
 +#define XRUN_DEBUG_JIFFIESCHECK       (1<<2)  /* do jiffies check */
 +#define XRUN_DEBUG_PERIODUPDATE       (1<<3)  /* full period update info */
 +#define XRUN_DEBUG_HWPTRUPDATE        (1<<4)  /* full hwptr update info */
 +#define XRUN_DEBUG_LOG                (1<<5)  /* show last 10 positions on err */
 +#define XRUN_DEBUG_LOGONCE    (1<<6)  /* do above only once */
 +
 +#ifdef CONFIG_SND_PCM_XRUN_DEBUG
 +
 +#define xrun_debug(substream, mask) \
 +                      ((substream)->pstr->xrun_debug & (mask))
 +
 +#define dump_stack_on_xrun(substream) do {                    \
 +              if (xrun_debug(substream, XRUN_DEBUG_STACK))    \
 +                      dump_stack();                           \
 +      } while (0)
 +
  static void xrun(struct snd_pcm_substream *substream)
  {
        struct snd_pcm_runtime *runtime = substream->runtime;
        if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
                snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
        snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
 -      if (xrun_debug(substream, 1)) {
 +      if (xrun_debug(substream, XRUN_DEBUG_BASIC)) {
                char name[16];
                pcm_debug_name(substream, name, sizeof(name));
                snd_printd(KERN_DEBUG "XRUN: %s\n", name);
        }
  }
  
 -static snd_pcm_uframes_t
 -snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream,
 -                        struct snd_pcm_runtime *runtime)
 -{
 +#define hw_ptr_error(substream, fmt, args...)                         \
 +      do {                                                            \
 +              if (xrun_debug(substream, XRUN_DEBUG_BASIC)) {          \
 +                      xrun_log_show(substream);                       \
 +                      if (printk_ratelimit()) {                       \
 +                              snd_printd("PCM: " fmt, ##args);        \
 +                      }                                               \
 +                      dump_stack_on_xrun(substream);                  \
 +              }                                                       \
 +      } while (0)
 +
 +#define XRUN_LOG_CNT  10
 +
 +struct hwptr_log_entry {
 +      unsigned long jiffies;
        snd_pcm_uframes_t pos;
 +      snd_pcm_uframes_t period_size;
 +      snd_pcm_uframes_t buffer_size;
 +      snd_pcm_uframes_t old_hw_ptr;
 +      snd_pcm_uframes_t hw_ptr_base;
 +};
  
 -      pos = substream->ops->pointer(substream);
 -      if (pos == SNDRV_PCM_POS_XRUN)
 -              return pos; /* XRUN */
 -      if (pos >= runtime->buffer_size) {
 -              if (printk_ratelimit()) {
 -                      char name[16];
 -                      pcm_debug_name(substream, name, sizeof(name));
 -                      snd_printd(KERN_ERR  "BUG: %s, pos = 0x%lx, "
 -                                 "buffer size = 0x%lx, period size = 0x%lx\n",
 -                                 name, pos, runtime->buffer_size,
 -                                 runtime->period_size);
 -              }
 -              pos = 0;
 +struct snd_pcm_hwptr_log {
 +      unsigned int idx;
 +      unsigned int hit: 1;
 +      struct hwptr_log_entry entries[XRUN_LOG_CNT];
 +};
 +
 +static void xrun_log(struct snd_pcm_substream *substream,
 +                   snd_pcm_uframes_t pos)
 +{
 +      struct snd_pcm_runtime *runtime = substream->runtime;
 +      struct snd_pcm_hwptr_log *log = runtime->hwptr_log;
 +      struct hwptr_log_entry *entry;
 +
 +      if (log == NULL) {
 +              log = kzalloc(sizeof(*log), GFP_ATOMIC);
 +              if (log == NULL)
 +                      return;
 +              runtime->hwptr_log = log;
 +      } else {
 +              if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit)
 +                      return;
        }
 -      pos -= pos % runtime->min_align;
 -      return pos;
 +      entry = &log->entries[log->idx];
 +      entry->jiffies = jiffies;
 +      entry->pos = pos;
 +      entry->period_size = runtime->period_size;
 +      entry->buffer_size = runtime->buffer_size;;
 +      entry->old_hw_ptr = runtime->status->hw_ptr;
 +      entry->hw_ptr_base = runtime->hw_ptr_base;
 +      log->idx = (log->idx + 1) % XRUN_LOG_CNT;
  }
  
 -static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream,
 -                                    struct snd_pcm_runtime *runtime)
 +static void xrun_log_show(struct snd_pcm_substream *substream)
 +{
 +      struct snd_pcm_hwptr_log *log = substream->runtime->hwptr_log;
 +      struct hwptr_log_entry *entry;
 +      char name[16];
 +      unsigned int idx;
 +      int cnt;
 +
 +      if (log == NULL)
 +              return;
 +      if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit)
 +              return;
 +      pcm_debug_name(substream, name, sizeof(name));
 +      for (cnt = 0, idx = log->idx; cnt < XRUN_LOG_CNT; cnt++) {
 +              entry = &log->entries[idx];
 +              if (entry->period_size == 0)
 +                      break;
 +              snd_printd("hwptr log: %s: j=%lu, pos=%ld/%ld/%ld, "
 +                         "hwptr=%ld/%ld\n",
 +                         name, entry->jiffies, (unsigned long)entry->pos,
 +                         (unsigned long)entry->period_size,
 +                         (unsigned long)entry->buffer_size,
 +                         (unsigned long)entry->old_hw_ptr,
 +                         (unsigned long)entry->hw_ptr_base);
 +              idx++;
 +              idx %= XRUN_LOG_CNT;
 +      }
 +      log->hit = 1;
 +}
 +
 +#else /* ! CONFIG_SND_PCM_XRUN_DEBUG */
 +
 +#define xrun_debug(substream, mask)   0
 +#define xrun(substream)                       do { } while (0)
 +#define hw_ptr_error(substream, fmt, args...) do { } while (0)
 +#define xrun_log(substream, pos)      do { } while (0)
 +#define xrun_log_show(substream)      do { } while (0)
 +
 +#endif
 +
 +int snd_pcm_update_state(struct snd_pcm_substream *substream,
 +                       struct snd_pcm_runtime *runtime)
  {
        snd_pcm_uframes_t avail;
  
                        return -EPIPE;
                }
        }
 -      if (avail >= runtime->control->avail_min)
 +      if (!runtime->nowake && avail >= runtime->control->avail_min)
                wake_up(&runtime->sleep);
        return 0;
  }
  
 -#define hw_ptr_error(substream, fmt, args...)                         \
 -      do {                                                            \
 -              if (xrun_debug(substream, 1)) {                         \
 -                      if (printk_ratelimit()) {                       \
 -                              snd_printd("PCM: " fmt, ##args);        \
 -                      }                                               \
 -                      dump_stack_on_xrun(substream);                  \
 -              }                                                       \
 -      } while (0)
 -
 -static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
 +static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
 +                                unsigned int in_interrupt)
  {
        struct snd_pcm_runtime *runtime = substream->runtime;
        snd_pcm_uframes_t pos;
 -      snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_ptr_interrupt, hw_base;
 +      snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base;
        snd_pcm_sframes_t hdelta, delta;
        unsigned long jdelta;
  
        old_hw_ptr = runtime->status->hw_ptr;
 -      pos = snd_pcm_update_hw_ptr_pos(substream, runtime);
 +      pos = substream->ops->pointer(substream);
        if (pos == SNDRV_PCM_POS_XRUN) {
                xrun(substream);
                return -EPIPE;
        }
 -      if (xrun_debug(substream, 8)) {
 -              char name[16];
 -              pcm_debug_name(substream, name, sizeof(name));
 -              snd_printd("period_update: %s: pos=0x%x/0x%x/0x%x, "
 -                         "hwptr=0x%lx, hw_base=0x%lx, hw_intr=0x%lx\n",
 -                         name, (unsigned int)pos,
 -                         (unsigned int)runtime->period_size,
 -                         (unsigned int)runtime->buffer_size,
 -                         (unsigned long)old_hw_ptr,
 -                         (unsigned long)runtime->hw_ptr_base,
 -                         (unsigned long)runtime->hw_ptr_interrupt);
 +      if (pos >= runtime->buffer_size) {
 +              if (printk_ratelimit()) {
 +                      char name[16];
 +                      pcm_debug_name(substream, name, sizeof(name));
 +                      xrun_log_show(substream);
 +                      snd_printd(KERN_ERR  "BUG: %s, pos = %ld, "
 +                                 "buffer size = %ld, period size = %ld\n",
 +                                 name, pos, runtime->buffer_size,
 +                                 runtime->period_size);
 +              }
 +              pos = 0;
        }
 +      pos -= pos % runtime->min_align;
 +      if (xrun_debug(substream, XRUN_DEBUG_LOG))
 +              xrun_log(substream, pos);
        hw_base = runtime->hw_ptr_base;
        new_hw_ptr = hw_base + pos;
 -      hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size;
 -      delta = new_hw_ptr - hw_ptr_interrupt;
 -      if (hw_ptr_interrupt >= runtime->boundary) {
 -              hw_ptr_interrupt -= runtime->boundary;
 -              if (hw_base < runtime->boundary / 2)
 -                      /* hw_base was already lapped; recalc delta */
 -                      delta = new_hw_ptr - hw_ptr_interrupt;
 -      }
 -      if (delta < 0) {
 -              if (runtime->periods == 1 || new_hw_ptr < old_hw_ptr)
 -                      delta += runtime->buffer_size;
 -              if (delta < 0) {
 -                      hw_ptr_error(substream, 
 -                                   "Unexpected hw_pointer value "
 -                                   "(stream=%i, pos=%ld, intr_ptr=%ld)\n",
 -                                   substream->stream, (long)pos,
 -                                   (long)hw_ptr_interrupt);
 -#if 1
 -                      /* simply skipping the hwptr update seems more
 -                       * robust in some cases, e.g. on VMware with
 -                       * inaccurate timer source
 -                       */
 -                      return 0; /* skip this update */
 -#else
 -                      /* rebase to interrupt position */
 -                      hw_base = new_hw_ptr = hw_ptr_interrupt;
 -                      /* align hw_base to buffer_size */
 -                      hw_base -= hw_base % runtime->buffer_size;
 -                      delta = 0;
 -#endif
 -              } else {
 +      if (in_interrupt) {
 +              /* we know that one period was processed */
 +              /* delta = "expected next hw_ptr" for in_interrupt != 0 */
 +              delta = old_hw_ptr - (old_hw_ptr % runtime->period_size)
 +                      + runtime->period_size;
 +              if (delta > new_hw_ptr) {
                        hw_base += runtime->buffer_size;
                        if (hw_base >= runtime->boundary)
                                hw_base = 0;
                        new_hw_ptr = hw_base + pos;
 +                      goto __delta;
                }
        }
 +      /* new_hw_ptr might be lower than old_hw_ptr in case when */
 +      /* pointer crosses the end of the ring buffer */
 +      if (new_hw_ptr < old_hw_ptr) {
 +              hw_base += runtime->buffer_size;
 +              if (hw_base >= runtime->boundary)
 +                      hw_base = 0;
 +              new_hw_ptr = hw_base + pos;
 +      }
 +      __delta:
 +      delta = (new_hw_ptr - old_hw_ptr) % runtime->boundary;
 +      if (xrun_debug(substream, in_interrupt ?
 +                      XRUN_DEBUG_PERIODUPDATE : XRUN_DEBUG_HWPTRUPDATE)) {
 +              char name[16];
 +              pcm_debug_name(substream, name, sizeof(name));
 +              snd_printd("%s_update: %s: pos=%u/%u/%u, "
 +                         "hwptr=%ld/%ld/%ld/%ld\n",
 +                         in_interrupt ? "period" : "hwptr",
 +                         name,
 +                         (unsigned int)pos,
 +                         (unsigned int)runtime->period_size,
 +                         (unsigned int)runtime->buffer_size,
 +                         (unsigned long)delta,
 +                         (unsigned long)old_hw_ptr,
 +                         (unsigned long)new_hw_ptr,
 +                         (unsigned long)runtime->hw_ptr_base);
 +      }
 +      /* something must be really wrong */
 +      if (delta >= runtime->buffer_size + runtime->period_size) {
 +              hw_ptr_error(substream,
 +                             "Unexpected hw_pointer value %s"
 +                             "(stream=%i, pos=%ld, new_hw_ptr=%ld, "
 +                             "old_hw_ptr=%ld)\n",
 +                                   in_interrupt ? "[Q] " : "[P]",
 +                                   substream->stream, (long)pos,
 +                                   (long)new_hw_ptr, (long)old_hw_ptr);
 +              return 0;
 +      }
  
        /* Do jiffies check only in xrun_debug mode */
 -      if (!xrun_debug(substream, 4))
 +      if (!xrun_debug(substream, XRUN_DEBUG_JIFFIESCHECK))
                goto no_jiffies_check;
  
        /* Skip the jiffies check for hardwares with BATCH flag.
         */
        if (runtime->hw.info & SNDRV_PCM_INFO_BATCH)
                goto no_jiffies_check;
 -      hdelta = new_hw_ptr - old_hw_ptr;
 +      hdelta = delta;
        if (hdelta < runtime->delay)
                goto no_jiffies_check;
        hdelta -= runtime->delay;
                delta = jdelta /
                        (((runtime->period_size * HZ) / runtime->rate)
                                                                + HZ/100);
 +              /* move new_hw_ptr according jiffies not pos variable */
 +              new_hw_ptr = old_hw_ptr;
 +              /* use loop to avoid checks for delta overflows */
 +              /* the delta value is small or zero in most cases */
 +              while (delta > 0) {
 +                      new_hw_ptr += runtime->period_size;
 +                      if (new_hw_ptr >= runtime->boundary)
 +                              new_hw_ptr -= runtime->boundary;
 +                      delta--;
 +              }
 +              /* align hw_base to buffer_size */
 +              hw_base = new_hw_ptr - (new_hw_ptr % runtime->buffer_size);
 +              delta = 0;
                hw_ptr_error(substream,
 -                           "hw_ptr skipping! [Q] "
 +                           "hw_ptr skipping! %s"
                             "(pos=%ld, delta=%ld, period=%ld, "
 -                           "jdelta=%lu/%lu/%lu)\n",
 +                           "jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n",
 +                           in_interrupt ? "[Q] " : "",
                             (long)pos, (long)hdelta,
                             (long)runtime->period_size, jdelta,
 -                           ((hdelta * HZ) / runtime->rate), delta);
 -              hw_ptr_interrupt = runtime->hw_ptr_interrupt +
 -                                 runtime->period_size * delta;
 -              if (hw_ptr_interrupt >= runtime->boundary)
 -                      hw_ptr_interrupt -= runtime->boundary;
 -              /* rebase to interrupt position */
 -              hw_base = new_hw_ptr = hw_ptr_interrupt;
 -              /* align hw_base to buffer_size */
 -              hw_base -= hw_base % runtime->buffer_size;
 -              delta = 0;
 +                           ((hdelta * HZ) / runtime->rate), delta,
 +                           (unsigned long)old_hw_ptr,
 +                           (unsigned long)new_hw_ptr);
        }
   no_jiffies_check:
        if (delta > runtime->period_size + runtime->period_size / 2) {
                hw_ptr_error(substream,
 -                           "Lost interrupts? "
 -                           "(stream=%i, delta=%ld, intr_ptr=%ld)\n",
 +                           "Lost interrupts? %s"
 +                           "(stream=%i, delta=%ld, new_hw_ptr=%ld, "
 +                           "old_hw_ptr=%ld)\n",
 +                           in_interrupt ? "[Q] " : "",
                             substream->stream, (long)delta,
 -                           (long)hw_ptr_interrupt);
 -              /* rebase hw_ptr_interrupt */
 -              hw_ptr_interrupt =
 -                      new_hw_ptr - new_hw_ptr % runtime->period_size;
 +                           (long)new_hw_ptr,
 +                           (long)old_hw_ptr);
        }
 -      runtime->hw_ptr_interrupt = hw_ptr_interrupt;
 +
 +      if (runtime->status->hw_ptr == new_hw_ptr)
 +              return 0;
  
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
            runtime->silence_size > 0)
                snd_pcm_playback_silence(substream, new_hw_ptr);
  
 -      if (runtime->status->hw_ptr == new_hw_ptr)
 -              return 0;
 -
        runtime->hw_ptr_base = hw_base;
        runtime->status->hw_ptr = new_hw_ptr;
        runtime->hw_ptr_jiffies = jiffies;
        if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
                snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
  
 -      return snd_pcm_update_hw_ptr_post(substream, runtime);
 +      return snd_pcm_update_state(substream, runtime);
  }
  
  /* CAUTION: call it with irq disabled */
  int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
  {
 -      struct snd_pcm_runtime *runtime = substream->runtime;
 -      snd_pcm_uframes_t pos;
 -      snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base;
 -      snd_pcm_sframes_t delta;
 -      unsigned long jdelta;
 -
 -      old_hw_ptr = runtime->status->hw_ptr;
 -      pos = snd_pcm_update_hw_ptr_pos(substream, runtime);
 -      if (pos == SNDRV_PCM_POS_XRUN) {
 -              xrun(substream);
 -              return -EPIPE;
 -      }
 -      if (xrun_debug(substream, 16)) {
 -              char name[16];
 -              pcm_debug_name(substream, name, sizeof(name));
 -              snd_printd("hw_update: %s: pos=0x%x/0x%x/0x%x, "
 -                         "hwptr=0x%lx, hw_base=0x%lx, hw_intr=0x%lx\n",
 -                         name, (unsigned int)pos,
 -                         (unsigned int)runtime->period_size,
 -                         (unsigned int)runtime->buffer_size,
 -                         (unsigned long)old_hw_ptr,
 -                         (unsigned long)runtime->hw_ptr_base,
 -                         (unsigned long)runtime->hw_ptr_interrupt);
 -      }
 -
 -      hw_base = runtime->hw_ptr_base;
 -      new_hw_ptr = hw_base + pos;
 -
 -      delta = new_hw_ptr - old_hw_ptr;
 -      jdelta = jiffies - runtime->hw_ptr_jiffies;
 -      if (delta < 0) {
 -              delta += runtime->buffer_size;
 -              if (delta < 0) {
 -                      hw_ptr_error(substream, 
 -                                   "Unexpected hw_pointer value [2] "
 -                                   "(stream=%i, pos=%ld, old_ptr=%ld, jdelta=%li)\n",
 -                                   substream->stream, (long)pos,
 -                                   (long)old_hw_ptr, jdelta);
 -                      return 0;
 -              }
 -              hw_base += runtime->buffer_size;
 -              if (hw_base >= runtime->boundary)
 -                      hw_base = 0;
 -              new_hw_ptr = hw_base + pos;
 -      }
 -      /* Do jiffies check only in xrun_debug mode */
 -      if (!xrun_debug(substream, 4))
 -              goto no_jiffies_check;
 -      if (delta < runtime->delay)
 -              goto no_jiffies_check;
 -      delta -= runtime->delay;
 -      if (((delta * HZ) / runtime->rate) > jdelta + HZ/100) {
 -              hw_ptr_error(substream,
 -                           "hw_ptr skipping! "
 -                           "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n",
 -                           (long)pos, (long)delta,
 -                           (long)runtime->period_size, jdelta,
 -                           ((delta * HZ) / runtime->rate));
 -              return 0;
 -      }
 - no_jiffies_check:
 -      if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
 -          runtime->silence_size > 0)
 -              snd_pcm_playback_silence(substream, new_hw_ptr);
 -
 -      if (runtime->status->hw_ptr == new_hw_ptr)
 -              return 0;
 -
 -      runtime->hw_ptr_base = hw_base;
 -      runtime->status->hw_ptr = new_hw_ptr;
 -      runtime->hw_ptr_jiffies = jiffies;
 -      if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
 -              snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
 -
 -      return snd_pcm_update_hw_ptr_post(substream, runtime);
 +      return snd_pcm_update_hw_ptr0(substream, 0);
  }
  
  /**
@@@ -774,7 -758,7 +774,7 @@@ int snd_interval_ratnum(struct snd_inte
                int diff;
                if (q == 0)
                        q = 1;
-               den = div_down(num, q);
+               den = div_up(num, q);
                if (den < rats[k].den_min)
                        continue;
                if (den > rats[k].den_max)
                        i->empty = 1;
                        return -EINVAL;
                }
-               den = div_up(num, q);
+               den = div_down(num, q);
                if (den > rats[k].den_max)
                        continue;
                if (den < rats[k].den_min)
@@@ -1659,7 -1643,7 +1659,7 @@@ void snd_pcm_period_elapsed(struct snd_
  
        snd_pcm_stream_lock_irqsave(substream, flags);
        if (!snd_pcm_running(substream) ||
 -          snd_pcm_update_hw_ptr_interrupt(substream) < 0)
 +          snd_pcm_update_hw_ptr0(substream, 1) < 0)
                goto _end;
  
        if (substream->timer_running)
@@@ -1792,7 -1776,6 +1792,7 @@@ static snd_pcm_sframes_t snd_pcm_lib_wr
                goto _end_unlock;
        }
  
 +      runtime->nowake = 1;
        while (size > 0) {
                snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
                snd_pcm_uframes_t avail;
                if (frames > cont)
                        frames = cont;
                if (snd_BUG_ON(!frames)) {
 +                      runtime->nowake = 0;
                        snd_pcm_stream_unlock_irq(substream);
                        return -EINVAL;
                }
                appl_ptr = runtime->control->appl_ptr;
                appl_ofs = appl_ptr % runtime->buffer_size;
                snd_pcm_stream_unlock_irq(substream);
 -              if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0)
 -                      goto _end;
 +              err = transfer(substream, appl_ofs, data, offset, frames);
                snd_pcm_stream_lock_irq(substream);
 +              if (err < 0)
 +                      goto _end_unlock;
                switch (runtime->status->state) {
                case SNDRV_PCM_STATE_XRUN:
                        err = -EPIPE;
                }
        }
   _end_unlock:
 +      runtime->nowake = 0;
 +      if (xfer > 0 && err >= 0)
 +              snd_pcm_update_state(substream, runtime);
        snd_pcm_stream_unlock_irq(substream);
 - _end:
        return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
  }
  
@@@ -2014,7 -1993,6 +2014,7 @@@ static snd_pcm_sframes_t snd_pcm_lib_re
                goto _end_unlock;
        }
  
 +      runtime->nowake = 1;
        while (size > 0) {
                snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
                snd_pcm_uframes_t avail;
                if (frames > cont)
                        frames = cont;
                if (snd_BUG_ON(!frames)) {
 +                      runtime->nowake = 0;
                        snd_pcm_stream_unlock_irq(substream);
                        return -EINVAL;
                }
                appl_ptr = runtime->control->appl_ptr;
                appl_ofs = appl_ptr % runtime->buffer_size;
                snd_pcm_stream_unlock_irq(substream);
 -              if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0)
 -                      goto _end;
 +              err = transfer(substream, appl_ofs, data, offset, frames);
                snd_pcm_stream_lock_irq(substream);
 +              if (err < 0)
 +                      goto _end_unlock;
                switch (runtime->status->state) {
                case SNDRV_PCM_STATE_XRUN:
                        err = -EPIPE;
                xfer += frames;
        }
   _end_unlock:
 +      runtime->nowake = 0;
 +      if (xfer > 0 && err >= 0)
 +              snd_pcm_update_state(substream, runtime);
        snd_pcm_stream_unlock_irq(substream);
 - _end:
        return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
  }
  
index b20c640f75025c5c92be3f25eccd0623eeee93b5,947785f43b28566f782d04dc721e0786c95a2ac0..01e46ba726905e10b1c31a3d251c210a8e126851
@@@ -29,6 -29,7 +29,7 @@@
  
  #include "hda_codec.h"
  #include "hda_local.h"
+ #include "hda_beep.h"
  
  #define CXT_PIN_DIR_IN              0x00
  #define CXT_PIN_DIR_OUT             0x01
@@@ -111,6 -112,7 +112,7 @@@ struct conexant_spec 
        unsigned int dell_automute;
        unsigned int port_d_mode;
        unsigned char ext_mic_bias;
+       unsigned int dell_vostro;
  };
  
  static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo,
@@@ -476,6 -478,7 +478,7 @@@ static void conexant_free(struct hda_co
                snd_array_free(&spec->jacks);
        }
  #endif
+       snd_hda_detach_beep_device(codec);
        kfree(codec->spec);
  }
  
@@@ -1720,22 -1723,6 +1723,22 @@@ static struct snd_kcontrol_new cxt5051_
        {}
  };
  
 +static struct snd_kcontrol_new cxt5051_f700_mixers[] = {
 +      HDA_CODEC_VOLUME("Mic Volume", 0x14, 0x01, HDA_INPUT),
 +      HDA_CODEC_MUTE("Mic Switch", 0x14, 0x01, HDA_INPUT),
 +      HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
 +      {
 +              .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 +              .name = "Master Playback Switch",
 +              .info = cxt_eapd_info,
 +              .get = cxt_eapd_get,
 +              .put = cxt5051_hp_master_sw_put,
 +              .private_value = 0x1a,
 +      },
 +
 +      {}
 +};
 +
  static struct hda_verb cxt5051_init_verbs[] = {
        /* Line in, Mic */
        {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
@@@ -1826,32 -1813,6 +1829,32 @@@ static struct hda_verb cxt5051_lenovo_x
        { } /* end */
  };
  
 +static struct hda_verb cxt5051_f700_init_verbs[] = {
 +      /* Line in, Mic */
 +      {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x03},
 +      {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
 +      {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0},
 +      {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0},
 +      /* SPK  */
 +      {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
 +      {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
 +      /* HP, Amp  */
 +      {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
 +      {0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
 +      /* DAC1 */
 +      {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 +      /* Record selector: Int mic */
 +      {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
 +      {0x14, AC_VERB_SET_CONNECT_SEL, 0x1},
 +      /* SPDIF route: PCM */
 +      {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
 +      /* EAPD */
 +      {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
 +      {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
 +      {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT},
 +      { } /* end */
 +};
 +
  /* initialize jack-sensing, too */
  static int cxt5051_init(struct hda_codec *codec)
  {
@@@ -1871,7 -1832,6 +1874,7 @@@ enum 
        CXT5051_HP,     /* no docking */
        CXT5051_HP_DV6736,      /* HP without mic switch */
        CXT5051_LENOVO_X200,    /* Lenovo X200 laptop */
 +      CXT5051_F700,       /* HP Compaq Presario F700 */
        CXT5051_MODELS
  };
  
@@@ -1880,7 -1840,6 +1883,7 @@@ static const char *cxt5051_models[CXT50
        [CXT5051_HP]            = "hp",
        [CXT5051_HP_DV6736]     = "hp-dv6736",
        [CXT5051_LENOVO_X200]   = "lenovo-x200",
 +      [CXT5051_F700]          = "hp 700"
  };
  
  static struct snd_pci_quirk cxt5051_cfg_tbl[] = {
                      CXT5051_LAPTOP),
        SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP),
        SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT5051_LENOVO_X200),
 +      SND_PCI_QUIRK(0x103c, 0x30ea, "Compaq Presario F700", CXT5051_F700),
        {}
  };
  
@@@ -1941,11 -1899,6 +1944,11 @@@ static int patch_cxt5051(struct hda_cod
        case CXT5051_LENOVO_X200:
                spec->init_verbs[0] = cxt5051_lenovo_x200_init_verbs;
                break;
 +      case CXT5051_F700:
 +              spec->init_verbs[0] = cxt5051_f700_init_verbs;
 +              spec->mixers[0] = cxt5051_f700_mixers;
 +              spec->no_auto_mic = 1;
 +              break;
        }
  
        return 0;
@@@ -2159,9 -2112,12 +2162,12 @@@ static int cxt5066_mic_boost_mux_enum_g
  {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        int val;
+       hda_nid_t nid = kcontrol->private_value & 0xff;
+       int inout = (kcontrol->private_value & 0x100) ?
+               AC_AMP_GET_INPUT : AC_AMP_GET_OUTPUT;
  
-       val = snd_hda_codec_read(codec, 0x17, 0,
-               AC_VERB_GET_AMP_GAIN_MUTE, AC_AMP_GET_OUTPUT);
+       val = snd_hda_codec_read(codec, nid, 0,
+               AC_VERB_GET_AMP_GAIN_MUTE, inout);
  
        ucontrol->value.enumerated.item[0] = val & AC_AMP_GAIN;
        return 0;
@@@ -2173,6 -2129,9 +2179,9 @@@ static int cxt5066_mic_boost_mux_enum_p
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        const struct hda_input_mux *imux = &cxt5066_analog_mic_boost;
        unsigned int idx;
+       hda_nid_t nid = kcontrol->private_value & 0xff;
+       int inout = (kcontrol->private_value & 0x100) ?
+               AC_AMP_SET_INPUT : AC_AMP_SET_OUTPUT;
  
        if (!imux->num_items)
                return 0;
        if (idx >= imux->num_items)
                idx = imux->num_items - 1;
  
-       snd_hda_codec_write_cache(codec, 0x17, 0,
+       snd_hda_codec_write_cache(codec, nid, 0,
                AC_VERB_SET_AMP_GAIN_MUTE,
-               AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT |
+               AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | inout |
                        imux->items[idx].index);
  
        return 1;
@@@ -2252,10 -2211,11 +2261,11 @@@ static struct snd_kcontrol_new cxt5066_
  
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Analog Mic Boost Capture Enum",
+               .name = "Ext Mic Boost Capture Enum",
                .info = cxt5066_mic_boost_mux_enum_info,
                .get = cxt5066_mic_boost_mux_enum_get,
                .put = cxt5066_mic_boost_mux_enum_put,
+               .private_value = 0x17,
        },
  
        HDA_BIND_VOL("Capture Volume", &cxt5066_bind_capture_vol_others),
        {}
  };
  
+ static struct snd_kcontrol_new cxt5066_vostro_mixers[] = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Int Mic Boost Capture Enum",
+               .info = cxt5066_mic_boost_mux_enum_info,
+               .get = cxt5066_mic_boost_mux_enum_get,
+               .put = cxt5066_mic_boost_mux_enum_put,
+               .private_value = 0x23 | 0x100,
+       },
+       HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
+       {}
+ };
  static struct hda_verb cxt5066_init_verbs[] = {
        {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port B */
        {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port C */
@@@ -2448,11 -2421,16 +2471,16 @@@ static struct hda_verb cxt5066_init_ver
  /* initialize jack-sensing, too */
  static int cxt5066_init(struct hda_codec *codec)
  {
+       struct conexant_spec *spec = codec->spec;
        snd_printdd("CXT5066: init\n");
        conexant_init(codec);
        if (codec->patch_ops.unsol_event) {
                cxt5066_hp_automute(codec);
-               cxt5066_automic(codec);
+               if (spec->dell_vostro)
+                       cxt5066_vostro_automic(codec);
+               else
+                       cxt5066_automic(codec);
        }
        return 0;
  }
@@@ -2551,7 -2529,10 +2579,10 @@@ static int patch_cxt5066(struct hda_cod
                spec->init_verbs[0] = cxt5066_init_verbs_vostro;
                spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
                spec->mixers[spec->num_mixers++] = cxt5066_mixers;
+               spec->mixers[spec->num_mixers++] = cxt5066_vostro_mixers;
                spec->port_d_mode = 0;
+               spec->dell_vostro = 1;
+               snd_hda_attach_beep_device(codec, 0x13);
  
                /* no S/PDIF out */
                spec->multiout.dig_out_nid = 0;