diff options
Diffstat (limited to 'sound')
172 files changed, 7500 insertions, 3155 deletions
diff --git a/sound/core/misc.c b/sound/core/misc.c index 30e027e..f2e8226 100644 --- a/sound/core/misc.c +++ b/sound/core/misc.c @@ -145,6 +145,8 @@ EXPORT_SYMBOL(snd_pci_quirk_lookup_id); const struct snd_pci_quirk * snd_pci_quirk_lookup(struct pci_dev *pci, const struct snd_pci_quirk *list) { + if (!pci) + return NULL; return snd_pci_quirk_lookup_id(pci->subsystem_vendor, pci->subsystem_device, list); diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 43932e8..42ded99 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -215,6 +215,7 @@ static char *snd_pcm_format_names[] = { FORMAT(G723_40_1B), FORMAT(DSD_U8), FORMAT(DSD_U16_LE), + FORMAT(DSD_U32_LE), }; const char *snd_pcm_format_name(snd_pcm_format_t format) @@ -698,6 +699,7 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) } substream->group = &substream->self_group; spin_lock_init(&substream->self_group.lock); + mutex_init(&substream->self_group.mutex); INIT_LIST_HEAD(&substream->self_group.substreams); list_add_tail(&substream->link_list, &substream->self_group.substreams); atomic_set(&substream->mmap_count, 0); diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 0032278..dfc2854 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1113,18 +1113,20 @@ int snd_interval_list(struct snd_interval *i, unsigned int count, EXPORT_SYMBOL(snd_interval_list); -static int snd_interval_step(struct snd_interval *i, unsigned int min, unsigned int step) +static int snd_interval_step(struct snd_interval *i, unsigned int step) { unsigned int n; int changed = 0; - n = (i->min - min) % step; + n = i->min % step; if (n != 0 || i->openmin) { i->min += step - n; + i->openmin = 0; changed = 1; } - n = (i->max - min) % step; + n = i->max % step; if (n != 0 || i->openmax) { i->max -= n; + i->openmax = 0; changed = 1; } if (snd_interval_checkempty(i)) { @@ -1427,7 +1429,7 @@ static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { unsigned long step = (unsigned long) rule->private; - return snd_interval_step(hw_param_interval(params, rule->var), 0, step); + return snd_interval_step(hw_param_interval(params, rule->var), step); } /** diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index 2c6fd80..ae7a0fe 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -148,6 +148,10 @@ static struct pcm_format_data pcm_formats[(INT)SNDRV_PCM_FORMAT_LAST+1] = { .width = 16, .phys = 16, .le = 1, .signd = 0, .silence = { 0x69, 0x69 }, }, + [SNDRV_PCM_FORMAT_DSD_U32_LE] = { + .width = 32, .phys = 32, .le = 1, .signd = 0, + .silence = { 0x69, 0x69, 0x69, 0x69 }, + }, /* FIXME: the following three formats are not defined properly yet */ [SNDRV_PCM_FORMAT_MPEG] = { .le = -1, .signd = -1, diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 8cd2f93..85fe1a2 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -74,11 +74,68 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream); * */ -DEFINE_RWLOCK(snd_pcm_link_rwlock); -EXPORT_SYMBOL(snd_pcm_link_rwlock); - +static DEFINE_RWLOCK(snd_pcm_link_rwlock); static DECLARE_RWSEM(snd_pcm_link_rwsem); +void snd_pcm_stream_lock(struct snd_pcm_substream *substream) +{ + if (substream->pcm->nonatomic) { + down_read(&snd_pcm_link_rwsem); + mutex_lock(&substream->self_group.mutex); + } else { + read_lock(&snd_pcm_link_rwlock); + spin_lock(&substream->self_group.lock); + } +} +EXPORT_SYMBOL_GPL(snd_pcm_stream_lock); + +void snd_pcm_stream_unlock(struct snd_pcm_substream *substream) +{ + if (substream->pcm->nonatomic) { + mutex_unlock(&substream->self_group.mutex); + up_read(&snd_pcm_link_rwsem); + } else { + spin_unlock(&substream->self_group.lock); + read_unlock(&snd_pcm_link_rwlock); + } +} +EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock); + +void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream) +{ + if (!substream->pcm->nonatomic) + local_irq_disable(); + snd_pcm_stream_lock(substream); +} +EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq); + +void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream) +{ + snd_pcm_stream_unlock(substream); + if (!substream->pcm->nonatomic) + local_irq_enable(); +} +EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irq); + +unsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream) +{ + unsigned long flags = 0; + if (!substream->pcm->nonatomic) + local_irq_save(flags); + snd_pcm_stream_lock(substream); + return flags; +} +EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave); + +void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream, + unsigned long flags) +{ + snd_pcm_stream_unlock(substream); + if (!substream->pcm->nonatomic) + local_irq_restore(flags); +} +EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irqrestore); + static inline mm_segment_t snd_enter_user(void) { mm_segment_t fs = get_fs(); @@ -727,9 +784,14 @@ static int snd_pcm_action_group(struct action_ops *ops, int res = 0; snd_pcm_group_for_each_entry(s, substream) { - if (do_lock && s != substream) - spin_lock_nested(&s->self_group.lock, - SINGLE_DEPTH_NESTING); + if (do_lock && s != substream) { + if (s->pcm->nonatomic) + mutex_lock_nested(&s->self_group.mutex, + SINGLE_DEPTH_NESTING); + else + spin_lock_nested(&s->self_group.lock, + SINGLE_DEPTH_NESTING); + } res = ops->pre_action(s, state); if (res < 0) goto _unlock; @@ -755,8 +817,12 @@ static int snd_pcm_action_group(struct action_ops *ops, if (do_lock) { /* unlock streams */ snd_pcm_group_for_each_entry(s1, substream) { - if (s1 != substream) - spin_unlock(&s1->self_group.lock); + if (s1 != substream) { + if (s->pcm->nonatomic) + mutex_unlock(&s1->self_group.mutex); + else + spin_unlock(&s1->self_group.lock); + } if (s1 == s) /* end */ break; } @@ -784,6 +850,27 @@ static int snd_pcm_action_single(struct action_ops *ops, return res; } +/* call in mutex-protected context */ +static int snd_pcm_action_mutex(struct action_ops *ops, + struct snd_pcm_substream *substream, + int state) +{ + int res; + + if (snd_pcm_stream_linked(substream)) { + if (!mutex_trylock(&substream->group->mutex)) { + mutex_unlock(&substream->self_group.mutex); + mutex_lock(&substream->group->mutex); + mutex_lock(&substream->self_group.mutex); + } + res = snd_pcm_action_group(ops, substream, state, 1); + mutex_unlock(&substream->group->mutex); + } else { + res = snd_pcm_action_single(ops, substream, state); + } + return res; +} + /* * Note: call with stream lock */ @@ -793,6 +880,9 @@ static int snd_pcm_action(struct action_ops *ops, { int res; + if (substream->pcm->nonatomic) + return snd_pcm_action_mutex(ops, substream, state); + if (snd_pcm_stream_linked(substream)) { if (!spin_trylock(&substream->group->lock)) { spin_unlock(&substream->self_group.lock); @@ -807,6 +897,29 @@ static int snd_pcm_action(struct action_ops *ops, return res; } +static int snd_pcm_action_lock_mutex(struct action_ops *ops, + struct snd_pcm_substream *substream, + int state) +{ + int res; + + down_read(&snd_pcm_link_rwsem); + if (snd_pcm_stream_linked(substream)) { + mutex_lock(&substream->group->mutex); + mutex_lock_nested(&substream->self_group.mutex, + SINGLE_DEPTH_NESTING); + res = snd_pcm_action_group(ops, substream, state, 1); + mutex_unlock(&substream->self_group.mutex); + mutex_unlock(&substream->group->mutex); + } else { + mutex_lock(&substream->self_group.mutex); + res = snd_pcm_action_single(ops, substream, state); + mutex_unlock(&substream->self_group.mutex); + } + up_read(&snd_pcm_link_rwsem); + return res; +} + /* * Note: don't use any locks before */ @@ -816,6 +929,9 @@ static int snd_pcm_action_lock_irq(struct action_ops *ops, { int res; + if (substream->pcm->nonatomic) + return snd_pcm_action_lock_mutex(ops, substream, state); + read_lock_irq(&snd_pcm_link_rwlock); if (snd_pcm_stream_linked(substream)) { spin_lock(&substream->group->lock); @@ -1634,7 +1750,8 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) down_write(&snd_pcm_link_rwsem); write_lock_irq(&snd_pcm_link_rwlock); if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN || - substream->runtime->status->state != substream1->runtime->status->state) { + substream->runtime->status->state != substream1->runtime->status->state || + substream->pcm->nonatomic != substream1->pcm->nonatomic) { res = -EBADFD; goto _end; } @@ -1646,6 +1763,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) substream->group = group; group = NULL; spin_lock_init(&substream->group->lock); + mutex_init(&substream->group->mutex); INIT_LIST_HEAD(&substream->group->substreams); list_add_tail(&substream->link_list, &substream->group->substreams); substream->group->count = 1; diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c index 8359689..e8cc169 100644 --- a/sound/drivers/vx/vx_core.c +++ b/sound/drivers/vx/vx_core.c @@ -117,7 +117,7 @@ static int vx_reset_chk(struct vx_core *chip) * * returns 0 if successful, or a negative error code. * the error code can be VX-specific, retrieved via vx_get_error(). - * NB: call with spinlock held! + * NB: call with mutex held! */ static int vx_transfer_end(struct vx_core *chip, int cmd) { @@ -155,7 +155,7 @@ static int vx_transfer_end(struct vx_core *chip, int cmd) * * returns 0 if successful, or a negative error code. * the error code can be VX-specific, retrieved via vx_get_error(). - * NB: call with spinlock held! + * NB: call with mutex held! */ static int vx_read_status(struct vx_core *chip, struct vx_rmh *rmh) { @@ -236,7 +236,7 @@ static int vx_read_status(struct vx_core *chip, struct vx_rmh *rmh) * returns 0 if successful, or a negative error code. * the error code can be VX-specific, retrieved via vx_get_error(). * - * this function doesn't call spinlock at all. + * this function doesn't call mutex lock at all. */ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh) { @@ -337,7 +337,7 @@ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh) /* - * vx_send_msg - send a DSP message with spinlock + * vx_send_msg - send a DSP message with mutex * @rmh: the rmh record to send and receive * * returns 0 if successful, or a negative error code. @@ -345,12 +345,11 @@ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh) */ int vx_send_msg(struct vx_core *chip, struct vx_rmh *rmh) { - unsigned long flags; int err; - spin_lock_irqsave(&chip->lock, flags); + mutex_lock(&chip->lock); err = vx_send_msg_nolock(chip, rmh); - spin_unlock_irqrestore(&chip->lock, flags); + mutex_unlock(&chip->lock); return err; } @@ -362,7 +361,7 @@ int vx_send_msg(struct vx_core *chip, struct vx_rmh *rmh) * returns 0 if successful, or a negative error code. * the error code can be VX-specific, retrieved via vx_get_error(). * - * this function doesn't call spinlock at all. + * this function doesn't call mutex at all. * * unlike RMH, no command is sent to DSP. */ @@ -398,19 +397,18 @@ int vx_send_rih_nolock(struct vx_core *chip, int cmd) /* - * vx_send_rih - send an RIH with spinlock + * vx_send_rih - send an RIH with mutex * @cmd: the command to send * * see vx_send_rih_nolock(). */ int vx_send_rih(struct vx_core *chip, int cmd) { - unsigned long flags; int err; - spin_lock_irqsave(&chip->lock, flags); + mutex_lock(&chip->lock); err = vx_send_rih_nolock(chip, cmd); - spin_unlock_irqrestore(&chip->lock, flags); + mutex_unlock(&chip->lock); return err; } @@ -482,30 +480,30 @@ static int vx_test_irq_src(struct vx_core *chip, unsigned int *ret) int err; vx_init_rmh(&chip->irq_rmh, CMD_TEST_IT); - spin_lock(&chip->lock); + mutex_lock(&chip->lock); err = vx_send_msg_nolock(chip, &chip->irq_rmh); if (err < 0) *ret = 0; else *ret = chip->irq_rmh.Stat[0]; - spin_unlock(&chip->lock); + mutex_unlock(&chip->lock); return err; } /* - * vx_interrupt - soft irq handler + * snd_vx_threaded_irq_handler - threaded irq handler */ -static void vx_interrupt(unsigned long private_data) +irqreturn_t snd_vx_threaded_irq_handler(int irq, void *dev) { - struct vx_core *chip = (struct vx_core *) private_data; + struct vx_core *chip = dev; unsigned int events; if (chip->chip_status & VX_STAT_IS_STALE) - return; + return IRQ_HANDLED; if (vx_test_irq_src(chip, &events) < 0) - return; + return IRQ_HANDLED; #if 0 if (events & 0x000800) @@ -519,7 +517,7 @@ static void vx_interrupt(unsigned long private_data) */ if (events & FATAL_DSP_ERROR) { snd_printk(KERN_ERR "vx_core: fatal DSP error!!\n"); - return; + return IRQ_HANDLED; } /* The start on time code conditions are filled (ie the time code @@ -534,8 +532,9 @@ static void vx_interrupt(unsigned long private_data) /* update the pcm streams */ vx_pcm_update_intr(chip, events); + return IRQ_HANDLED; } - +EXPORT_SYMBOL(snd_vx_threaded_irq_handler); /** * snd_vx_irq_handler - interrupt handler @@ -548,8 +547,8 @@ irqreturn_t snd_vx_irq_handler(int irq, void *dev) (chip->chip_status & VX_STAT_IS_STALE)) return IRQ_NONE; if (! vx_test_and_ack(chip)) - tasklet_schedule(&chip->tq); - return IRQ_HANDLED; + return IRQ_WAKE_THREAD; + return IRQ_NONE; } EXPORT_SYMBOL(snd_vx_irq_handler); @@ -790,13 +789,11 @@ struct vx_core *snd_vx_create(struct snd_card *card, struct snd_vx_hardware *hw, snd_printk(KERN_ERR "vx_core: no memory\n"); return NULL; } - spin_lock_init(&chip->lock); - spin_lock_init(&chip->irq_lock); + mutex_init(&chip->lock); chip->irq = -1; chip->hw = hw; chip->type = hw->type; chip->ops = ops; - tasklet_init(&chip->tq, vx_interrupt, (unsigned long)chip); mutex_init(&chip->mixer_mutex); chip->card = card; diff --git a/sound/drivers/vx/vx_mixer.c b/sound/drivers/vx/vx_mixer.c index c71b8d1..3b6823f 100644 --- a/sound/drivers/vx/vx_mixer.c +++ b/sound/drivers/vx/vx_mixer.c @@ -32,17 +32,15 @@ */ static void vx_write_codec_reg(struct vx_core *chip, int codec, unsigned int data) { - unsigned long flags; - if (snd_BUG_ON(!chip->ops->write_codec)) return; if (chip->chip_status & VX_STAT_IS_STALE) return; - spin_lock_irqsave(&chip->lock, flags); + mutex_lock(&chip->lock); chip->ops->write_codec(chip, codec, data); - spin_unlock_irqrestore(&chip->lock, flags); + mutex_unlock(&chip->lock); } /* @@ -178,14 +176,12 @@ void vx_reset_codec(struct vx_core *chip, int cold_reset) */ static void vx_change_audio_source(struct vx_core *chip, int src) { - unsigned long flags; - if (chip->chip_status & VX_STAT_IS_STALE) return; - spin_lock_irqsave(&chip->lock, flags); + mutex_lock(&chip->lock); chip->ops->change_audio_source(chip, src); - spin_unlock_irqrestore(&chip->lock, flags); + mutex_unlock(&chip->lock); } diff --git a/sound/drivers/vx/vx_pcm.c b/sound/drivers/vx/vx_pcm.c index deed5ef..1146727 100644 --- a/sound/drivers/vx/vx_pcm.c +++ b/sound/drivers/vx/vx_pcm.c @@ -229,7 +229,7 @@ static int vx_get_pipe_state(struct vx_core *chip, struct vx_pipe *pipe, int *st vx_init_rmh(&rmh, CMD_PIPE_STATE); vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0); - err = vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ + err = vx_send_msg(chip, &rmh); if (! err) *state = (rmh.Stat[0] & (1 << pipe->number)) ? 1 : 0; return err; @@ -280,7 +280,7 @@ static int vx_pipe_can_start(struct vx_core *chip, struct vx_pipe *pipe) vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0); rmh.Cmd[0] |= 1; - err = vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ + err = vx_send_msg(chip, &rmh); if (! err) { if (rmh.Stat[0]) err = 1; @@ -300,7 +300,7 @@ static int vx_conf_pipe(struct vx_core *chip, struct vx_pipe *pipe) if (pipe->is_capture) rmh.Cmd[0] |= COMMAND_RECORD_MASK; rmh.Cmd[1] = 1 << pipe->number; - return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ + return vx_send_msg(chip, &rmh); } /* @@ -311,7 +311,7 @@ static int vx_send_irqa(struct vx_core *chip) struct vx_rmh rmh; vx_init_rmh(&rmh, CMD_SEND_IRQA); - return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ + return vx_send_msg(chip, &rmh); } @@ -389,7 +389,7 @@ static int vx_stop_pipe(struct vx_core *chip, struct vx_pipe *pipe) struct vx_rmh rmh; vx_init_rmh(&rmh, CMD_STOP_PIPE); vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0); - return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ + return vx_send_msg(chip, &rmh); } @@ -477,7 +477,7 @@ static int vx_start_stream(struct vx_core *chip, struct vx_pipe *pipe) vx_init_rmh(&rmh, CMD_START_ONE_STREAM); vx_set_stream_cmd_params(&rmh, pipe->is_capture, pipe->number); vx_set_differed_time(chip, &rmh, pipe); - return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ + return vx_send_msg(chip, &rmh); } @@ -492,7 +492,7 @@ static int vx_stop_stream(struct vx_core *chip, struct vx_pipe *pipe) vx_init_rmh(&rmh, CMD_STOP_STREAM); vx_set_stream_cmd_params(&rmh, pipe->is_capture, pipe->number); - return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ + return vx_send_msg(chip, &rmh); } @@ -520,8 +520,6 @@ static struct snd_pcm_hardware vx_pcm_playback_hw = { }; -static void vx_pcm_delayed_start(unsigned long arg); - /* * vx_pcm_playback_open - open callback for playback */ @@ -553,7 +551,6 @@ static int vx_pcm_playback_open(struct snd_pcm_substream *subs) pipe->references++; pipe->substream = subs; - tasklet_init(&pipe->start_tq, vx_pcm_delayed_start, (unsigned long)subs); chip->playback_pipes[audio] = pipe; runtime->hw = vx_pcm_playback_hw; @@ -646,12 +643,12 @@ static int vx_pcm_playback_transfer_chunk(struct vx_core *chip, /* we don't need irqsave here, because this function * is called from either trigger callback or irq handler */ - spin_lock(&chip->lock); + mutex_lock(&chip->lock); vx_pseudo_dma_write(chip, runtime, pipe, size); err = vx_notify_end_of_buffer(chip, pipe); /* disconnect the host, SIZE_HBUF command always switches to the stream mode */ vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT); - spin_unlock(&chip->lock); + mutex_unlock(&chip->lock); return err; } @@ -728,31 +725,6 @@ static void vx_pcm_playback_update(struct vx_core *chip, } /* - * start the stream and pipe. - * this function is called from tasklet, which is invoked by the trigger - * START callback. - */ -static void vx_pcm_delayed_start(unsigned long arg) -{ - struct snd_pcm_substream *subs = (struct snd_pcm_substream *)arg; - struct vx_core *chip = subs->pcm->private_data; - struct vx_pipe *pipe = subs->runtime->private_data; - int err; - - /* printk( KERN_DEBUG "DDDD tasklet delayed start jiffies = %ld\n", jiffies);*/ - - if ((err = vx_start_stream(chip, pipe)) < 0) { - snd_printk(KERN_ERR "vx: cannot start stream\n"); - return; - } - if ((err = vx_toggle_pipe(chip, pipe, 1)) < 0) { - snd_printk(KERN_ERR "vx: cannot start pipe\n"); - return; - } - /* printk( KERN_DEBUG "dddd tasklet delayed start jiffies = %ld \n", jiffies);*/ -} - -/* * vx_pcm_playback_trigger - trigger callback for playback */ static int vx_pcm_trigger(struct snd_pcm_substream *subs, int cmd) @@ -769,11 +741,17 @@ static int vx_pcm_trigger(struct snd_pcm_substream *subs, int cmd) case SNDRV_PCM_TRIGGER_RESUME: if (! pipe->is_capture) vx_pcm_playback_transfer(chip, subs, pipe, 2); - /* FIXME: - * we trigger the pipe using tasklet, so that the interrupts are - * issued surely after the trigger is completed. - */ - tasklet_schedule(&pipe->start_tq); + err = vx_start_stream(chip, pipe); + if (err < 0) { + pr_debug("vx: cannot start stream\n"); + return err; + } + err = vx_toggle_pipe(chip, pipe, 1); + if (err < 0) { + pr_debug("vx: cannot start pipe\n"); + vx_stop_stream(chip, pipe); + return err; + } chip->pcm_running++; pipe->running = 1; break; @@ -955,7 +933,6 @@ static int vx_pcm_capture_open(struct snd_pcm_substream *subs) if (err < 0) return err; pipe->substream = subs; - tasklet_init(&pipe->start_tq, vx_pcm_delayed_start, (unsigned long)subs); chip->capture_pipes[audio] = pipe; /* check if monitoring is needed */ @@ -1082,7 +1059,7 @@ static void vx_pcm_capture_update(struct vx_core *chip, struct snd_pcm_substream count -= 3; } /* disconnect the host, SIZE_HBUF command always switches to the stream mode */ - vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT); + vx_send_rih(chip, IRQ_CONNECT_STREAM_NEXT); /* read the last pending 6 bytes */ count = DMA_READ_ALIGN; while (count > 0) { @@ -1099,7 +1076,7 @@ static void vx_pcm_capture_update(struct vx_core *chip, struct snd_pcm_substream _error: /* disconnect the host, SIZE_HBUF command always switches to the stream mode */ - vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT); + vx_send_rih(chip, IRQ_CONNECT_STREAM_NEXT); return; } @@ -1275,6 +1252,7 @@ int snd_vx_pcm_new(struct vx_core *chip) pcm->private_data = chip; pcm->private_free = snd_vx_pcm_free; pcm->info_flags = 0; + pcm->nonatomic = true; strcpy(pcm->name, chip->card->shortname); chip->pcm[i] = pcm; } diff --git a/sound/drivers/vx/vx_uer.c b/sound/drivers/vx/vx_uer.c index b0560fec..ef0b40c 100644 --- a/sound/drivers/vx/vx_uer.c +++ b/sound/drivers/vx/vx_uer.c @@ -60,9 +60,9 @@ static int vx_modify_board_inputs(struct vx_core *chip) */ static int vx_read_one_cbit(struct vx_core *chip, int index) { - unsigned long flags; int val; - spin_lock_irqsave(&chip->lock, flags); + + mutex_lock(&chip->lock); if (chip->type >= VX_TYPE_VXPOCKET) { vx_outb(chip, CSUER, 1); /* read */ vx_outb(chip, RUER, index & XX_UER_CBITS_OFFSET_MASK); @@ -72,7 +72,7 @@ static int vx_read_one_cbit(struct vx_core *chip, int index) vx_outl(chip, RUER, index & XX_UER_CBITS_OFFSET_MASK); val = (vx_inl(chip, RUER) >> 7) & 0x01; } - spin_unlock_irqrestore(&chip->lock, flags); + mutex_unlock(&chip->lock); return val; } @@ -83,9 +83,8 @@ static int vx_read_one_cbit(struct vx_core *chip, int index) */ static void vx_write_one_cbit(struct vx_core *chip, int index, int val) { - unsigned long flags; val = !!val; /* 0 or 1 */ - spin_lock_irqsave(&chip->lock, flags); + mutex_lock(&chip->lock); if (vx_is_pcmcia(chip)) { vx_outb(chip, CSUER, 0); /* write */ vx_outb(chip, RUER, (val << 7) | (index & XX_UER_CBITS_OFFSET_MASK)); @@ -93,7 +92,7 @@ static void vx_write_one_cbit(struct vx_core *chip, int index, int val) vx_outl(chip, CSUER, 0); /* write */ vx_outl(chip, RUER, (val << 7) | (index & XX_UER_CBITS_OFFSET_MASK)); } - spin_unlock_irqrestore(&chip->lock, flags); + mutex_unlock(&chip->lock); } /* @@ -190,14 +189,12 @@ static int vx_calc_clock_from_freq(struct vx_core *chip, int freq) */ static void vx_change_clock_source(struct vx_core *chip, int source) { - unsigned long flags; - /* we mute DAC to prevent clicks */ vx_toggle_dac_mute(chip, 1); - spin_lock_irqsave(&chip->lock, flags); + mutex_lock(&chip->lock); chip->ops->set_clock_source(chip, source); chip->clock_source = source; - spin_unlock_irqrestore(&chip->lock, flags); + mutex_unlock(&chip->lock); /* unmute */ vx_toggle_dac_mute(chip, 0); } @@ -209,11 +206,11 @@ static void vx_change_clock_source(struct vx_core *chip, int source) void vx_set_internal_clock(struct vx_core *chip, unsigned int freq) { int clock; - unsigned long flags; + /* Get real clock value */ clock = vx_calc_clock_from_freq(chip, freq); snd_printdd(KERN_DEBUG "set internal clock to 0x%x from freq %d\n", clock, freq); - spin_lock_irqsave(&chip->lock, flags); + mutex_lock(&chip->lock); if (vx_is_pcmcia(chip)) { vx_outb(chip, HIFREQ, (clock >> 8) & 0x0f); vx_outb(chip, LOFREQ, clock & 0xff); @@ -221,7 +218,7 @@ void vx_set_internal_clock(struct vx_core *chip, unsigned int freq) vx_outl(chip, HIFREQ, (clock >> 8) & 0x0f); vx_outl(chip, LOFREQ, clock & 0xff); } - spin_unlock_irqrestore(&chip->lock, flags); + mutex_unlock(&chip->lock); } diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c index afb1b44..21ce31f 100644 --- a/sound/pci/au88x0/au88x0.c +++ b/sound/pci/au88x0/au88x0.c @@ -48,10 +48,10 @@ static void vortex_fix_latency(struct pci_dev *vortex) { int rc; if (!(rc = pci_write_config_byte(vortex, 0x40, 0xff))) { - printk(KERN_INFO CARD_NAME + pr_info( CARD_NAME ": vortex latency is 0xff\n"); } else { - printk(KERN_WARNING CARD_NAME + pr_warn( CARD_NAME ": could not set vortex latency: pci error 0x%x\n", rc); } } @@ -70,10 +70,10 @@ static void vortex_fix_agp_bridge(struct pci_dev *via) if (!(rc = pci_read_config_byte(via, 0x42, &value)) && ((value & 0x10) || !(rc = pci_write_config_byte(via, 0x42, value | 0x10)))) { - printk(KERN_INFO CARD_NAME + pr_info( CARD_NAME ": bridge config is 0x%x\n", value | 0x10); } else { - printk(KERN_WARNING CARD_NAME + pr_warn( CARD_NAME ": could not set vortex latency: pci error 0x%x\n", rc); } } @@ -97,7 +97,7 @@ static void snd_vortex_workaround(struct pci_dev *vortex, int fix) PCI_DEVICE_ID_AMD_FE_GATE_7007, NULL); } if (via) { - printk(KERN_INFO CARD_NAME ": Activating latency workaround...\n"); + pr_info( CARD_NAME ": Activating latency workaround...\n"); vortex_fix_latency(vortex); vortex_fix_agp_bridge(via); } @@ -153,7 +153,7 @@ snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip) return err; if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0 || pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0) { - printk(KERN_ERR "error to set DMA mask\n"); + pr_err( "error to set DMA mask\n"); pci_disable_device(pci); return -ENXIO; } @@ -182,7 +182,7 @@ snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip) chip->mmio = pci_ioremap_bar(pci, 0); if (!chip->mmio) { - printk(KERN_ERR "MMIO area remap failed.\n"); + pr_err( "MMIO area remap failed.\n"); err = -ENOMEM; goto ioremap_out; } @@ -191,14 +191,14 @@ snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip) * This must be done before we do request_irq otherwise we can get spurious * interrupts that we do not handle properly and make a mess of things */ if ((err = vortex_core_init(chip)) != 0) { - printk(KERN_ERR "hw core init failed\n"); + pr_err( "hw core init failed\n"); goto core_out; } if ((err = request_irq(pci->irq, vortex_interrupt, IRQF_SHARED, KBUILD_MODNAME, chip)) != 0) { - printk(KERN_ERR "cannot grab irq\n"); + pr_err( "cannot grab irq\n"); goto irq_out; } chip->irq = pci->irq; @@ -342,10 +342,10 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) chip->rev = pci->revision; #ifdef CHIP_AU8830 if ((chip->rev) != 0xfe && (chip->rev) != 0xfa) { - printk(KERN_ALERT + pr_alert( "vortex: The revision (%x) of your card has not been seen before.\n", chip->rev); - printk(KERN_ALERT + pr_alert( "vortex: Please email the results of 'lspci -vv' to openvortex-dev@nongnu.org.\n"); snd_card_free(card); err = -ENODEV; diff --git a/sound/pci/au88x0/au88x0_a3d.c b/sound/pci/au88x0/au88x0_a3d.c index aad831a..30f760e 100644 --- a/sound/pci/au88x0/au88x0_a3d.c +++ b/sound/pci/au88x0/au88x0_a3d.c @@ -463,7 +463,7 @@ static void a3dsrc_ZeroSliceIO(a3dsrc_t * a) static void a3dsrc_ZeroState(a3dsrc_t * a) { /* - printk(KERN_DEBUG "vortex: ZeroState slice: %d, source %d\n", + pr_debug( "vortex: ZeroState slice: %d, source %d\n", a->slice, a->source); */ a3dsrc_SetAtmosState(a, 0, 0, 0, 0); @@ -489,7 +489,7 @@ static void a3dsrc_ZeroStateA3D(a3dsrc_t * a) int i, var, var2; if ((a->vortex) == NULL) { - printk(KERN_ERR "vortex: ZeroStateA3D: ERROR: a->vortex is NULL\n"); + pr_err( "vortex: ZeroStateA3D: ERROR: a->vortex is NULL\n"); return; } @@ -628,14 +628,14 @@ static void vortex_Vort3D_connect(vortex_t * v, int en) v->mixxtlk[0] = vortex_adb_checkinout(v, v->fixed_res, en, VORTEX_RESOURCE_MIXIN); if (v->mixxtlk[0] < 0) { - printk + pr_warn ("vortex: vortex_Vort3D: ERROR: not enough free mixer resources.\n"); return; } v->mixxtlk[1] = vortex_adb_checkinout(v, v->fixed_res, en, VORTEX_RESOURCE_MIXIN); if (v->mixxtlk[1] < 0) { - printk + pr_warn ("vortex: vortex_Vort3D: ERROR: not enough free mixer resources.\n"); return; } @@ -679,7 +679,7 @@ static void vortex_Vort3D_connect(vortex_t * v, int en) static void vortex_Vort3D_InitializeSource(a3dsrc_t * a, int en) { if (a->vortex == NULL) { - printk + pr_warn ("vortex: Vort3D_InitializeSource: A3D source not initialized\n"); return; } diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c index ae59dba..72e8128 100644 --- a/sound/pci/au88x0/au88x0_core.c +++ b/sound/pci/au88x0/au88x0_core.c @@ -285,7 +285,7 @@ vortex_mixer_addWTD(vortex_t * vortex, unsigned char mix, unsigned char ch) temp = hwread(vortex->mmio, prev); //printk(KERN_INFO "vortex: mixAddWTD: while addr=%x, val=%x\n", prev, temp); if ((++lifeboat) > 0xf) { - printk(KERN_ERR + pr_err( "vortex_mixer_addWTD: lifeboat overflow\n"); return 0; } @@ -303,7 +303,7 @@ vortex_mixer_delWTD(vortex_t * vortex, unsigned char mix, unsigned char ch) eax = hwread(vortex->mmio, VORTEX_MIXER_SR); if (((1 << ch) & eax) == 0) { - printk(KERN_ERR "mix ALARM %x\n", eax); + pr_err( "mix ALARM %x\n", eax); return 0; } ebp = VORTEX_MIXER_CHNBASE + (ch << 2); @@ -324,7 +324,7 @@ vortex_mixer_delWTD(vortex_t * vortex, unsigned char mix, unsigned char ch) //printk(KERN_INFO "vortex: mixdelWTD: 1 addr=%x, val=%x, src=%x\n", ebx, edx, src); while ((edx & 0xf) != mix) { if ((esi) > 0xf) { - printk(KERN_ERR + pr_err( "vortex: mixdelWTD: error lifeboat overflow\n"); return 0; } @@ -492,7 +492,7 @@ vortex_src_persist_convratio(vortex_t * vortex, unsigned char src, int ratio) hwwrite(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2), ratio); temp = hwread(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2)); if ((++lifeboat) > 0x9) { - printk(KERN_ERR "Vortex: Src cvr fail\n"); + pr_err( "Vortex: Src cvr fail\n"); break; } } @@ -545,7 +545,7 @@ vortex_src_checkratio(vortex_t * vortex, unsigned char src, hwwrite(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2), desired_ratio); if ((lifeboat++) > 15) { - printk(KERN_ERR "Vortex: could not set src-%d from %d to %d\n", + pr_err( "Vortex: could not set src-%d from %d to %d\n", src, hw_ratio, desired_ratio); break; } @@ -684,7 +684,7 @@ vortex_src_addWTD(vortex_t * vortex, unsigned char src, unsigned char ch) temp = hwread(vortex->mmio, prev); //printk(KERN_INFO "vortex: srcAddWTD: while addr=%x, val=%x\n", prev, temp); if ((++lifeboat) > 0xf) { - printk(KERN_ERR + pr_err( "vortex_src_addWTD: lifeboat overflow\n"); return 0; } @@ -703,7 +703,7 @@ vortex_src_delWTD(vortex_t * vortex, unsigned char src, unsigned char ch) eax = hwread(vortex->mmio, VORTEX_SRCBLOCK_SR); if (((1 << ch) & eax) == 0) { - printk(KERN_ERR "src alarm\n"); + pr_err( "src alarm\n"); return 0; } ebp = VORTEX_SRC_CHNBASE + (ch << 2); @@ -724,7 +724,7 @@ vortex_src_delWTD(vortex_t * vortex, unsigned char src, unsigned char ch) //printk(KERN_INFO "vortex: srcdelWTD: 1 addr=%x, val=%x, src=%x\n", ebx, edx, src); while ((edx & 0xf) != src) { if ((esi) > 0xf) { - printk + pr_warn ("vortex: srcdelWTD: error, lifeboat overflow\n"); return 0; } @@ -819,7 +819,7 @@ vortex_fifo_setadbctrl(vortex_t * vortex, int fifo, int stereo, int priority, do { temp = hwread(vortex->mmio, VORTEX_FIFO_ADBCTRL + (fifo << 2)); if (lifeboat++ > 0xbb8) { - printk(KERN_ERR + pr_err( "Vortex: vortex_fifo_setadbctrl fail\n"); break; } @@ -915,7 +915,7 @@ vortex_fifo_setwtctrl(vortex_t * vortex, int fifo, int ctrl, int priority, do { temp = hwread(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2)); if (lifeboat++ > 0xbb8) { - printk(KERN_ERR "Vortex: vortex_fifo_setwtctrl fail\n"); + pr_err( "Vortex: vortex_fifo_setwtctrl fail\n"); break; } } @@ -970,7 +970,7 @@ vortex_fifo_setwtctrl(vortex_t * vortex, int fifo, int ctrl, int priority, do { temp = hwread(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2)); if (lifeboat++ > 0xbb8) { - printk(KERN_ERR "Vortex: vortex_fifo_setwtctrl fail (hanging)\n"); + pr_err( "Vortex: vortex_fifo_setwtctrl fail (hanging)\n"); break; } } while ((temp & FIFO_RDONLY)&&(temp & FIFO_VALID)&&(temp != 0xFFFFFFFF)); @@ -1042,7 +1042,7 @@ static void vortex_fifo_init(vortex_t * vortex) for (x = NR_ADB - 1; x >= 0; x--) { hwwrite(vortex->mmio, addr, (FIFO_U0 | FIFO_U1)); if (hwread(vortex->mmio, addr) != (FIFO_U0 | FIFO_U1)) - printk(KERN_ERR "bad adb fifo reset!"); + pr_err( "bad adb fifo reset!"); vortex_fifo_clearadbdata(vortex, x, FIFO_SIZE); addr -= 4; } @@ -1053,7 +1053,7 @@ static void vortex_fifo_init(vortex_t * vortex) for (x = NR_WT - 1; x >= 0; x--) { hwwrite(vortex->mmio, addr, FIFO_U0); if (hwread(vortex->mmio, addr) != FIFO_U0) - printk(KERN_ERR + pr_err( "bad wt fifo reset (0x%08x, 0x%08x)!\n", addr, hwread(vortex->mmio, addr)); vortex_fifo_clearwtdata(vortex, x, FIFO_SIZE); @@ -1136,7 +1136,7 @@ vortex_adbdma_setbuffers(vortex_t * vortex, int adbdma, break; } /* - printk(KERN_DEBUG "vortex: cfg0 = 0x%x\nvortex: cfg1=0x%x\n", + pr_debug( "vortex: cfg0 = 0x%x\nvortex: cfg1=0x%x\n", dma->cfg0, dma->cfg1); */ hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFCFG0 + (adbdma << 3), dma->cfg0); @@ -1213,7 +1213,7 @@ static int vortex_adbdma_bufshift(vortex_t * vortex, int adbdma) if (dma->period_virt >= dma->nr_periods) dma->period_virt -= dma->nr_periods; if (delta != 1) - printk(KERN_INFO "vortex: %d virt=%d, real=%d, delta=%d\n", + pr_info( "vortex: %d virt=%d, real=%d, delta=%d\n", adbdma, dma->period_virt, dma->period_real, delta); return delta; @@ -1482,7 +1482,7 @@ static int vortex_wtdma_bufshift(vortex_t * vortex, int wtdma) dma->period_real = page; if (delta != 1) - printk(KERN_WARNING "vortex: wt virt = %d, delta = %d\n", + pr_warn( "vortex: wt virt = %d, delta = %d\n", dma->period_virt, delta); return delta; @@ -1667,7 +1667,7 @@ vortex_adb_addroutes(vortex_t * vortex, unsigned char channel, hwread(vortex->mmio, VORTEX_ADB_RTBASE + (temp << 2)) & ADB_MASK; if ((lifeboat++) > ADB_MASK) { - printk(KERN_ERR + pr_err( "vortex_adb_addroutes: unending route! 0x%x\n", *route); return; @@ -1703,7 +1703,7 @@ vortex_adb_delroutes(vortex_t * vortex, unsigned char channel, hwread(vortex->mmio, VORTEX_ADB_RTBASE + (prev << 2)) & ADB_MASK; if (((lifeboat++) > ADB_MASK) || (temp == ADB_MASK)) { - printk(KERN_ERR + pr_err( "vortex_adb_delroutes: route not found! 0x%x\n", route0); return; @@ -1967,7 +1967,7 @@ vortex_connect_codecplay(vortex_t * vortex, int en, unsigned char mixers[]) ADB_CODECOUT(0 + 4)); vortex_connection_mix_adb(vortex, en, 0x11, mixers[3], ADB_CODECOUT(1 + 4)); - /* printk(KERN_DEBUG "SDAC detected "); */ + /* pr_debug( "SDAC detected "); */ } #else // Use plain direct output to codec. @@ -2022,7 +2022,7 @@ vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype) else vortex->dma_adb[i].resources[restype] |= (1 << i); /* - printk(KERN_DEBUG + pr_debug( "vortex: ResManager: type %d out %d\n", restype, i); */ @@ -2037,7 +2037,7 @@ vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype) if (resmap[restype] & (1 << i)) { resmap[restype] &= ~(1 << i); /* - printk(KERN_DEBUG + pr_debug( "vortex: ResManager: type %d in %d\n", restype, i); */ @@ -2045,7 +2045,7 @@ vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype) } } } - printk(KERN_ERR "vortex: FATAL: ResManager: resource type %d exhausted.\n", restype); + pr_err( "vortex: FATAL: ResManager: resource type %d exhausted.\n", restype); return -ENOMEM; } @@ -2173,7 +2173,7 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir, memset(stream->resources, 0, sizeof(unsigned char) * VORTEX_RESOURCE_LAST); - printk(KERN_ERR "vortex: out of A3D sources. Sorry\n"); + pr_err( "vortex: out of A3D sources. Sorry\n"); return -EBUSY; } /* (De)Initialize A3D hardware source. */ @@ -2421,7 +2421,7 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id) hwread(vortex->mmio, VORTEX_IRQ_SOURCE); // Is at least one IRQ flag set? if (source == 0) { - printk(KERN_ERR "vortex: missing irq source\n"); + pr_err( "vortex: missing irq source\n"); return IRQ_NONE; } @@ -2429,19 +2429,19 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id) // Attend every interrupt source. if (unlikely(source & IRQ_ERR_MASK)) { if (source & IRQ_FATAL) { - printk(KERN_ERR "vortex: IRQ fatal error\n"); + pr_err( "vortex: IRQ fatal error\n"); } if (source & IRQ_PARITY) { - printk(KERN_ERR "vortex: IRQ parity error\n"); + pr_err( "vortex: IRQ parity error\n"); } if (source & IRQ_REG) { - printk(KERN_ERR "vortex: IRQ reg error\n"); + pr_err( "vortex: IRQ reg error\n"); } if (source & IRQ_FIFO) { - printk(KERN_ERR "vortex: IRQ fifo error\n"); + pr_err( "vortex: IRQ fifo error\n"); } if (source & IRQ_DMA) { - printk(KERN_ERR "vortex: IRQ dma error\n"); + pr_err( "vortex: IRQ dma error\n"); } handled = 1; } @@ -2489,7 +2489,7 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id) } if (!handled) { - printk(KERN_ERR "vortex: unknown irq source %x\n", source); + pr_err( "vortex: unknown irq source %x\n", source); } return IRQ_RETVAL(handled); } @@ -2546,7 +2546,7 @@ vortex_codec_write(struct snd_ac97 * codec, unsigned short addr, unsigned short while (!(hwread(card->mmio, VORTEX_CODEC_CTRL) & 0x100)) { udelay(100); if (lifeboat++ > POLL_COUNT) { - printk(KERN_ERR "vortex: ac97 codec stuck busy\n"); + pr_err( "vortex: ac97 codec stuck busy\n"); return; } } @@ -2572,7 +2572,7 @@ static unsigned short vortex_codec_read(struct snd_ac97 * codec, unsigned short while (!(hwread(card->mmio, VORTEX_CODEC_CTRL) & 0x100)) { udelay(100); if (lifeboat++ > POLL_COUNT) { - printk(KERN_ERR "vortex: ac97 codec stuck busy\n"); + pr_err( "vortex: ac97 codec stuck busy\n"); return 0xffff; } } @@ -2586,7 +2586,7 @@ static unsigned short vortex_codec_read(struct snd_ac97 * codec, unsigned short udelay(100); data = hwread(card->mmio, VORTEX_CODEC_IO); if (lifeboat++ > POLL_COUNT) { - printk(KERN_ERR "vortex: ac97 address never arrived\n"); + pr_err( "vortex: ac97 address never arrived\n"); return 0xffff; } } while ((data & VORTEX_CODEC_ADDMASK) != @@ -2683,7 +2683,7 @@ static void vortex_spdif_init(vortex_t * vortex, int spdif_sr, int spdif_mode) static int vortex_core_init(vortex_t *vortex) { - printk(KERN_INFO "Vortex: init.... "); + pr_info( "Vortex: init.... "); /* Hardware Init. */ hwwrite(vortex->mmio, VORTEX_CTRL, 0xffffffff); msleep(5); @@ -2728,7 +2728,7 @@ static int vortex_core_init(vortex_t *vortex) //vortex_enable_timer_int(vortex); //vortex_disable_timer_int(vortex); - printk(KERN_INFO "done.\n"); + pr_info( "done.\n"); spin_lock_init(&vortex->lock); return 0; @@ -2737,7 +2737,7 @@ static int vortex_core_init(vortex_t *vortex) static int vortex_core_shutdown(vortex_t * vortex) { - printk(KERN_INFO "Vortex: shutdown..."); + pr_info( "Vortex: shutdown..."); #ifndef CHIP_AU8820 vortex_eq_free(vortex); vortex_Vort3D_disable(vortex); @@ -2759,7 +2759,7 @@ static int vortex_core_shutdown(vortex_t * vortex) msleep(5); hwwrite(vortex->mmio, VORTEX_IRQ_SOURCE, 0xffff); - printk(KERN_INFO "done.\n"); + pr_info( "done.\n"); return 0; } @@ -2793,7 +2793,7 @@ static int vortex_alsafmt_aspfmt(int alsafmt) break; default: fmt = 0x8; - printk(KERN_ERR "vortex: format unsupported %d\n", alsafmt); + pr_err( "vortex: format unsupported %d\n", alsafmt); break; } return fmt; diff --git a/sound/pci/au88x0/au88x0_eq.c b/sound/pci/au88x0/au88x0_eq.c index e722053..9404ba7 100644 --- a/sound/pci/au88x0/au88x0_eq.c +++ b/sound/pci/au88x0/au88x0_eq.c @@ -845,7 +845,7 @@ snd_vortex_peaks_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u vortex_Eqlzr_GetAllPeaks(vortex, peaks, &count); if (count != 20) { - printk(KERN_ERR "vortex: peak count error 20 != %d \n", count); + pr_err( "vortex: peak count error 20 != %d \n", count); return -1; } for (i = 0; i < 20; i++) diff --git a/sound/pci/au88x0/au88x0_game.c b/sound/pci/au88x0/au88x0_game.c index 280f86d..72daf6c 100644 --- a/sound/pci/au88x0/au88x0_game.c +++ b/sound/pci/au88x0/au88x0_game.c @@ -98,7 +98,7 @@ static int vortex_gameport_register(vortex_t *vortex) vortex->gameport = gp = gameport_allocate_port(); if (!gp) { - printk(KERN_ERR "vortex: cannot allocate memory for gameport\n"); + pr_err( "vortex: cannot allocate memory for gameport\n"); return -ENOMEM; } diff --git a/sound/pci/au88x0/au88x0_mpu401.c b/sound/pci/au88x0/au88x0_mpu401.c index 29e5945..328c194 100644 --- a/sound/pci/au88x0/au88x0_mpu401.c +++ b/sound/pci/au88x0/au88x0_mpu401.c @@ -73,7 +73,7 @@ static int snd_vortex_midi(vortex_t *vortex) /* Check if anything is OK. */ temp = hwread(vortex->mmio, VORTEX_MIDI_DATA); if (temp != MPU401_ACK /*0xfe */ ) { - printk(KERN_ERR "midi port doesn't acknowledge!\n"); + pr_err( "midi port doesn't acknowledge!\n"); return -ENODEV; } /* Enable MPU401 interrupts. */ diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c index 9fb03b4..5adc6b9 100644 --- a/sound/pci/au88x0/au88x0_pcm.c +++ b/sound/pci/au88x0/au88x0_pcm.c @@ -227,11 +227,11 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream, err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); if (err < 0) { - printk(KERN_ERR "Vortex: pcm page alloc failed!\n"); + pr_err( "Vortex: pcm page alloc failed!\n"); return err; } /* - printk(KERN_INFO "Vortex: periods %d, period_bytes %d, channels = %d\n", params_periods(hw_params), + pr_info( "Vortex: periods %d, period_bytes %d, channels = %d\n", params_periods(hw_params), params_period_bytes(hw_params), params_channels(hw_params)); */ spin_lock_irq(&chip->lock); @@ -371,7 +371,7 @@ static int snd_vortex_pcm_trigger(struct snd_pcm_substream *substream, int cmd) } #ifndef CHIP_AU8810 else { - printk(KERN_INFO "vortex: wt start %d\n", dma); + pr_info( "vortex: wt start %d\n", dma); vortex_wtdma_startfifo(chip, dma); } #endif @@ -384,7 +384,7 @@ static int snd_vortex_pcm_trigger(struct snd_pcm_substream *substream, int cmd) vortex_adbdma_stopfifo(chip, dma); #ifndef CHIP_AU8810 else { - printk(KERN_INFO "vortex: wt stop %d\n", dma); + pr_info( "vortex: wt stop %d\n", dma); vortex_wtdma_stopfifo(chip, dma); } #endif diff --git a/sound/pci/au88x0/au88x0_synth.c b/sound/pci/au88x0/au88x0_synth.c index 922a84b..f094bac 100644 --- a/sound/pci/au88x0/au88x0_synth.c +++ b/sound/pci/au88x0/au88x0_synth.c @@ -90,7 +90,7 @@ static int vortex_wt_allocroute(vortex_t * vortex, int wt, int nr_ch) hwwrite(vortex->mmio, WT_PARM(wt, 2), 0); temp = hwread(vortex->mmio, WT_PARM(wt, 3)); - printk(KERN_DEBUG "vortex: WT PARM3: %x\n", temp); + pr_debug( "vortex: WT PARM3: %x\n", temp); //hwwrite(vortex->mmio, WT_PARM(wt, 3), temp); hwwrite(vortex->mmio, WT_DELAY(wt, 0), 0); @@ -98,7 +98,7 @@ static int vortex_wt_allocroute(vortex_t * vortex, int wt, int nr_ch) hwwrite(vortex->mmio, WT_DELAY(wt, 2), 0); hwwrite(vortex->mmio, WT_DELAY(wt, 3), 0); - printk(KERN_DEBUG "vortex: WT GMODE: %x\n", hwread(vortex->mmio, WT_GMODE(wt))); + pr_debug( "vortex: WT GMODE: %x\n", hwread(vortex->mmio, WT_GMODE(wt))); hwwrite(vortex->mmio, WT_PARM(wt, 2), 0xffffffff); hwwrite(vortex->mmio, WT_PARM(wt, 3), 0xcff1c810); @@ -106,7 +106,7 @@ static int vortex_wt_allocroute(vortex_t * vortex, int wt, int nr_ch) voice->parm0 = voice->parm1 = 0xcfb23e2f; hwwrite(vortex->mmio, WT_PARM(wt, 0), voice->parm0); hwwrite(vortex->mmio, WT_PARM(wt, 1), voice->parm1); - printk(KERN_DEBUG "vortex: WT GMODE 2 : %x\n", hwread(vortex->mmio, WT_GMODE(wt))); + pr_debug( "vortex: WT GMODE 2 : %x\n", hwread(vortex->mmio, WT_GMODE(wt))); return 0; } @@ -196,14 +196,14 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt, if ((reg == 5) || ((reg >= 7) && (reg <= 10)) || (reg == 0xc)) { if (wt >= (NR_WT / NR_WT_PB)) { - printk + pr_warn ("vortex: WT SetReg: bank out of range. reg=0x%x, wt=%d\n", reg, wt); return 0; } } else { if (wt >= NR_WT) { - printk(KERN_ERR "vortex: WT SetReg: voice out of range\n"); + pr_err( "vortex: WT SetReg: voice out of range\n"); return 0; } } @@ -214,42 +214,42 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt, /* Voice specific parameters */ case 0: /* running */ /* - printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", + pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n", WT_RUN(wt), (int)val); */ hwwrite(vortex->mmio, WT_RUN(wt), val); return 0xc; case 1: /* param 0 */ /* - printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", + pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n", WT_PARM(wt,0), (int)val); */ hwwrite(vortex->mmio, WT_PARM(wt, 0), val); return 0xc; case 2: /* param 1 */ /* - printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", + pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n", WT_PARM(wt,1), (int)val); */ hwwrite(vortex->mmio, WT_PARM(wt, 1), val); return 0xc; case 3: /* param 2 */ /* - printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", + pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n", WT_PARM(wt,2), (int)val); */ hwwrite(vortex->mmio, WT_PARM(wt, 2), val); return 0xc; case 4: /* param 3 */ /* - printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", + pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n", WT_PARM(wt,3), (int)val); */ hwwrite(vortex->mmio, WT_PARM(wt, 3), val); return 0xc; case 6: /* mute */ /* - printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", + pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n", WT_MUTE(wt), (int)val); */ hwwrite(vortex->mmio, WT_MUTE(wt), val); @@ -257,7 +257,7 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt, case 0xb: /* delay */ /* - printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", + pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n", WT_DELAY(wt,0), (int)val); */ hwwrite(vortex->mmio, WT_DELAY(wt, 3), val); @@ -285,7 +285,7 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt, return 0; } /* - printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", ecx, (int)val); + pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n", ecx, (int)val); */ hwwrite(vortex->mmio, ecx, val); return 1; diff --git a/sound/pci/ctxfi/ctamixer.c b/sound/pci/ctxfi/ctamixer.c index fee35cf..c7dc38d 100644 --- a/sound/pci/ctxfi/ctamixer.c +++ b/sound/pci/ctxfi/ctamixer.c @@ -258,7 +258,8 @@ static int get_amixer_rsc(struct amixer_mgr *mgr, } spin_unlock_irqrestore(&mgr->mgr_lock, flags); if (err) { - printk(KERN_ERR "ctxfi: Can't meet AMIXER resource request!\n"); + dev_err(mgr->card->dev, + "Can't meet AMIXER resource request!\n"); goto error; } @@ -296,7 +297,7 @@ static int put_amixer_rsc(struct amixer_mgr *mgr, struct amixer *amixer) return 0; } -int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr) +int amixer_mgr_create(struct hw *hw, struct amixer_mgr **ramixer_mgr) { int err; struct amixer_mgr *amixer_mgr; @@ -314,6 +315,7 @@ int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr) amixer_mgr->get_amixer = get_amixer_rsc; amixer_mgr->put_amixer = put_amixer_rsc; + amixer_mgr->card = hw->card; *ramixer_mgr = amixer_mgr; @@ -411,7 +413,8 @@ static int get_sum_rsc(struct sum_mgr *mgr, } spin_unlock_irqrestore(&mgr->mgr_lock, flags); if (err) { - printk(KERN_ERR "ctxfi: Can't meet SUM resource request!\n"); + dev_err(mgr->card->dev, + "Can't meet SUM resource request!\n"); goto error; } @@ -449,7 +452,7 @@ static int put_sum_rsc(struct sum_mgr *mgr, struct sum *sum) return 0; } -int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr) +int sum_mgr_create(struct hw *hw, struct sum_mgr **rsum_mgr) { int err; struct sum_mgr *sum_mgr; @@ -467,6 +470,7 @@ int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr) sum_mgr->get_sum = get_sum_rsc; sum_mgr->put_sum = put_sum_rsc; + sum_mgr->card = hw->card; *rsum_mgr = sum_mgr; diff --git a/sound/pci/ctxfi/ctamixer.h b/sound/pci/ctxfi/ctamixer.h index cc49e5a..72f42f2 100644 --- a/sound/pci/ctxfi/ctamixer.h +++ b/sound/pci/ctxfi/ctamixer.h @@ -21,6 +21,7 @@ #include "ctresource.h" #include <linux/spinlock.h> +#include <sound/core.h> /* Define the descriptor of a summation node resource */ struct sum { @@ -35,6 +36,7 @@ struct sum_desc { struct sum_mgr { struct rsc_mgr mgr; /* Basic resource manager info */ + struct snd_card *card; /* pointer to this card */ spinlock_t mgr_lock; /* request one sum resource */ @@ -45,7 +47,7 @@ struct sum_mgr { }; /* Constructor and destructor of daio resource manager */ -int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr); +int sum_mgr_create(struct hw *hw, struct sum_mgr **rsum_mgr); int sum_mgr_destroy(struct sum_mgr *sum_mgr); /* Define the descriptor of a amixer resource */ @@ -79,6 +81,7 @@ struct amixer_desc { struct amixer_mgr { struct rsc_mgr mgr; /* Basic resource manager info */ + struct snd_card *card; /* pointer to this card */ spinlock_t mgr_lock; /* request one amixer resource */ @@ -90,7 +93,7 @@ struct amixer_mgr { }; /* Constructor and destructor of amixer resource manager */ -int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr); +int amixer_mgr_create(struct hw *hw, struct amixer_mgr **ramixer_mgr); int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr); #endif /* CTAMIXER_H */ diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index af632bd..4546590 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -106,11 +106,11 @@ static struct { .public_name = "Mixer"} }; -typedef int (*create_t)(void *, void **); +typedef int (*create_t)(struct hw *, void **); typedef int (*destroy_t)(void *); static struct { - int (*create)(void *hw, void **rmgr); + int (*create)(struct hw *hw, void **rmgr); int (*destroy)(void *mgr); } rsc_mgr_funcs[NUM_RSCTYP] = { [SRC] = { .create = (create_t)src_mgr_create, @@ -171,7 +171,8 @@ static unsigned long atc_get_ptp_phys(struct ct_atc *atc, int index) return atc->vm->get_ptp_phys(atc->vm, index); } -static unsigned int convert_format(snd_pcm_format_t snd_format) +static unsigned int convert_format(snd_pcm_format_t snd_format, + struct snd_card *card) { switch (snd_format) { case SNDRV_PCM_FORMAT_U8: @@ -185,7 +186,7 @@ static unsigned int convert_format(snd_pcm_format_t snd_format) case SNDRV_PCM_FORMAT_FLOAT_LE: return SRC_SF_F32; default: - printk(KERN_ERR "ctxfi: not recognized snd format is %d \n", + dev_err(card->dev, "not recognized snd format is %d\n", snd_format); return SRC_SF_S16; } @@ -268,7 +269,8 @@ static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) src = apcm->src; src->ops->set_pitch(src, pitch); src->ops->set_rom(src, select_rom(pitch)); - src->ops->set_sf(src, convert_format(apcm->substream->runtime->format)); + src->ops->set_sf(src, convert_format(apcm->substream->runtime->format, + atc->card)); src->ops->set_pm(src, (src->ops->next_interleave(src) != NULL)); /* Get AMIXER resource */ @@ -738,7 +740,8 @@ static int atc_pcm_capture_start(struct ct_atc *atc, struct ct_atc_pcm *apcm) /* Set up recording SRC */ src = apcm->src; - src->ops->set_sf(src, convert_format(apcm->substream->runtime->format)); + src->ops->set_sf(src, convert_format(apcm->substream->runtime->format, + atc->card)); src->ops->set_sa(src, apcm->vm_block->addr); src->ops->set_la(src, apcm->vm_block->addr + apcm->vm_block->size); src->ops->set_ca(src, apcm->vm_block->addr); @@ -807,7 +810,8 @@ static int spdif_passthru_playback_get_resources(struct ct_atc *atc, src = apcm->src; src->ops->set_pitch(src, pitch); src->ops->set_rom(src, select_rom(pitch)); - src->ops->set_sf(src, convert_format(apcm->substream->runtime->format)); + src->ops->set_sf(src, convert_format(apcm->substream->runtime->format, + atc->card)); src->ops->set_pm(src, (src->ops->next_interleave(src) != NULL)); src->ops->set_bp(src, 1); @@ -1235,7 +1239,7 @@ static int ct_atc_destroy(struct ct_atc *atc) } if (atc->hw) - destroy_hw_obj((struct hw *)atc->hw); + destroy_hw_obj(atc->hw); /* Destroy device virtual memory manager object */ if (atc->vm) { @@ -1282,9 +1286,9 @@ static int atc_identify_card(struct ct_atc *atc, unsigned int ssid) p = snd_pci_quirk_lookup_id(vendor_id, device_id, list); if (p) { if (p->value < 0) { - printk(KERN_ERR "ctxfi: " - "Device %04x:%04x is black-listed\n", - vendor_id, device_id); + dev_err(atc->card->dev, + "Device %04x:%04x is black-listed\n", + vendor_id, device_id); return -ENOENT; } atc->model = p->value; @@ -1315,8 +1319,8 @@ int ct_atc_create_alsa_devs(struct ct_atc *atc) err = alsa_dev_funcs[i].create(atc, i, alsa_dev_funcs[i].public_name); if (err) { - printk(KERN_ERR "ctxfi: " - "Creating alsa device %d failed!\n", i); + dev_err(atc->card->dev, + "Creating alsa device %d failed!\n", i); return err; } } @@ -1332,9 +1336,10 @@ static int atc_create_hw_devs(struct ct_atc *atc) err = create_hw_obj(atc->pci, atc->chip_type, atc->model, &hw); if (err) { - printk(KERN_ERR "Failed to create hw obj!!!\n"); + dev_err(atc->card->dev, "Failed to create hw obj!!!\n"); return err; } + hw->card = atc->card; atc->hw = hw; /* Initialize card hardware. */ @@ -1351,8 +1356,8 @@ static int atc_create_hw_devs(struct ct_atc *atc) err = rsc_mgr_funcs[i].create(atc->hw, &atc->rsc_mgrs[i]); if (err) { - printk(KERN_ERR "ctxfi: " - "Failed to create rsc_mgr %d!!!\n", i); + dev_err(atc->card->dev, + "Failed to create rsc_mgr %d!!!\n", i); return err; } } @@ -1399,8 +1404,9 @@ static int atc_get_resources(struct ct_atc *atc) err = daio_mgr->get_daio(daio_mgr, &da_desc, (struct daio **)&atc->daios[i]); if (err) { - printk(KERN_ERR "ctxfi: Failed to get DAIO " - "resource %d!!!\n", i); + dev_err(atc->card->dev, + "Failed to get DAIO resource %d!!!\n", + i); return err; } atc->n_daio++; @@ -1603,8 +1609,8 @@ static int atc_resume(struct ct_atc *atc) /* Do hardware resume. */ err = atc_hw_resume(atc); if (err < 0) { - printk(KERN_ERR "ctxfi: pci_enable_device failed, " - "disabling device\n"); + dev_err(atc->card->dev, + "pci_enable_device failed, disabling device\n"); snd_card_disconnect(atc->card); return err; } @@ -1701,7 +1707,7 @@ int ct_atc_create(struct snd_card *card, struct pci_dev *pci, /* Find card model */ err = atc_identify_card(atc, ssid); if (err < 0) { - printk(KERN_ERR "ctatc: Card not recognised\n"); + dev_err(card->dev, "ctatc: Card not recognised\n"); goto error1; } @@ -1717,7 +1723,7 @@ int ct_atc_create(struct snd_card *card, struct pci_dev *pci, err = ct_mixer_create(atc, (struct ct_mixer **)&atc->mixer); if (err) { - printk(KERN_ERR "ctxfi: Failed to create mixer obj!!!\n"); + dev_err(card->dev, "Failed to create mixer obj!!!\n"); goto error1; } @@ -1744,6 +1750,6 @@ int ct_atc_create(struct snd_card *card, struct pci_dev *pci, error1: ct_atc_destroy(atc); - printk(KERN_ERR "ctxfi: Something wrong!!!\n"); + dev_err(card->dev, "Something wrong!!!\n"); return err; } diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h index 5f11ca2..5641334 100644 --- a/sound/pci/ctxfi/ctatc.h +++ b/sound/pci/ctxfi/ctatc.h @@ -131,7 +131,7 @@ struct ct_atc { /* Don't touch! Used for internal object. */ void *rsc_mgrs[NUM_RSCTYP]; /* chip resource managers */ void *mixer; /* internal mixer object */ - void *hw; /* chip specific hardware access object */ + struct hw *hw; /* chip specific hardware access object */ void **daios; /* digital audio io resources */ void **pcm; /* SUMs for collecting all pcm stream */ void **srcs; /* Sample Rate Converters for input signal */ diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c index 84f86bf..c1c3f88 100644 --- a/sound/pci/ctxfi/ctdaio.c +++ b/sound/pci/ctxfi/ctdaio.c @@ -140,19 +140,19 @@ static int dao_rsc_reinit(struct dao *dao, const struct dao_desc *desc); static int dao_spdif_get_spos(struct dao *dao, unsigned int *spos) { - ((struct hw *)dao->hw)->dao_get_spos(dao->ctrl_blk, spos); + dao->hw->dao_get_spos(dao->ctrl_blk, spos); return 0; } static int dao_spdif_set_spos(struct dao *dao, unsigned int spos) { - ((struct hw *)dao->hw)->dao_set_spos(dao->ctrl_blk, spos); + dao->hw->dao_set_spos(dao->ctrl_blk, spos); return 0; } static int dao_commit_write(struct dao *dao) { - ((struct hw *)dao->hw)->dao_commit_write(dao->hw, + dao->hw->dao_commit_write(dao->hw, daio_device_index(dao->daio.type, dao->hw), dao->ctrl_blk); return 0; } @@ -277,16 +277,14 @@ static struct dao_rsc_ops dao_ops = { static int dai_set_srt_srcl(struct dai *dai, struct rsc *src) { src->ops->master(src); - ((struct hw *)dai->hw)->dai_srt_set_srcm(dai->ctrl_blk, - src->ops->index(src)); + dai->hw->dai_srt_set_srcm(dai->ctrl_blk, src->ops->index(src)); return 0; } static int dai_set_srt_srcr(struct dai *dai, struct rsc *src) { src->ops->master(src); - ((struct hw *)dai->hw)->dai_srt_set_srco(dai->ctrl_blk, - src->ops->index(src)); + dai->hw->dai_srt_set_srco(dai->ctrl_blk, src->ops->index(src)); return 0; } @@ -297,25 +295,25 @@ static int dai_set_srt_msr(struct dai *dai, unsigned int msr) for (rsr = 0; msr > 1; msr >>= 1) rsr++; - ((struct hw *)dai->hw)->dai_srt_set_rsr(dai->ctrl_blk, rsr); + dai->hw->dai_srt_set_rsr(dai->ctrl_blk, rsr); return 0; } static int dai_set_enb_src(struct dai *dai, unsigned int enb) { - ((struct hw *)dai->hw)->dai_srt_set_ec(dai->ctrl_blk, enb); + dai->hw->dai_srt_set_ec(dai->ctrl_blk, enb); return 0; } static int dai_set_enb_srt(struct dai *dai, unsigned int enb) { - ((struct hw *)dai->hw)->dai_srt_set_et(dai->ctrl_blk, enb); + dai->hw->dai_srt_set_et(dai->ctrl_blk, enb); return 0; } static int dai_commit_write(struct dai *dai) { - ((struct hw *)dai->hw)->dai_commit_write(dai->hw, + dai->hw->dai_commit_write(dai->hw, daio_device_index(dai->daio.type, dai->hw), dai->ctrl_blk); return 0; } @@ -331,12 +329,12 @@ static struct dai_rsc_ops dai_ops = { static int daio_rsc_init(struct daio *daio, const struct daio_desc *desc, - void *hw) + struct hw *hw) { int err; unsigned int idx_l, idx_r; - switch (((struct hw *)hw)->chip_type) { + switch (hw->chip_type) { case ATC20K1: idx_l = idx_20k1[desc->type].left; idx_r = idx_20k1[desc->type].right; @@ -360,7 +358,7 @@ static int daio_rsc_init(struct daio *daio, if (desc->type <= DAIO_OUT_MAX) { daio->rscl.ops = daio->rscr.ops = &daio_out_rsc_ops; } else { - switch (((struct hw *)hw)->chip_type) { + switch (hw->chip_type) { case ATC20K1: daio->rscl.ops = daio->rscr.ops = &daio_in_rsc_ops_20k1; break; @@ -445,7 +443,7 @@ static int dao_rsc_uninit(struct dao *dao) kfree(dao->imappers); dao->imappers = NULL; } - ((struct hw *)dao->hw)->dao_put_ctrl_blk(dao->ctrl_blk); + dao->hw->dao_put_ctrl_blk(dao->ctrl_blk); dao->hw = dao->ctrl_blk = NULL; daio_rsc_uninit(&dao->daio); @@ -502,7 +500,7 @@ error1: static int dai_rsc_uninit(struct dai *dai) { - ((struct hw *)dai->hw)->dai_put_ctrl_blk(dai->ctrl_blk); + dai->hw->dai_put_ctrl_blk(dai->ctrl_blk); dai->hw = dai->ctrl_blk = NULL; daio_rsc_uninit(&dai->daio); return 0; @@ -541,7 +539,8 @@ static int get_daio_rsc(struct daio_mgr *mgr, err = daio_mgr_get_rsc(&mgr->mgr, desc->type); spin_unlock_irqrestore(&mgr->mgr_lock, flags); if (err) { - printk(KERN_ERR "Can't meet DAIO resource request!\n"); + dev_err(mgr->card->dev, + "Can't meet DAIO resource request!\n"); return err; } @@ -692,7 +691,7 @@ static int daio_mgr_commit_write(struct daio_mgr *mgr) return 0; } -int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr) +int daio_mgr_create(struct hw *hw, struct daio_mgr **rdaio_mgr) { int err, i; struct daio_mgr *daio_mgr; @@ -727,12 +726,13 @@ int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr) daio_mgr->imap_add = daio_imap_add; daio_mgr->imap_delete = daio_imap_delete; daio_mgr->commit_write = daio_mgr_commit_write; + daio_mgr->card = hw->card; for (i = 0; i < 8; i++) { - ((struct hw *)hw)->daio_mgr_dsb_dao(daio_mgr->mgr.ctrl_blk, i); - ((struct hw *)hw)->daio_mgr_dsb_dai(daio_mgr->mgr.ctrl_blk, i); + hw->daio_mgr_dsb_dao(daio_mgr->mgr.ctrl_blk, i); + hw->daio_mgr_dsb_dai(daio_mgr->mgr.ctrl_blk, i); } - ((struct hw *)hw)->daio_mgr_commit_write(hw, daio_mgr->mgr.ctrl_blk); + hw->daio_mgr_commit_write(hw, daio_mgr->mgr.ctrl_blk); *rdaio_mgr = daio_mgr; diff --git a/sound/pci/ctxfi/ctdaio.h b/sound/pci/ctxfi/ctdaio.h index 85ccb6e..0ebbf35 100644 --- a/sound/pci/ctxfi/ctdaio.h +++ b/sound/pci/ctxfi/ctdaio.h @@ -23,6 +23,7 @@ #include "ctimap.h" #include <linux/spinlock.h> #include <linux/list.h> +#include <sound/core.h> /* Define the descriptor of a daio resource */ enum DAIOTYP { @@ -53,14 +54,14 @@ struct dao { struct dao_rsc_ops *ops; /* DAO specific operations */ struct imapper **imappers; struct daio_mgr *mgr; - void *hw; + struct hw *hw; void *ctrl_blk; }; struct dai { struct daio daio; struct dai_rsc_ops *ops; /* DAI specific operations */ - void *hw; + struct hw *hw; void *ctrl_blk; }; @@ -98,6 +99,7 @@ struct daio_desc { struct daio_mgr { struct rsc_mgr mgr; /* Basic resource manager info */ + struct snd_card *card; /* pointer to this card */ spinlock_t mgr_lock; spinlock_t imap_lock; struct list_head imappers; @@ -117,7 +119,7 @@ struct daio_mgr { }; /* Constructor and destructor of daio resource manager */ -int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr); +int daio_mgr_create(struct hw *hw, struct daio_mgr **rdaio_mgr); int daio_mgr_destroy(struct daio_mgr *daio_mgr); #endif /* CTDAIO_H */ diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h index 5977e9a..54cc9cb 100644 --- a/sound/pci/ctxfi/cthardware.h +++ b/sound/pci/ctxfi/cthardware.h @@ -20,6 +20,7 @@ #include <linux/types.h> #include <linux/pci.h> +#include <sound/core.h> enum CHIPTYP { ATC20K1, @@ -184,9 +185,10 @@ struct hw { void *irq_callback_data; struct pci_dev *pci; /* the pci kernel structure of this card */ + struct snd_card *card; /* pointer to this card */ int irq; unsigned long io_base; - unsigned long mem_base; + void __iomem *mem_base; enum CHIPTYP chip_type; enum CTCARDS model; diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index 6ac40be..b425aa8 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -1268,7 +1268,8 @@ static int hw_trn_init(struct hw *hw, const struct trn_conf *info) /* Set up device page table */ if ((~0UL) == info->vm_pgt_phys) { - printk(KERN_ERR "Wrong device page table page address!\n"); + dev_err(hw->card->dev, + "Wrong device page table page address!\n"); return -1; } @@ -1327,7 +1328,7 @@ static int hw_pll_init(struct hw *hw, unsigned int rsr) mdelay(40); } if (i >= 3) { - printk(KERN_ALERT "PLL initialization failed!!!\n"); + dev_alert(hw->card->dev, "PLL initialization failed!!!\n"); return -EBUSY; } @@ -1351,7 +1352,7 @@ static int hw_auto_init(struct hw *hw) break; } if (!get_field(gctl, GCTL_AID)) { - printk(KERN_ALERT "Card Auto-init failed!!!\n"); + dev_alert(hw->card->dev, "Card Auto-init failed!!!\n"); return -EBUSY; } @@ -1802,7 +1803,7 @@ static int uaa_to_xfi(struct pci_dev *pci) unsigned int is_uaa; unsigned int data[4] = {0}; unsigned int io_base; - void *mem_base; + void __iomem *mem_base; int i; const u32 CTLX = CTLBITS('C', 'T', 'L', 'X'); const u32 CTL_ = CTLBITS('C', 'T', 'L', '-'); @@ -1911,9 +1912,9 @@ static int hw_card_start(struct hw *hw) /* Set DMA transfer mask */ if (pci_set_dma_mask(pci, CT_XFI_DMA_MASK) < 0 || pci_set_consistent_dma_mask(pci, CT_XFI_DMA_MASK) < 0) { - printk(KERN_ERR "architecture does not support PCI " - "busmaster DMA with mask 0x%llx\n", - CT_XFI_DMA_MASK); + dev_err(hw->card->dev, + "architecture does not support PCI busmaster DMA with mask 0x%llx\n", + CT_XFI_DMA_MASK); err = -ENXIO; goto error1; } @@ -1942,7 +1943,8 @@ static int hw_card_start(struct hw *hw) err = request_irq(pci->irq, ct_20k1_interrupt, IRQF_SHARED, KBUILD_MODNAME, hw); if (err < 0) { - printk(KERN_ERR "XFi: Cannot get irq %d\n", pci->irq); + dev_err(hw->card->dev, + "XFi: Cannot get irq %d\n", pci->irq); goto error2; } hw->irq = pci->irq; @@ -1985,9 +1987,9 @@ static int hw_card_shutdown(struct hw *hw) hw->irq = -1; if (hw->mem_base) - iounmap((void *)hw->mem_base); + iounmap(hw->mem_base); - hw->mem_base = (unsigned long)NULL; + hw->mem_base = NULL; if (hw->io_base) pci_release_regions(hw->pci); diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index b143886..253899d 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -1187,7 +1187,8 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info) hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x21212121); hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0); } else { - printk(KERN_ALERT "ctxfi: ERROR!!! Invalid sampling rate!!!\n"); + dev_alert(hw->card->dev, + "ERROR!!! Invalid sampling rate!!!\n"); return -EINVAL; } @@ -1246,8 +1247,8 @@ static int hw_trn_init(struct hw *hw, const struct trn_conf *info) /* Set up device page table */ if ((~0UL) == info->vm_pgt_phys) { - printk(KERN_ALERT "ctxfi: " - "Wrong device page table page address!!!\n"); + dev_alert(hw->card->dev, + "Wrong device page table page address!!!\n"); return -1; } @@ -1352,7 +1353,8 @@ static int hw_pll_init(struct hw *hw, unsigned int rsr) break; } if (i >= 1000) { - printk(KERN_ALERT "ctxfi: PLL initialization failed!!!\n"); + dev_alert(hw->card->dev, + "PLL initialization failed!!!\n"); return -EBUSY; } @@ -1376,7 +1378,7 @@ static int hw_auto_init(struct hw *hw) break; } if (!get_field(gctl, GCTL_AID)) { - printk(KERN_ALERT "ctxfi: Card Auto-init failed!!!\n"); + dev_alert(hw->card->dev, "Card Auto-init failed!!!\n"); return -EBUSY; } @@ -1847,7 +1849,7 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) /* Initialize I2C */ err = hw20k2_i2c_init(hw, 0x1A, 1, 1); if (err < 0) { - printk(KERN_ALERT "ctxfi: Failure to acquire I2C!!!\n"); + dev_alert(hw->card->dev, "Failure to acquire I2C!!!\n"); goto error; } @@ -1890,8 +1892,9 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x0A), MAKE_WM8775_DATA(0x0A)); } else { - printk(KERN_ALERT "ctxfi: Invalid master sampling " - "rate (msr %d)!!!\n", info->msr); + dev_alert(hw->card->dev, + "Invalid master sampling rate (msr %d)!!!\n", + info->msr); err = -EINVAL; goto error; } @@ -2034,8 +2037,9 @@ static int hw_card_start(struct hw *hw) /* Set DMA transfer mask */ if (pci_set_dma_mask(pci, CT_XFI_DMA_MASK) < 0 || pci_set_consistent_dma_mask(pci, CT_XFI_DMA_MASK) < 0) { - printk(KERN_ERR "ctxfi: architecture does not support PCI " - "busmaster DMA with mask 0x%llx\n", CT_XFI_DMA_MASK); + dev_err(hw->card->dev, + "architecture does not support PCI busmaster DMA with mask 0x%llx\n", + CT_XFI_DMA_MASK); err = -ENXIO; goto error1; } @@ -2046,8 +2050,8 @@ static int hw_card_start(struct hw *hw) goto error1; hw->io_base = pci_resource_start(hw->pci, 2); - hw->mem_base = (unsigned long)ioremap(hw->io_base, - pci_resource_len(hw->pci, 2)); + hw->mem_base = ioremap(hw->io_base, + pci_resource_len(hw->pci, 2)); if (!hw->mem_base) { err = -ENOENT; goto error2; @@ -2063,7 +2067,8 @@ static int hw_card_start(struct hw *hw) err = request_irq(pci->irq, ct_20k2_interrupt, IRQF_SHARED, KBUILD_MODNAME, hw); if (err < 0) { - printk(KERN_ERR "XFi: Cannot get irq %d\n", pci->irq); + dev_err(hw->card->dev, + "XFi: Cannot get irq %d\n", pci->irq); goto error2; } hw->irq = pci->irq; @@ -2107,9 +2112,9 @@ static int hw_card_shutdown(struct hw *hw) hw->irq = -1; if (hw->mem_base) - iounmap((void *)hw->mem_base); + iounmap(hw->mem_base); - hw->mem_base = (unsigned long)NULL; + hw->mem_base = NULL; if (hw->io_base) pci_release_regions(hw->pci); @@ -2229,12 +2234,12 @@ static int hw_resume(struct hw *hw, struct card_conf *info) static u32 hw_read_20kx(struct hw *hw, u32 reg) { - return readl((void *)(hw->mem_base + reg)); + return readl(hw->mem_base + reg); } static void hw_write_20kx(struct hw *hw, u32 reg, u32 data) { - writel(data, (void *)(hw->mem_base + reg)); + writel(data, hw->mem_base + reg); } static struct hw ct20k2_preset = { diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c index 48fe0e3..4f4a2a5 100644 --- a/sound/pci/ctxfi/ctmixer.c +++ b/sound/pci/ctxfi/ctmixer.c @@ -854,8 +854,8 @@ static int ct_mixer_get_resources(struct ct_mixer *mixer) for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) { err = sum_mgr->get_sum(sum_mgr, &sum_desc, &sum); if (err) { - printk(KERN_ERR "ctxfi:Failed to get sum resources for " - "front output!\n"); + dev_err(mixer->atc->card->dev, + "Failed to get sum resources for front output!\n"); break; } mixer->sums[i] = sum; @@ -869,8 +869,8 @@ static int ct_mixer_get_resources(struct ct_mixer *mixer) for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) { err = amixer_mgr->get_amixer(amixer_mgr, &am_desc, &amixer); if (err) { - printk(KERN_ERR "ctxfi:Failed to get amixer resources " - "for mixer obj!\n"); + dev_err(mixer->atc->card->dev, + "Failed to get amixer resources for mixer obj!\n"); break; } mixer->amixers[i] = amixer; diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c index e8a4feb..d86c474 100644 --- a/sound/pci/ctxfi/ctpcm.c +++ b/sound/pci/ctxfi/ctpcm.c @@ -217,7 +217,8 @@ static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream) err = atc->pcm_playback_prepare(atc, apcm); if (err < 0) { - printk(KERN_ERR "ctxfi: Preparing pcm playback failed!!!\n"); + dev_err(atc->card->dev, + "Preparing pcm playback failed!!!\n"); return err; } @@ -324,7 +325,8 @@ static int ct_pcm_capture_prepare(struct snd_pcm_substream *substream) err = atc->pcm_capture_prepare(atc, apcm); if (err < 0) { - printk(KERN_ERR "ctxfi: Preparing pcm capture failed!!!\n"); + dev_err(atc->card->dev, + "Preparing pcm capture failed!!!\n"); return err; } @@ -435,7 +437,8 @@ int ct_alsa_pcm_create(struct ct_atc *atc, err = snd_pcm_new(atc->card, "ctxfi", device, playback_count, capture_count, &pcm); if (err < 0) { - printk(KERN_ERR "ctxfi: snd_pcm_new failed!! Err=%d\n", err); + dev_err(atc->card->dev, "snd_pcm_new failed!! Err=%d\n", + err); return err; } diff --git a/sound/pci/ctxfi/ctresource.c b/sound/pci/ctxfi/ctresource.c index 7dfaf67..1a97e40 100644 --- a/sound/pci/ctxfi/ctresource.c +++ b/sound/pci/ctxfi/ctresource.c @@ -134,7 +134,8 @@ static struct rsc_ops rsc_generic_ops = { .next_conj = rsc_next_conj, }; -int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw) +int +rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, struct hw *hw) { int err = 0; @@ -151,25 +152,24 @@ int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw) switch (type) { case SRC: - err = ((struct hw *)hw)->src_rsc_get_ctrl_blk(&rsc->ctrl_blk); + err = hw->src_rsc_get_ctrl_blk(&rsc->ctrl_blk); break; case AMIXER: - err = ((struct hw *)hw)-> - amixer_rsc_get_ctrl_blk(&rsc->ctrl_blk); + err = hw->amixer_rsc_get_ctrl_blk(&rsc->ctrl_blk); break; case SRCIMP: case SUM: case DAIO: break; default: - printk(KERN_ERR - "ctxfi: Invalid resource type value %d!\n", type); + dev_err(((struct hw *)hw)->card->dev, + "Invalid resource type value %d!\n", type); return -EINVAL; } if (err) { - printk(KERN_ERR - "ctxfi: Failed to get resource control block!\n"); + dev_err(((struct hw *)hw)->card->dev, + "Failed to get resource control block!\n"); return err; } @@ -181,19 +181,18 @@ int rsc_uninit(struct rsc *rsc) if ((NULL != rsc->hw) && (NULL != rsc->ctrl_blk)) { switch (rsc->type) { case SRC: - ((struct hw *)rsc->hw)-> - src_rsc_put_ctrl_blk(rsc->ctrl_blk); + rsc->hw->src_rsc_put_ctrl_blk(rsc->ctrl_blk); break; case AMIXER: - ((struct hw *)rsc->hw)-> - amixer_rsc_put_ctrl_blk(rsc->ctrl_blk); + rsc->hw->amixer_rsc_put_ctrl_blk(rsc->ctrl_blk); break; case SUM: case DAIO: break; default: - printk(KERN_ERR "ctxfi: " - "Invalid resource type value %d!\n", rsc->type); + dev_err(((struct hw *)rsc->hw)->card->dev, + "Invalid resource type value %d!\n", + rsc->type); break; } @@ -208,10 +207,9 @@ int rsc_uninit(struct rsc *rsc) } int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type, - unsigned int amount, void *hw_obj) + unsigned int amount, struct hw *hw) { int err = 0; - struct hw *hw = hw_obj; mgr->type = NUM_RSCTYP; @@ -235,15 +233,15 @@ int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type, case SUM: break; default: - printk(KERN_ERR - "ctxfi: Invalid resource type value %d!\n", type); + dev_err(hw->card->dev, + "Invalid resource type value %d!\n", type); err = -EINVAL; goto error; } if (err) { - printk(KERN_ERR - "ctxfi: Failed to get manager control block!\n"); + dev_err(hw->card->dev, + "Failed to get manager control block!\n"); goto error; } @@ -268,26 +266,23 @@ int rsc_mgr_uninit(struct rsc_mgr *mgr) if ((NULL != mgr->hw) && (NULL != mgr->ctrl_blk)) { switch (mgr->type) { case SRC: - ((struct hw *)mgr->hw)-> - src_mgr_put_ctrl_blk(mgr->ctrl_blk); + mgr->hw->src_mgr_put_ctrl_blk(mgr->ctrl_blk); break; case SRCIMP: - ((struct hw *)mgr->hw)-> - srcimp_mgr_put_ctrl_blk(mgr->ctrl_blk); + mgr->hw->srcimp_mgr_put_ctrl_blk(mgr->ctrl_blk); break; case AMIXER: - ((struct hw *)mgr->hw)-> - amixer_mgr_put_ctrl_blk(mgr->ctrl_blk); + mgr->hw->amixer_mgr_put_ctrl_blk(mgr->ctrl_blk); break; case DAIO: - ((struct hw *)mgr->hw)-> - daio_mgr_put_ctrl_blk(mgr->ctrl_blk); + mgr->hw->daio_mgr_put_ctrl_blk(mgr->ctrl_blk); break; case SUM: break; default: - printk(KERN_ERR "ctxfi: " - "Invalid resource type value %d!\n", mgr->type); + dev_err(((struct hw *)mgr->hw)->card->dev, + "Invalid resource type value %d!\n", + mgr->type); break; } diff --git a/sound/pci/ctxfi/ctresource.h b/sound/pci/ctxfi/ctresource.h index 0838c2e..9b746c3 100644 --- a/sound/pci/ctxfi/ctresource.h +++ b/sound/pci/ctxfi/ctresource.h @@ -38,7 +38,7 @@ struct rsc { u32 conj:12; /* Current conjugate index */ u32 msr:4; /* The Master Sample Rate a resource working on */ void *ctrl_blk; /* Chip specific control info block for a resource */ - void *hw; /* Chip specific object for hardware access means */ + struct hw *hw; /* Chip specific object for hardware access means */ struct rsc_ops *ops; /* Generic resource operations */ }; @@ -50,7 +50,8 @@ struct rsc_ops { int (*output_slot)(const struct rsc *rsc); }; -int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw); +int +rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, struct hw *hw); int rsc_uninit(struct rsc *rsc); struct rsc_mgr { @@ -59,12 +60,12 @@ struct rsc_mgr { unsigned int avail; /* The amount of currently available resources */ unsigned char *rscs; /* The bit-map for resource allocation */ void *ctrl_blk; /* Chip specific control info block */ - void *hw; /* Chip specific object for hardware access */ + struct hw *hw; /* Chip specific object for hardware access */ }; /* Resource management is based on bit-map mechanism */ int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type, - unsigned int amount, void *hw); + unsigned int amount, struct hw *hw); int rsc_mgr_uninit(struct rsc_mgr *mgr); int mgr_get_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int *ridx); int mgr_put_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int idx); diff --git a/sound/pci/ctxfi/ctsrc.c b/sound/pci/ctxfi/ctsrc.c index 6e77e86..ec1f084 100644 --- a/sound/pci/ctxfi/ctsrc.c +++ b/sound/pci/ctxfi/ctsrc.c @@ -431,7 +431,8 @@ get_src_rsc(struct src_mgr *mgr, const struct src_desc *desc, struct src **rsrc) spin_unlock_irqrestore(&mgr->mgr_lock, flags); if (err) { - printk(KERN_ERR "ctxfi: Can't meet SRC resource request!\n"); + dev_err(mgr->card->dev, + "Can't meet SRC resource request!\n"); return err; } @@ -543,7 +544,7 @@ static int src_mgr_commit_write(struct src_mgr *mgr) return 0; } -int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr) +int src_mgr_create(struct hw *hw, struct src_mgr **rsrc_mgr) { int err, i; struct src_mgr *src_mgr; @@ -558,7 +559,7 @@ int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr) goto error1; spin_lock_init(&src_mgr->mgr_lock); - conj_mask = ((struct hw *)hw)->src_dirty_conj_mask(); + conj_mask = hw->src_dirty_conj_mask(); src_mgr->get_src = get_src_rsc; src_mgr->put_src = put_src_rsc; @@ -566,12 +567,13 @@ int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr) src_mgr->src_enable = src_enable; src_mgr->src_disable = src_disable; src_mgr->commit_write = src_mgr_commit_write; + src_mgr->card = hw->card; /* Disable all SRC resources. */ for (i = 0; i < 256; i++) - ((struct hw *)hw)->src_mgr_dsb_src(src_mgr->mgr.ctrl_blk, i); + hw->src_mgr_dsb_src(src_mgr->mgr.ctrl_blk, i); - ((struct hw *)hw)->src_mgr_commit_write(hw, src_mgr->mgr.ctrl_blk); + hw->src_mgr_commit_write(hw, src_mgr->mgr.ctrl_blk); *rsrc_mgr = src_mgr; @@ -739,7 +741,8 @@ static int get_srcimp_rsc(struct srcimp_mgr *mgr, } spin_unlock_irqrestore(&mgr->mgr_lock, flags); if (err) { - printk(KERN_ERR "ctxfi: Can't meet SRCIMP resource request!\n"); + dev_err(mgr->card->dev, + "Can't meet SRCIMP resource request!\n"); goto error1; } @@ -825,7 +828,7 @@ static int srcimp_imap_delete(struct srcimp_mgr *mgr, struct imapper *entry) return err; } -int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrcimp_mgr) +int srcimp_mgr_create(struct hw *hw, struct srcimp_mgr **rsrcimp_mgr) { int err; struct srcimp_mgr *srcimp_mgr; @@ -857,6 +860,7 @@ int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrcimp_mgr) srcimp_mgr->put_srcimp = put_srcimp_rsc; srcimp_mgr->imap_add = srcimp_imap_add; srcimp_mgr->imap_delete = srcimp_imap_delete; + srcimp_mgr->card = hw->card; *rsrcimp_mgr = srcimp_mgr; diff --git a/sound/pci/ctxfi/ctsrc.h b/sound/pci/ctxfi/ctsrc.h index 259366a..da7573c 100644 --- a/sound/pci/ctxfi/ctsrc.h +++ b/sound/pci/ctxfi/ctsrc.h @@ -23,6 +23,7 @@ #include "ctimap.h" #include <linux/spinlock.h> #include <linux/list.h> +#include <sound/core.h> #define SRC_STATE_OFF 0x0 #define SRC_STATE_INIT 0x4 @@ -85,6 +86,7 @@ struct src_desc { /* Define src manager object */ struct src_mgr { struct rsc_mgr mgr; /* Basic resource manager info */ + struct snd_card *card; /* pointer to this card */ spinlock_t mgr_lock; /* request src resource */ @@ -123,6 +125,7 @@ struct srcimp_desc { struct srcimp_mgr { struct rsc_mgr mgr; /* Basic resource manager info */ + struct snd_card *card; /* pointer to this card */ spinlock_t mgr_lock; spinlock_t imap_lock; struct list_head imappers; @@ -140,10 +143,10 @@ struct srcimp_mgr { }; /* Constructor and destructor of SRC resource manager */ -int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr); +int src_mgr_create(struct hw *hw, struct src_mgr **rsrc_mgr); int src_mgr_destroy(struct src_mgr *src_mgr); /* Constructor and destructor of SRCIMP resource manager */ -int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrc_mgr); +int srcimp_mgr_create(struct hw *hw, struct srcimp_mgr **rsrc_mgr); int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr); #endif /* CTSRC_H */ diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c index 6109490..419306e 100644 --- a/sound/pci/ctxfi/ctvmem.c +++ b/sound/pci/ctxfi/ctvmem.c @@ -16,6 +16,7 @@ */ #include "ctvmem.h" +#include "ctatc.h" #include <linux/slab.h> #include <linux/mm.h> #include <linux/io.h> @@ -29,15 +30,15 @@ * @size must be page aligned. * */ static struct ct_vm_block * -get_vm_block(struct ct_vm *vm, unsigned int size) +get_vm_block(struct ct_vm *vm, unsigned int size, struct ct_atc *atc) { struct ct_vm_block *block = NULL, *entry; struct list_head *pos; size = CT_PAGE_ALIGN(size); if (size > vm->size) { - printk(KERN_ERR "ctxfi: Fail! No sufficient device virtual " - "memory space available!\n"); + dev_err(atc->card->dev, + "Fail! No sufficient device virtual memory space available!\n"); return NULL; } @@ -129,11 +130,12 @@ ct_vm_map(struct ct_vm *vm, struct snd_pcm_substream *substream, int size) unsigned int pte_start; unsigned i, pages; unsigned long *ptp; + struct ct_atc *atc = snd_pcm_substream_chip(substream); - block = get_vm_block(vm, size); + block = get_vm_block(vm, size, atc); if (block == NULL) { - printk(KERN_ERR "ctxfi: No virtual memory block that is big " - "enough to allocate!\n"); + dev_err(atc->card->dev, + "No virtual memory block that is big enough to allocate!\n"); return NULL; } diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c index 8f8b566..f2f3277 100644 --- a/sound/pci/ctxfi/xfi.c +++ b/sound/pci/ctxfi/xfi.c @@ -76,17 +76,18 @@ ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) if (err) return err; if ((reference_rate != 48000) && (reference_rate != 44100)) { - printk(KERN_ERR "ctxfi: Invalid reference_rate value %u!!!\n", - reference_rate); - printk(KERN_ERR "ctxfi: The valid values for reference_rate " - "are 48000 and 44100, Value 48000 is assumed.\n"); + dev_err(card->dev, + "Invalid reference_rate value %u!!!\n", + reference_rate); + dev_err(card->dev, + "The valid values for reference_rate are 48000 and 44100, Value 48000 is assumed.\n"); reference_rate = 48000; } if ((multiple != 1) && (multiple != 2) && (multiple != 4)) { - printk(KERN_ERR "ctxfi: Invalid multiple value %u!!!\n", - multiple); - printk(KERN_ERR "ctxfi: The valid values for multiple are " - "1, 2 and 4, Value 2 is assumed.\n"); + dev_err(card->dev, "Invalid multiple value %u!!!\n", + multiple); + dev_err(card->dev, + "The valid values for multiple are 1, 2 and 4, Value 2 is assumed.\n"); multiple = 2; } err = ct_atc_create(card, pci, reference_rate, multiple, diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 51dea49..fcc5e47 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -57,12 +57,14 @@ static void sort_pins_by_sequence(hda_nid_t *pins, struct auto_out_pin *list, /* add the found input-pin to the cfg->inputs[] table */ -static void add_auto_cfg_input_pin(struct auto_pin_cfg *cfg, hda_nid_t nid, - int type) +static void add_auto_cfg_input_pin(struct hda_codec *codec, struct auto_pin_cfg *cfg, + hda_nid_t nid, int type) { if (cfg->num_inputs < AUTO_CFG_MAX_INS) { cfg->inputs[cfg->num_inputs].pin = nid; cfg->inputs[cfg->num_inputs].type = type; + cfg->inputs[cfg->num_inputs].has_boost_on_pin = + nid_has_volume(codec, nid, HDA_INPUT); cfg->num_inputs++; } } @@ -71,7 +73,12 @@ static int compare_input_type(const void *ap, const void *bp) { const struct auto_pin_cfg_item *a = ap; const struct auto_pin_cfg_item *b = bp; - return (int)(a->type - b->type); + if (a->type != b->type) + return (int)(a->type - b->type); + + /* In case one has boost and the other one has not, + pick the one with boost first. */ + return (int)(b->has_boost_on_pin - a->has_boost_on_pin); } /* Reorder the surround channels @@ -268,16 +275,16 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, cfg->hp_outs++; break; case AC_JACK_MIC_IN: - add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_MIC); + add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_MIC); break; case AC_JACK_LINE_IN: - add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_LINE_IN); + add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_LINE_IN); break; case AC_JACK_CD: - add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD); + add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_CD); break; case AC_JACK_AUX: - add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX); + add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_AUX); break; case AC_JACK_SPDIF_OUT: case AC_JACK_DIG_OTHER_OUT: diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/pci/hda/hda_auto_parser.h index e941f60..2b8e29f 100644 --- a/sound/pci/hda/hda_auto_parser.h +++ b/sound/pci/hda/hda_auto_parser.h @@ -38,6 +38,7 @@ struct auto_pin_cfg_item { int type; unsigned int is_headset_mic:1; unsigned int is_headphone_mic:1; /* Mic-only in headphone jack */ + unsigned int has_boost_on_pin:1; }; struct auto_pin_cfg; diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index ec6a7d0..15e0089 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2002,6 +2002,26 @@ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction) EXPORT_SYMBOL_GPL(query_amp_caps); /** + * snd_hda_check_amp_caps - query AMP capabilities + * @codec: the HD-audio codec + * @nid: the NID to query + * @dir: either #HDA_INPUT or #HDA_OUTPUT + * + * Check whether the widget has the given amp capability for the direction. + */ +bool snd_hda_check_amp_caps(struct hda_codec *codec, hda_nid_t nid, + int dir, unsigned int bits) +{ + if (!nid) + return false; + if (get_wcaps(codec, nid) & (1 << (dir + 1))) + if (query_amp_caps(codec, nid, dir) & bits) + return true; + return false; +} +EXPORT_SYMBOL_GPL(snd_hda_check_amp_caps); + +/** * snd_hda_override_amp_caps - Override the AMP capabilities * @codec: the CODEC to clean up * @nid: the NID to clean up @@ -4817,121 +4837,6 @@ int snd_hda_build_pcms(struct hda_bus *bus) EXPORT_SYMBOL_GPL(snd_hda_build_pcms); /** - * snd_hda_check_board_config - compare the current codec with the config table - * @codec: the HDA codec - * @num_configs: number of config enums - * @models: array of model name strings - * @tbl: configuration table, terminated by null entries - * - * Compares the modelname or PCI subsystem id of the current codec with the - * given configuration table. If a matching entry is found, returns its - * config value (supposed to be 0 or positive). - * - * If no entries are matching, the function returns a negative value. - */ -int snd_hda_check_board_config(struct hda_codec *codec, - int num_configs, const char * const *models, - const struct snd_pci_quirk *tbl) -{ - if (codec->modelname && models) { - int i; - for (i = 0; i < num_configs; i++) { - if (models[i] && - !strcmp(codec->modelname, models[i])) { - codec_info(codec, "model '%s' is selected\n", - models[i]); - return i; - } - } - } - - if (!codec->bus->pci || !tbl) - return -1; - - tbl = snd_pci_quirk_lookup(codec->bus->pci, tbl); - if (!tbl) - return -1; - if (tbl->value >= 0 && tbl->value < num_configs) { -#ifdef CONFIG_SND_DEBUG_VERBOSE - char tmp[10]; - const char *model = NULL; - if (models) - model = models[tbl->value]; - if (!model) { - sprintf(tmp, "#%d", tbl->value); - model = tmp; - } - codec_info(codec, "model '%s' is selected for config %x:%x (%s)\n", - model, tbl->subvendor, tbl->subdevice, - (tbl->name ? tbl->name : "Unknown device")); -#endif - return tbl->value; - } - return -1; -} -EXPORT_SYMBOL_GPL(snd_hda_check_board_config); - -/** - * snd_hda_check_board_codec_sid_config - compare the current codec - subsystem ID with the - config table - - This is important for Gateway notebooks with SB450 HDA Audio - where the vendor ID of the PCI device is: - ATI Technologies Inc SB450 HDA Audio [1002:437b] - and the vendor/subvendor are found only at the codec. - - * @codec: the HDA codec - * @num_configs: number of config enums - * @models: array of model name strings - * @tbl: configuration table, terminated by null entries - * - * Compares the modelname or PCI subsystem id of the current codec with the - * given configuration table. If a matching entry is found, returns its - * config value (supposed to be 0 or positive). - * - * If no entries are matching, the function returns a negative value. - */ -int snd_hda_check_board_codec_sid_config(struct hda_codec *codec, - int num_configs, const char * const *models, - const struct snd_pci_quirk *tbl) -{ - const struct snd_pci_quirk *q; - - /* Search for codec ID */ - for (q = tbl; q->subvendor; q++) { - unsigned int mask = 0xffff0000 | q->subdevice_mask; - unsigned int id = (q->subdevice | (q->subvendor << 16)) & mask; - if ((codec->subsystem_id & mask) == id) - break; - } - - if (!q->subvendor) - return -1; - - tbl = q; - - if (tbl->value >= 0 && tbl->value < num_configs) { -#ifdef CONFIG_SND_DEBUG_VERBOSE - char tmp[10]; - const char *model = NULL; - if (models) - model = models[tbl->value]; - if (!model) { - sprintf(tmp, "#%d", tbl->value); - model = tmp; - } - codec_info(codec, "model '%s' is selected for config %x:%x (%s)\n", - model, tbl->subvendor, tbl->subdevice, - (tbl->name ? tbl->name : "Unknown device")); -#endif - return tbl->value; - } - return -1; -} -EXPORT_SYMBOL_GPL(snd_hda_check_board_codec_sid_config); - -/** * snd_hda_add_new_ctls - create controls from the array * @codec: the HDA codec * @knew: the array of struct snd_kcontrol_new diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index bbc5a13..9c8820f 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -687,6 +687,4 @@ snd_hda_codec_load_dsp_cleanup(struct hda_codec *codec, struct snd_dma_buffer *dmab) {} #endif -#define EXPORT_SYMBOL_HDA(sym) EXPORT_SYMBOL_GPL(sym) - #endif /* __SOUND_HDA_CODEC_H */ diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index b956449..64220c0 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -31,6 +31,7 @@ #include <linux/module.h> #include <sound/core.h> #include <sound/jack.h> +#include <sound/tlv.h> #include "hda_codec.h" #include "hda_local.h" #include "hda_auto_parser.h" @@ -518,18 +519,6 @@ static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs) return val; } -/* check whether the widget has the given amp capability for the direction */ -static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, - int dir, unsigned int bits) -{ - if (!nid) - return false; - if (get_wcaps(codec, nid) & (1 << (dir + 1))) - if (query_amp_caps(codec, nid, dir) & bits) - return true; - return false; -} - static bool same_amp_caps(struct hda_codec *codec, hda_nid_t nid1, hda_nid_t nid2, int dir) { @@ -539,11 +528,6 @@ static bool same_amp_caps(struct hda_codec *codec, hda_nid_t nid1, query_amp_caps(codec, nid2, dir)); } -#define nid_has_mute(codec, nid, dir) \ - check_amp_caps(codec, nid, dir, (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) -#define nid_has_volume(codec, nid, dir) \ - check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS) - /* look for a widget suitable for assigning a mute switch in the path */ static hda_nid_t look_for_out_mute_nid(struct hda_codec *codec, struct nid_path *path) @@ -1105,6 +1089,7 @@ enum { */ static int assign_out_path_ctls(struct hda_codec *codec, struct nid_path *path) { + struct hda_gen_spec *spec = codec->spec; hda_nid_t nid; unsigned int val; int badness = 0; @@ -1119,6 +1104,8 @@ static int assign_out_path_ctls(struct hda_codec *codec, struct nid_path *path) nid = look_for_out_vol_nid(codec, path); if (nid) { val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + if (spec->dac_min_mute) + val |= HDA_AMP_VAL_MIN_MUTE; if (is_ctl_used(codec, val, NID_PATH_VOL_CTL)) badness += BAD_SHARED_VOL; else @@ -1880,9 +1867,12 @@ static int parse_output_paths(struct hda_codec *codec) path = snd_hda_get_path_from_idx(codec, spec->out_paths[0]); if (path) spec->vmaster_nid = look_for_out_vol_nid(codec, path); - if (spec->vmaster_nid) + if (spec->vmaster_nid) { snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, HDA_OUTPUT, spec->vmaster_tlv); + if (spec->dac_min_mute) + spec->vmaster_tlv[3] |= TLV_DB_SCALE_MUTE; + } } /* set initial pinctl targets */ @@ -2025,7 +2015,8 @@ static int create_speaker_out_ctls(struct hda_codec *codec) * independent HP controls */ -static void call_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack); +static void call_hp_automute(struct hda_codec *codec, + struct hda_jack_callback *jack); static int indep_hp_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -3941,7 +3932,8 @@ static void call_update_outputs(struct hda_codec *codec) } /* standard HP-automute helper */ -void snd_hda_gen_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) +void snd_hda_gen_hp_automute(struct hda_codec *codec, + struct hda_jack_callback *jack) { struct hda_gen_spec *spec = codec->spec; hda_nid_t *pins = spec->autocfg.hp_pins; @@ -3961,7 +3953,8 @@ void snd_hda_gen_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) EXPORT_SYMBOL_GPL(snd_hda_gen_hp_automute); /* standard line-out-automute helper */ -void snd_hda_gen_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) +void snd_hda_gen_line_automute(struct hda_codec *codec, + struct hda_jack_callback *jack) { struct hda_gen_spec *spec = codec->spec; @@ -3981,7 +3974,8 @@ void snd_hda_gen_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jac EXPORT_SYMBOL_GPL(snd_hda_gen_line_automute); /* standard mic auto-switch helper */ -void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *jack) +void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, + struct hda_jack_callback *jack) { struct hda_gen_spec *spec = codec->spec; int i; @@ -4004,7 +3998,8 @@ void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *ja EXPORT_SYMBOL_GPL(snd_hda_gen_mic_autoswitch); /* call appropriate hooks */ -static void call_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) +static void call_hp_automute(struct hda_codec *codec, + struct hda_jack_callback *jack) { struct hda_gen_spec *spec = codec->spec; if (spec->hp_automute_hook) @@ -4014,7 +4009,7 @@ static void call_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) } static void call_line_automute(struct hda_codec *codec, - struct hda_jack_tbl *jack) + struct hda_jack_callback *jack) { struct hda_gen_spec *spec = codec->spec; if (spec->line_automute_hook) @@ -4024,7 +4019,7 @@ static void call_line_automute(struct hda_codec *codec, } static void call_mic_autoswitch(struct hda_codec *codec, - struct hda_jack_tbl *jack) + struct hda_jack_callback *jack) { struct hda_gen_spec *spec = codec->spec; if (spec->mic_autoswitch_hook) @@ -4173,7 +4168,7 @@ static int check_auto_mute_availability(struct hda_codec *codec) if (!is_jack_detectable(codec, nid)) continue; codec_dbg(codec, "Enable HP auto-muting on NID 0x%x\n", nid); - snd_hda_jack_detect_enable_callback(codec, nid, HDA_GEN_HP_EVENT, + snd_hda_jack_detect_enable_callback(codec, nid, call_hp_automute); spec->detect_hp = 1; } @@ -4186,7 +4181,6 @@ static int check_auto_mute_availability(struct hda_codec *codec) continue; codec_dbg(codec, "Enable Line-Out auto-muting on NID 0x%x\n", nid); snd_hda_jack_detect_enable_callback(codec, nid, - HDA_GEN_FRONT_EVENT, call_line_automute); spec->detect_lo = 1; } @@ -4228,7 +4222,6 @@ static bool auto_mic_check_imux(struct hda_codec *codec) for (i = 1; i < spec->am_num_entries; i++) snd_hda_jack_detect_enable_callback(codec, spec->am_entry[i].pin, - HDA_GEN_MIC_EVENT, call_mic_autoswitch); return true; } diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index bb2dea7..61dd515 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -12,12 +12,6 @@ #ifndef __SOUND_HDA_GENERIC_H #define __SOUND_HDA_GENERIC_H -/* unsol event tags */ -enum { - HDA_GEN_HP_EVENT = 1, HDA_GEN_FRONT_EVENT, HDA_GEN_MIC_EVENT, - HDA_GEN_LAST_EVENT = HDA_GEN_MIC_EVENT -}; - /* table entry for multi-io paths */ struct hda_multi_io { hda_nid_t pin; /* multi-io widget pin NID */ @@ -231,6 +225,7 @@ struct hda_gen_spec { unsigned int add_stereo_mix_input:1; /* add aamix as a capture src */ unsigned int add_jack_modes:1; /* add i/o jack mode enum ctls */ unsigned int power_down_unused:1; /* power down unused widgets */ + unsigned int dac_min_mute:1; /* minimal = mute for DACs */ /* other internal flags */ unsigned int no_analog:1; /* digital I/O only */ @@ -289,11 +284,11 @@ struct hda_gen_spec { /* automute / autoswitch hooks */ void (*hp_automute_hook)(struct hda_codec *codec, - struct hda_jack_tbl *tbl); + struct hda_jack_callback *cb); void (*line_automute_hook)(struct hda_codec *codec, - struct hda_jack_tbl *tbl); + struct hda_jack_callback *cb); void (*mic_autoswitch_hook)(struct hda_codec *codec, - struct hda_jack_tbl *tbl); + struct hda_jack_callback *cb); }; int snd_hda_gen_spec_init(struct hda_gen_spec *spec); @@ -325,11 +320,11 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec); /* standard jack event callbacks */ void snd_hda_gen_hp_automute(struct hda_codec *codec, - struct hda_jack_tbl *jack); + struct hda_jack_callback *jack); void snd_hda_gen_line_automute(struct hda_codec *codec, - struct hda_jack_tbl *jack); + struct hda_jack_callback *jack); void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, - struct hda_jack_tbl *jack); + struct hda_jack_callback *jack); void snd_hda_gen_update_outputs(struct hda_codec *codec); #ifdef CONFIG_PM diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 9746d73..f56765a 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -94,7 +94,7 @@ EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get_from_tag); /** * snd_hda_jack_tbl_new - create a jack-table entry for the given NID */ -struct hda_jack_tbl * +static struct hda_jack_tbl * snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid) { struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid); @@ -108,21 +108,24 @@ snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid) jack->tag = codec->jacktbl.used; return jack; } -EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_new); void snd_hda_jack_tbl_clear(struct hda_codec *codec) { + struct hda_jack_tbl *jack = codec->jacktbl.list; + int i; + + for (i = 0; i < codec->jacktbl.used; i++, jack++) { + struct hda_jack_callback *cb, *next; #ifdef CONFIG_SND_HDA_INPUT_JACK - /* free jack instances manually when clearing/reconfiguring */ - if (!codec->bus->shutdown && codec->jacktbl.list) { - struct hda_jack_tbl *jack = codec->jacktbl.list; - int i; - for (i = 0; i < codec->jacktbl.used; i++, jack++) { - if (jack->jack) - snd_device_free(codec->bus->card, jack->jack); + /* free jack instances manually when clearing/reconfiguring */ + if (!codec->bus->shutdown && jack->jack) + snd_device_free(codec->bus->card, jack->jack); +#endif + for (cb = jack->callback; cb; cb = next) { + next = cb->next; + kfree(cb); } } -#endif snd_array_free(&codec->jacktbl); } @@ -215,33 +218,49 @@ EXPORT_SYMBOL_GPL(snd_hda_jack_detect_state); /** * snd_hda_jack_detect_enable - enable the jack-detection + * + * In the case of error, the return value will be a pointer embedded with + * errno. Check and handle the return value appropriately with standard + * macros such as @IS_ERR() and @PTR_ERR(). */ -int snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid, - unsigned char action, - hda_jack_callback cb) +struct hda_jack_callback * +snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid, + hda_jack_callback_fn func) { - struct hda_jack_tbl *jack = snd_hda_jack_tbl_new(codec, nid); + struct hda_jack_tbl *jack; + struct hda_jack_callback *callback = NULL; + int err; + + jack = snd_hda_jack_tbl_new(codec, nid); if (!jack) - return -ENOMEM; + return ERR_PTR(-ENOMEM); + if (func) { + callback = kzalloc(sizeof(*callback), GFP_KERNEL); + if (!callback) + return ERR_PTR(-ENOMEM); + callback->func = func; + callback->tbl = jack; + callback->next = jack->callback; + jack->callback = callback; + } + if (jack->jack_detect) - return 0; /* already registered */ + return callback; /* already registered */ jack->jack_detect = 1; - if (action) - jack->action = action; - if (cb) - jack->callback = cb; if (codec->jackpoll_interval > 0) - return 0; /* No unsol if we're polling instead */ - return snd_hda_codec_write_cache(codec, nid, 0, + return callback; /* No unsol if we're polling instead */ + err = snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | jack->tag); + if (err < 0) + return ERR_PTR(err); + return callback; } EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable_callback); -int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid, - unsigned char action) +int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid) { - return snd_hda_jack_detect_enable_callback(codec, nid, action, NULL); + return PTR_ERR_OR_ZERO(snd_hda_jack_detect_enable_callback(codec, nid, NULL)); } EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable); @@ -431,7 +450,7 @@ static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, return err; if (!phantom_jack) - return snd_hda_jack_detect_enable(codec, nid, 0); + return snd_hda_jack_detect_enable(codec, nid); return 0; } @@ -498,13 +517,17 @@ EXPORT_SYMBOL_GPL(snd_hda_jack_add_kctls); static void call_jack_callback(struct hda_codec *codec, struct hda_jack_tbl *jack) { - if (jack->callback) - jack->callback(codec, jack); + struct hda_jack_callback *cb; + + for (cb = jack->callback; cb; cb = cb->next) + cb->func(codec, cb); if (jack->gated_jack) { struct hda_jack_tbl *gated = snd_hda_jack_tbl_get(codec, jack->gated_jack); - if (gated && gated->callback) - gated->callback(codec, gated); + if (gated) { + for (cb = gated->callback; cb; cb = cb->next) + cb->func(codec, cb); + } } } diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index 46e1ea8..13cb375 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -12,17 +12,25 @@ #ifndef __SOUND_HDA_JACK_H #define __SOUND_HDA_JACK_H +#include <linux/err.h> + struct auto_pin_cfg; struct hda_jack_tbl; +struct hda_jack_callback; + +typedef void (*hda_jack_callback_fn) (struct hda_codec *, struct hda_jack_callback *); -typedef void (*hda_jack_callback) (struct hda_codec *, struct hda_jack_tbl *); +struct hda_jack_callback { + struct hda_jack_tbl *tbl; + hda_jack_callback_fn func; + unsigned int private_data; /* arbitrary data */ + struct hda_jack_callback *next; +}; struct hda_jack_tbl { hda_nid_t nid; - unsigned char action; /* event action (0 = none) */ unsigned char tag; /* unsol event tag */ - unsigned int private_data; /* arbitrary data */ - hda_jack_callback callback; + struct hda_jack_callback *callback; /* jack-detection stuff */ unsigned int pin_sense; /* cached pin-sense value */ unsigned int jack_detect:1; /* capable of jack-detection? */ @@ -43,34 +51,14 @@ snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid); struct hda_jack_tbl * snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec, unsigned char tag); -struct hda_jack_tbl * -snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid); void snd_hda_jack_tbl_clear(struct hda_codec *codec); -/** - * snd_hda_jack_get_action - get jack-tbl entry for the tag - * - * Call this from the unsol event handler to get the assigned action for the - * event. This will mark the dirty flag for the later reporting, too. - */ -static inline unsigned char -snd_hda_jack_get_action(struct hda_codec *codec, unsigned int tag) -{ - struct hda_jack_tbl *jack = snd_hda_jack_tbl_get_from_tag(codec, tag); - if (jack) { - jack->jack_dirty = 1; - return jack->action; - } - return 0; -} - void snd_hda_jack_set_dirty_all(struct hda_codec *codec); -int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid, - unsigned char action); -int snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid, - unsigned char action, - hda_jack_callback cb); +int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid); +struct hda_jack_callback * +snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid, + hda_jack_callback_fn cb); int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid, hda_nid_t gating_nid); diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 364bb41..7eb44e7 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -371,12 +371,6 @@ void snd_print_pcm_bits(int pcm, char *buf, int buflen); /* * Misc */ -int snd_hda_check_board_config(struct hda_codec *codec, int num_configs, - const char * const *modelnames, - const struct snd_pci_quirk *pci_list); -int snd_hda_check_board_codec_sid_config(struct hda_codec *codec, - int num_configs, const char * const *models, - const struct snd_pci_quirk *tbl); int snd_hda_add_new_ctls(struct hda_codec *codec, const struct snd_kcontrol_new *knew); @@ -609,6 +603,14 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid); int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid, unsigned int caps); +bool snd_hda_check_amp_caps(struct hda_codec *codec, hda_nid_t nid, + int dir, unsigned int bits); + +#define nid_has_mute(codec, nid, dir) \ + snd_hda_check_amp_caps(codec, nid, dir, (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) +#define nid_has_volume(codec, nid, dir) \ + snd_hda_check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS) + /* flags for hda_nid_item */ #define HDA_NID_ITEM_AMP (1<<0) diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c index e207909..9b49f15 100644 --- a/sound/pci/hda/hda_sysfs.c +++ b/sound/pci/hda/hda_sysfs.c @@ -514,7 +514,7 @@ enum { static inline int strmatch(const char *a, const char *b) { - return strnicmp(a, b, strlen(b)) == 0; + return strncasecmp(a, b, strlen(b)) == 0; } /* parse the contents after the line "[codec]" diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 5d8455e..4f7ffa8 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -3224,8 +3224,14 @@ static void ca0132_unsol_hp_delayed(struct work_struct *work) { struct ca0132_spec *spec = container_of( to_delayed_work(work), struct ca0132_spec, unsol_hp_work); + struct hda_jack_tbl *jack; + ca0132_select_out(spec->codec); - snd_hda_jack_report_sync(spec->codec); + jack = snd_hda_jack_tbl_get(spec->codec, UNSOL_TAG_HP); + if (jack) { + jack->block_report = 0; + snd_hda_jack_report_sync(spec->codec); + } } static void ca0132_set_dmic(struct hda_codec *codec, int enable); @@ -4114,12 +4120,6 @@ static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc) } } -static void ca0132_init_unsol(struct hda_codec *codec) -{ - snd_hda_jack_detect_enable(codec, UNSOL_TAG_HP, UNSOL_TAG_HP); - snd_hda_jack_detect_enable(codec, UNSOL_TAG_AMIC1, UNSOL_TAG_AMIC1); -} - static void refresh_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir) { unsigned int caps; @@ -4390,7 +4390,8 @@ static void ca0132_download_dsp(struct hda_codec *codec) ca0132_set_dsp_msr(codec, true); } -static void ca0132_process_dsp_response(struct hda_codec *codec) +static void ca0132_process_dsp_response(struct hda_codec *codec, + struct hda_jack_callback *callback) { struct ca0132_spec *spec = codec->spec; @@ -4403,36 +4404,31 @@ static void ca0132_process_dsp_response(struct hda_codec *codec) dspio_clear_response_queue(codec); } -static void ca0132_unsol_event(struct hda_codec *codec, unsigned int res) +static void hp_callback(struct hda_codec *codec, struct hda_jack_callback *cb) { struct ca0132_spec *spec = codec->spec; - if (((res >> AC_UNSOL_RES_TAG_SHIFT) & 0x3f) == UNSOL_TAG_DSP) { - ca0132_process_dsp_response(codec); - } else { - res = snd_hda_jack_get_action(codec, - (res >> AC_UNSOL_RES_TAG_SHIFT) & 0x3f); - - codec_dbg(codec, "snd_hda_jack_get_action: 0x%x\n", res); - - switch (res) { - case UNSOL_TAG_HP: - /* Delay enabling the HP amp, to let the mic-detection - * state machine run. - */ - cancel_delayed_work_sync(&spec->unsol_hp_work); - queue_delayed_work(codec->bus->workq, - &spec->unsol_hp_work, - msecs_to_jiffies(500)); - break; - case UNSOL_TAG_AMIC1: - ca0132_select_mic(codec); - snd_hda_jack_report_sync(codec); - break; - default: - break; - } - } + /* Delay enabling the HP amp, to let the mic-detection + * state machine run. + */ + cancel_delayed_work_sync(&spec->unsol_hp_work); + queue_delayed_work(codec->bus->workq, &spec->unsol_hp_work, + msecs_to_jiffies(500)); + cb->tbl->block_report = 1; +} + +static void amic_callback(struct hda_codec *codec, struct hda_jack_callback *cb) +{ + ca0132_select_mic(codec); +} + +static void ca0132_init_unsol(struct hda_codec *codec) +{ + snd_hda_jack_detect_enable_callback(codec, UNSOL_TAG_HP, hp_callback); + snd_hda_jack_detect_enable_callback(codec, UNSOL_TAG_AMIC1, + amic_callback); + snd_hda_jack_detect_enable_callback(codec, UNSOL_TAG_DSP, + ca0132_process_dsp_response); } /* @@ -4443,8 +4439,6 @@ static void ca0132_unsol_event(struct hda_codec *codec, unsigned int res) static struct hda_verb ca0132_base_init_verbs[] = { /*enable ct extension*/ {0x15, VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, 0x1}, - /*enable DSP node unsol, needed for DSP download*/ - {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | UNSOL_TAG_DSP}, {} }; @@ -4561,6 +4555,8 @@ static int ca0132_init(struct hda_codec *codec) snd_hda_power_up(codec); + ca0132_init_unsol(codec); + ca0132_init_params(codec); ca0132_init_flags(codec); snd_hda_sequence_write(codec, spec->base_init_verbs); @@ -4583,8 +4579,6 @@ static int ca0132_init(struct hda_codec *codec) for (i = 0; i < spec->num_init_verbs; i++) snd_hda_sequence_write(codec, spec->init_verbs[i]); - ca0132_init_unsol(codec); - ca0132_select_out(codec); ca0132_select_mic(codec); @@ -4612,7 +4606,7 @@ static struct hda_codec_ops ca0132_patch_ops = { .build_pcms = ca0132_build_pcms, .init = ca0132_init, .free = ca0132_free, - .unsol_event = ca0132_unsol_event, + .unsol_event = snd_hda_jack_unsol_event, }; static void ca0132_config(struct hda_codec *codec) diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 3db724e..1589c9b 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -135,8 +135,6 @@ enum { #define CS421X_IDX_DAC_CFG 0x03 #define CS421X_IDX_SPK_CTL 0x04 -#define SPDIF_EVENT 0x04 - /* Cirrus Logic CS4213 is like CS4210 but does not have SPDIF input/output */ #define CS4213_VENDOR_NID 0x09 @@ -984,7 +982,7 @@ static void cs4210_pinmux_init(struct hda_codec *codec) } static void cs4210_spdif_automute(struct hda_codec *codec, - struct hda_jack_tbl *tbl) + struct hda_jack_callback *tbl) { struct cs_spec *spec = codec->spec; bool spdif_present = false; @@ -1019,7 +1017,6 @@ static void parse_cs421x_digital(struct hda_codec *codec) if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) { spec->spdif_detect = 1; snd_hda_jack_detect_enable_callback(codec, nid, - SPDIF_EVENT, cs4210_spdif_automute); } } diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 47ccb8f..71e4bad 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -26,7 +26,6 @@ #include <linux/module.h> #include <sound/core.h> #include <sound/jack.h> -#include <sound/tlv.h> #include "hda_codec.h" #include "hda_local.h" @@ -394,7 +393,8 @@ static void olpc_xo_update_mic_pins(struct hda_codec *codec) } /* mic_autoswitch hook */ -static void olpc_xo_automic(struct hda_codec *codec, struct hda_jack_tbl *jack) +static void olpc_xo_automic(struct hda_codec *codec, + struct hda_jack_callback *jack) { struct conexant_spec *spec = codec->spec; int saved_cached_write = codec->cached_write; @@ -752,6 +752,7 @@ static const struct hda_model_fixup cxt5051_fixup_models[] = { static const struct snd_pci_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT_FIXUP_ASPIRE_DMIC), + SND_PCI_QUIRK(0x1025, 0x054f, "Acer Aspire 4830T", CXT_FIXUP_ASPIRE_DMIC), SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN), SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT_FIXUP_OLPC_XO), SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410), @@ -787,6 +788,7 @@ static const struct hda_model_fixup cxt5066_fixup_models[] = { */ static void add_cx5051_fake_mutes(struct hda_codec *codec) { + struct conexant_spec *spec = codec->spec; static hda_nid_t out_nids[] = { 0x10, 0x11, 0 }; @@ -796,6 +798,7 @@ static void add_cx5051_fake_mutes(struct hda_codec *codec) snd_hda_override_amp_caps(codec, *p, HDA_OUTPUT, AC_AMPCAP_MIN_MUTE | query_amp_caps(codec, *p, HDA_OUTPUT)); + spec->gen.dac_min_mute = true; } static int patch_conexant_auto(struct hda_codec *codec) @@ -868,11 +871,6 @@ static int patch_conexant_auto(struct hda_codec *codec) if (err < 0) goto error; - if (codec->vendor_id == 0x14f15051) { - /* minimum value is actually mute */ - spec->gen.vmaster_tlv[3] |= TLV_DB_SCALE_MUTE; - } - codec->patch_ops = cx_auto_patch_ops; /* Some laptops with Conexant chips show stalls in S3 resume, diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 99d7d7f..39862e9 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1163,17 +1163,23 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll); -static void jack_callback(struct hda_codec *codec, struct hda_jack_tbl *jack) +static void check_presence_and_report(struct hda_codec *codec, hda_nid_t nid) { struct hdmi_spec *spec = codec->spec; - int pin_idx = pin_nid_to_pin_index(codec, jack->nid); + int pin_idx = pin_nid_to_pin_index(codec, nid); + if (pin_idx < 0) return; - if (hdmi_present_sense(get_pin(spec, pin_idx), 1)) snd_hda_jack_report_sync(codec); } +static void jack_callback(struct hda_codec *codec, + struct hda_jack_callback *jack) +{ + check_presence_and_report(codec, jack->tbl->nid); +} + static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) { int tag = res >> AC_UNSOL_RES_TAG_SHIFT; @@ -1190,7 +1196,7 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) codec->addr, jack->nid, dev_entry, !!(res & AC_UNSOL_RES_IA), !!(res & AC_UNSOL_RES_PD), !!(res & AC_UNSOL_RES_ELDV)); - jack_callback(codec, jack); + check_presence_and_report(codec, jack->nid); } static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) @@ -2165,7 +2171,7 @@ static int generic_hdmi_init(struct hda_codec *codec) hda_nid_t pin_nid = per_pin->pin_nid; hdmi_init_pin(codec, pin_nid); - snd_hda_jack_detect_enable_callback(codec, pin_nid, pin_nid, + snd_hda_jack_detect_enable_callback(codec, pin_nid, codec->jackpoll_interval > 0 ? jack_callback : NULL); } return 0; @@ -2428,7 +2434,7 @@ static int simple_playback_init(struct hda_codec *codec) if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - snd_hda_jack_detect_enable(codec, pin, pin); + snd_hda_jack_detect_enable(codec, pin); return 0; } diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 1ba22fb..bc86c36 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -40,9 +40,6 @@ /* keep halting ALC5505 DSP, for power saving */ #define HALT_REALTEK_ALC5505 -/* unsol event tags */ -#define ALC_DCVOL_EVENT 0x08 - /* for GPIO Poll */ #define GPIO_MASK 0x03 @@ -93,11 +90,6 @@ struct alc_spec { struct alc_customize_define cdefine; unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */ - /* inverted dmic fix */ - unsigned int inv_dmic_fixup:1; /* has inverted digital-mic workaround */ - unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */ - hda_nid_t inv_dmic_pin; - /* mute LED for HP laptops, see alc269_fixup_mic_mute_hook() */ int mute_led_polarity; hda_nid_t mute_led_nid; @@ -129,6 +121,83 @@ struct alc_spec { }; /* + * COEF access helper functions + */ + +static int alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid, + unsigned int coef_idx) +{ + unsigned int val; + + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, coef_idx); + val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF, 0); + return val; +} + +#define alc_read_coef_idx(codec, coef_idx) \ + alc_read_coefex_idx(codec, 0x20, coef_idx) + +static void alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid, + unsigned int coef_idx, unsigned int coef_val) +{ + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, coef_idx); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PROC_COEF, coef_val); +} + +#define alc_write_coef_idx(codec, coef_idx, coef_val) \ + alc_write_coefex_idx(codec, 0x20, coef_idx, coef_val) + +static void alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid, + unsigned int coef_idx, unsigned int mask, + unsigned int bits_set) +{ + unsigned int val = alc_read_coefex_idx(codec, nid, coef_idx); + + if (val != -1) + alc_write_coefex_idx(codec, nid, coef_idx, + (val & ~mask) | bits_set); +} + +#define alc_update_coef_idx(codec, coef_idx, mask, bits_set) \ + alc_update_coefex_idx(codec, 0x20, coef_idx, mask, bits_set) + +/* a special bypass for COEF 0; read the cached value at the second time */ +static unsigned int alc_get_coef0(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (!spec->coef0) + spec->coef0 = alc_read_coef_idx(codec, 0); + return spec->coef0; +} + +/* coef writes/updates batch */ +struct coef_fw { + unsigned char nid; + unsigned char idx; + unsigned short mask; + unsigned short val; +}; + +#define UPDATE_COEFEX(_nid, _idx, _mask, _val) \ + { .nid = (_nid), .idx = (_idx), .mask = (_mask), .val = (_val) } +#define WRITE_COEFEX(_nid, _idx, _val) UPDATE_COEFEX(_nid, _idx, -1, _val) +#define WRITE_COEF(_idx, _val) WRITE_COEFEX(0x20, _idx, _val) +#define UPDATE_COEF(_idx, _mask, _val) UPDATE_COEFEX(0x20, _idx, _mask, _val) + +static void alc_process_coef_fw(struct hda_codec *codec, + const struct coef_fw *fw) +{ + for (; fw->nid; fw++) { + if (fw->mask == (unsigned short)-1) + alc_write_coefex_idx(codec, fw->nid, fw->idx, fw->val); + else + alc_update_coefex_idx(codec, fw->nid, fw->idx, + fw->mask, fw->val); + } +} + +/* * Append the given mixer and verb elements for the later use * The mixer array is referred in build_controls(), and init_verbs are * called in init(). @@ -173,20 +242,10 @@ static const struct hda_verb alc_gpio3_init_verbs[] = { static void alc_fix_pll(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - unsigned int val; - if (!spec->pll_nid) - return; - snd_hda_codec_write(codec, spec->pll_nid, 0, AC_VERB_SET_COEF_INDEX, - spec->pll_coef_idx); - val = snd_hda_codec_read(codec, spec->pll_nid, 0, - AC_VERB_GET_PROC_COEF, 0); - if (val == -1) - return; - snd_hda_codec_write(codec, spec->pll_nid, 0, AC_VERB_SET_COEF_INDEX, - spec->pll_coef_idx); - snd_hda_codec_write(codec, spec->pll_nid, 0, AC_VERB_SET_PROC_COEF, - val & ~(1 << spec->pll_coef_bit)); + if (spec->pll_nid) + alc_update_coefex_idx(codec, spec->pll_nid, spec->pll_coef_idx, + 1 << spec->pll_coef_bit, 0); } static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid, @@ -200,7 +259,8 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid, } /* update the master volume per volume-knob's unsol event */ -static void alc_update_knob_master(struct hda_codec *codec, struct hda_jack_tbl *jack) +static void alc_update_knob_master(struct hda_codec *codec, + struct hda_jack_callback *jack) { unsigned int val; struct snd_kcontrol *kctl; @@ -212,7 +272,7 @@ static void alc_update_knob_master(struct hda_codec *codec, struct hda_jack_tbl uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); if (!uctl) return; - val = snd_hda_codec_read(codec, jack->nid, 0, + val = snd_hda_codec_read(codec, jack->tbl->nid, 0, AC_VERB_GET_VOLUME_KNOB_CONTROL, 0); val &= HDA_AMP_VOLMASK; uctl->value.integer.value[0] = val; @@ -231,30 +291,18 @@ static void alc880_unsol_event(struct hda_codec *codec, unsigned int res) /* additional initialization for ALC888 variants */ static void alc888_coef_init(struct hda_codec *codec) { - unsigned int tmp; - - snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0); - tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0); - snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7); - if ((tmp & 0xf0) == 0x20) + if (alc_get_coef0(codec) == 0x20) /* alc888S-VC */ - snd_hda_codec_read(codec, 0x20, 0, - AC_VERB_SET_PROC_COEF, 0x830); + alc_write_coef_idx(codec, 7, 0x830); else /* alc888-VB */ - snd_hda_codec_read(codec, 0x20, 0, - AC_VERB_SET_PROC_COEF, 0x3030); + alc_write_coef_idx(codec, 7, 0x3030); } /* additional initialization for ALC889 variants */ static void alc889_coef_init(struct hda_codec *codec) { - unsigned int tmp; - - snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7); - tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0); - snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7); - snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, tmp|0x2010); + alc_update_coef_idx(codec, 7, 0, 0x2010); } /* turn on/off EAPD control (only if available) */ @@ -295,8 +343,6 @@ static void alc_eapd_shutup(struct hda_codec *codec) /* generic EAPD initialization */ static void alc_auto_init_amp(struct hda_codec *codec, int type) { - unsigned int tmp; - alc_auto_setup_eapd(codec, true); switch (type) { case ALC_INIT_GPIO1: @@ -311,15 +357,7 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type) case ALC_INIT_DEFAULT: switch (codec->vendor_id) { case 0x10ec0260: - snd_hda_codec_write(codec, 0x1a, 0, - AC_VERB_SET_COEF_INDEX, 7); - tmp = snd_hda_codec_read(codec, 0x1a, 0, - AC_VERB_GET_PROC_COEF, 0); - snd_hda_codec_write(codec, 0x1a, 0, - AC_VERB_SET_COEF_INDEX, 7); - snd_hda_codec_write(codec, 0x1a, 0, - AC_VERB_SET_PROC_COEF, - tmp | 0x2010); + alc_update_coefex_idx(codec, 0x1a, 7, 0, 0x2010); break; case 0x10ec0262: case 0x10ec0880: @@ -337,15 +375,7 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type) #if 0 /* XXX: This may cause the silent output on speaker on some machines */ case 0x10ec0267: case 0x10ec0268: - snd_hda_codec_write(codec, 0x20, 0, - AC_VERB_SET_COEF_INDEX, 7); - tmp = snd_hda_codec_read(codec, 0x20, 0, - AC_VERB_GET_PROC_COEF, 0); - snd_hda_codec_write(codec, 0x20, 0, - AC_VERB_SET_COEF_INDEX, 7); - snd_hda_codec_write(codec, 0x20, 0, - AC_VERB_SET_PROC_COEF, - tmp | 0x3000); + alc_update_coef_idx(codec, 7, 0, 0x3000); break; #endif /* XXX */ } @@ -588,190 +618,14 @@ static void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports) } /* - * COEF access helper functions - */ - -static int alc_read_coefex_idx(struct hda_codec *codec, - hda_nid_t nid, - unsigned int coef_idx) -{ - unsigned int val; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, - coef_idx); - val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PROC_COEF, 0); - return val; -} - -#define alc_read_coef_idx(codec, coef_idx) \ - alc_read_coefex_idx(codec, 0x20, coef_idx) - -static void alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx, - unsigned int coef_val) -{ - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, - coef_idx); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PROC_COEF, - coef_val); -} - -#define alc_write_coef_idx(codec, coef_idx, coef_val) \ - alc_write_coefex_idx(codec, 0x20, coef_idx, coef_val) - -/* a special bypass for COEF 0; read the cached value at the second time */ -static unsigned int alc_get_coef0(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - if (!spec->coef0) - spec->coef0 = alc_read_coef_idx(codec, 0); - return spec->coef0; -} - -/* */ -static hda_nid_t get_adc_nid(struct hda_codec *codec, int adc_idx, int imux_idx) -{ - struct hda_gen_spec *spec = codec->spec; - if (spec->dyn_adc_switch) - adc_idx = spec->dyn_adc_idx[imux_idx]; - return spec->adc_nids[adc_idx]; -} - -static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx) -{ - struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->gen.input_mux; - struct nid_path *path; - hda_nid_t nid; - int i, dir, parm; - unsigned int val; - - for (i = 0; i < imux->num_items; i++) { - if (spec->gen.imux_pins[i] == spec->inv_dmic_pin) - break; - } - if (i >= imux->num_items) - return; - - path = snd_hda_get_nid_path(codec, spec->inv_dmic_pin, - get_adc_nid(codec, adc_idx, i)); - val = path->ctls[NID_PATH_MUTE_CTL]; - if (!val) - return; - nid = get_amp_nid_(val); - dir = get_amp_direction_(val); - parm = AC_AMP_SET_RIGHT | - (dir == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT); - - /* flush all cached amps at first */ - snd_hda_codec_flush_cache(codec); - - /* we care only right channel */ - val = snd_hda_codec_amp_read(codec, nid, 1, dir, 0); - if (val & 0x80) /* if already muted, we don't need to touch */ - return; - val |= 0x80; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - parm | val); -} - -/* - * Inverted digital-mic handling - * - * First off, it's a bit tricky. The "Inverted Internal Mic Capture Switch" - * gives the additional mute only to the right channel of the digital mic - * capture stream. This is a workaround for avoiding the almost silence - * by summing the stereo stream from some (known to be ForteMedia) - * digital mic unit. - * - * The logic is to call alc_inv_dmic_sync() after each action (possibly) - * modifying ADC amp. When the mute flag is set, it mutes the R-channel - * without caching so that the cache can still keep the original value. - * The cached value is then restored when the flag is set off or any other - * than d-mic is used as the current input source. - */ -static void alc_inv_dmic_sync(struct hda_codec *codec, bool force) -{ - struct alc_spec *spec = codec->spec; - int src, nums; - - if (!spec->inv_dmic_fixup) - return; - if (!spec->inv_dmic_muted && !force) - return; - nums = spec->gen.dyn_adc_switch ? 1 : spec->gen.num_adc_nids; - for (src = 0; src < nums; src++) { - bool dmic_fixup = false; - - if (spec->inv_dmic_muted && - spec->gen.imux_pins[spec->gen.cur_mux[src]] == spec->inv_dmic_pin) - dmic_fixup = true; - if (!dmic_fixup && !force) - continue; - alc_inv_dmic_sync_adc(codec, src); - } -} - -static void alc_inv_dmic_hook(struct hda_codec *codec, - struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - alc_inv_dmic_sync(codec, false); -} - -static int alc_inv_dmic_sw_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - - ucontrol->value.integer.value[0] = !spec->inv_dmic_muted; - return 0; -} - -static int alc_inv_dmic_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - unsigned int val = !ucontrol->value.integer.value[0]; - - if (val == spec->inv_dmic_muted) - return 0; - spec->inv_dmic_muted = val; - alc_inv_dmic_sync(codec, true); - return 0; -} - -static const struct snd_kcontrol_new alc_inv_dmic_sw = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Inverted Internal Mic Capture Switch", - .info = snd_ctl_boolean_mono_info, - .get = alc_inv_dmic_sw_get, - .put = alc_inv_dmic_sw_put, -}; - -static int alc_add_inv_dmic_mixer(struct hda_codec *codec, hda_nid_t nid) +static void alc_fixup_inv_dmic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &alc_inv_dmic_sw)) - return -ENOMEM; - spec->inv_dmic_fixup = 1; - spec->inv_dmic_muted = 0; - spec->inv_dmic_pin = nid; - spec->gen.cap_sync_hook = alc_inv_dmic_hook; - return 0; -} - -/* typically the digital mic is put at node 0x12 */ -static void alc_fixup_inv_dmic_0x12(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PROBE) - alc_add_inv_dmic_mixer(codec, 0x12); + spec->gen.inv_dmic_split = 1; } @@ -880,7 +734,6 @@ static int alc_resume(struct hda_codec *codec) codec->patch_ops.init(codec); snd_hda_codec_resume_amp(codec); snd_hda_codec_resume_cache(codec); - alc_inv_dmic_sync(codec, true); hda_call_check_power_status(codec, 0x01); return 0; } @@ -1134,7 +987,8 @@ static void alc880_fixup_vol_knob(struct hda_codec *codec, const struct hda_fixup *fix, int action) { if (action == HDA_FIXUP_ACT_PROBE) - snd_hda_jack_detect_enable_callback(codec, 0x21, ALC_DCVOL_EVENT, alc_update_knob_master); + snd_hda_jack_detect_enable_callback(codec, 0x21, + alc_update_knob_master); } static const struct hda_fixup alc880_fixups[] = { @@ -1597,7 +1451,7 @@ static void alc260_fixup_gpio1_toggle(struct hda_codec *codec, spec->gen.detect_hp = 1; spec->gen.automute_speaker = 1; spec->gen.autocfg.hp_pins[0] = 0x0f; /* copy it for automute */ - snd_hda_jack_detect_enable_callback(codec, 0x0f, HDA_GEN_HP_EVENT, + snd_hda_jack_detect_enable_callback(codec, 0x0f, snd_hda_gen_hp_automute); snd_hda_add_verbs(codec, alc_gpio1_init_verbs); } @@ -2222,7 +2076,7 @@ static const struct hda_fixup alc882_fixups[] = { }, [ALC882_FIXUP_INV_DMIC] = { .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_inv_dmic_0x12, + .v.func = alc_fixup_inv_dmic, }, [ALC882_FIXUP_NO_PRIMARY_HP] = { .type = HDA_FIXUP_FUNC, @@ -2473,7 +2327,7 @@ static const struct hda_fixup alc262_fixups[] = { }, [ALC262_FIXUP_INV_DMIC] = { .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_inv_dmic_0x12, + .v.func = alc_fixup_inv_dmic, }, [ALC262_FIXUP_INTEL_BAYLEYBAY] = { .type = HDA_FIXUP_FUNC, @@ -2517,13 +2371,7 @@ static int patch_alc262(struct hda_codec *codec) /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is * under-run */ - { - int tmp; - snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7); - tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0); - snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7); - snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PROC_COEF, tmp | 0x80); - } + alc_update_coefex_idx(codec, 0x1a, 7, 0, 0x80); #endif alc_fix_pll_init(codec, 0x20, 0x0a, 10); @@ -2592,7 +2440,7 @@ enum { static const struct hda_fixup alc268_fixups[] = { [ALC268_FIXUP_INV_DMIC] = { .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_inv_dmic_0x12, + .v.func = alc_fixup_inv_dmic, }, [ALC268_FIXUP_HP_EAPD] = { .type = HDA_FIXUP_VERBS, @@ -2809,14 +2657,7 @@ static void alc286_shutup(struct hda_codec *codec) static void alc269vb_toggle_power_output(struct hda_codec *codec, int power_up) { - int val = alc_read_coef_idx(codec, 0x04); - if (val == -1) - return; - if (power_up) - val |= 1 << 11; - else - val &= ~(1 << 11); - alc_write_coef_idx(codec, 0x04, val); + alc_update_coef_idx(codec, 0x04, 1 << 11, power_up ? (1 << 11) : 0); } static void alc269_shutup(struct hda_codec *codec) @@ -2832,79 +2673,42 @@ static void alc269_shutup(struct hda_codec *codec) snd_hda_shutup_pins(codec); } +static struct coef_fw alc282_coefs[] = { + WRITE_COEF(0x03, 0x0002), /* Power Down Control */ + WRITE_COEF(0x05, 0x0700), /* FIFO and filter clock */ + WRITE_COEF(0x07, 0x0200), /* DMIC control */ + UPDATE_COEF(0x06, 0x00f0, 0), /* Analog clock */ + UPDATE_COEF(0x08, 0xfffc, 0x0c2c), /* JD */ + WRITE_COEF(0x0a, 0xcccc), /* JD offset1 */ + WRITE_COEF(0x0b, 0xcccc), /* JD offset2 */ + WRITE_COEF(0x0e, 0x6e00), /* LDO1/2/3, DAC/ADC */ + UPDATE_COEF(0x0f, 0xf800, 0x1000), /* JD */ + UPDATE_COEF(0x10, 0xfc00, 0x0c00), /* Capless */ + WRITE_COEF(0x6f, 0x0), /* Class D test 4 */ + UPDATE_COEF(0x0c, 0xfe00, 0), /* IO power down directly */ + WRITE_COEF(0x34, 0xa0c0), /* ANC */ + UPDATE_COEF(0x16, 0x0008, 0), /* AGC MUX */ + UPDATE_COEF(0x1d, 0x00e0, 0), /* DAC simple content protection */ + UPDATE_COEF(0x1f, 0x00e0, 0), /* ADC simple content protection */ + WRITE_COEF(0x21, 0x8804), /* DAC ADC Zero Detection */ + WRITE_COEF(0x63, 0x2902), /* PLL */ + WRITE_COEF(0x68, 0xa080), /* capless control 2 */ + WRITE_COEF(0x69, 0x3400), /* capless control 3 */ + WRITE_COEF(0x6a, 0x2f3e), /* capless control 4 */ + WRITE_COEF(0x6b, 0x0), /* capless control 5 */ + UPDATE_COEF(0x6d, 0x0fff, 0x0900), /* class D test 2 */ + WRITE_COEF(0x6e, 0x110a), /* class D test 3 */ + UPDATE_COEF(0x70, 0x00f8, 0x00d8), /* class D test 5 */ + WRITE_COEF(0x71, 0x0014), /* class D test 6 */ + WRITE_COEF(0x72, 0xc2ba), /* classD OCP */ + UPDATE_COEF(0x77, 0x0f80, 0), /* classD pure DC test */ + WRITE_COEF(0x6c, 0xfc06), /* Class D amp control */ + {} +}; + static void alc282_restore_default_value(struct hda_codec *codec) { - int val; - - /* Power Down Control */ - alc_write_coef_idx(codec, 0x03, 0x0002); - /* FIFO and filter clock */ - alc_write_coef_idx(codec, 0x05, 0x0700); - /* DMIC control */ - alc_write_coef_idx(codec, 0x07, 0x0200); - /* Analog clock */ - val = alc_read_coef_idx(codec, 0x06); - alc_write_coef_idx(codec, 0x06, (val & ~0x00f0) | 0x0); - /* JD */ - val = alc_read_coef_idx(codec, 0x08); - alc_write_coef_idx(codec, 0x08, (val & ~0xfffc) | 0x0c2c); - /* JD offset1 */ - alc_write_coef_idx(codec, 0x0a, 0xcccc); - /* JD offset2 */ - alc_write_coef_idx(codec, 0x0b, 0xcccc); - /* LDO1/2/3, DAC/ADC */ - alc_write_coef_idx(codec, 0x0e, 0x6e00); - /* JD */ - val = alc_read_coef_idx(codec, 0x0f); - alc_write_coef_idx(codec, 0x0f, (val & ~0xf800) | 0x1000); - /* Capless */ - val = alc_read_coef_idx(codec, 0x10); - alc_write_coef_idx(codec, 0x10, (val & ~0xfc00) | 0x0c00); - /* Class D test 4 */ - alc_write_coef_idx(codec, 0x6f, 0x0); - /* IO power down directly */ - val = alc_read_coef_idx(codec, 0x0c); - alc_write_coef_idx(codec, 0x0c, (val & ~0xfe00) | 0x0); - /* ANC */ - alc_write_coef_idx(codec, 0x34, 0xa0c0); - /* AGC MUX */ - val = alc_read_coef_idx(codec, 0x16); - alc_write_coef_idx(codec, 0x16, (val & ~0x0008) | 0x0); - /* DAC simple content protection */ - val = alc_read_coef_idx(codec, 0x1d); - alc_write_coef_idx(codec, 0x1d, (val & ~0x00e0) | 0x0); - /* ADC simple content protection */ - val = alc_read_coef_idx(codec, 0x1f); - alc_write_coef_idx(codec, 0x1f, (val & ~0x00e0) | 0x0); - /* DAC ADC Zero Detection */ - alc_write_coef_idx(codec, 0x21, 0x8804); - /* PLL */ - alc_write_coef_idx(codec, 0x63, 0x2902); - /* capless control 2 */ - alc_write_coef_idx(codec, 0x68, 0xa080); - /* capless control 3 */ - alc_write_coef_idx(codec, 0x69, 0x3400); - /* capless control 4 */ - alc_write_coef_idx(codec, 0x6a, 0x2f3e); - /* capless control 5 */ - alc_write_coef_idx(codec, 0x6b, 0x0); - /* class D test 2 */ - val = alc_read_coef_idx(codec, 0x6d); - alc_write_coef_idx(codec, 0x6d, (val & ~0x0fff) | 0x0900); - /* class D test 3 */ - alc_write_coef_idx(codec, 0x6e, 0x110a); - /* class D test 5 */ - val = alc_read_coef_idx(codec, 0x70); - alc_write_coef_idx(codec, 0x70, (val & ~0x00f8) | 0x00d8); - /* class D test 6 */ - alc_write_coef_idx(codec, 0x71, 0x0014); - /* classD OCP */ - alc_write_coef_idx(codec, 0x72, 0xc2ba); - /* classD pure DC test */ - val = alc_read_coef_idx(codec, 0x77); - alc_write_coef_idx(codec, 0x77, (val & ~0x0f80) | 0x0); - /* Class D amp control */ - alc_write_coef_idx(codec, 0x6c, 0xfc06); + alc_process_coef_fw(codec, alc282_coefs); } static void alc282_init(struct hda_codec *codec) @@ -2980,87 +2784,45 @@ static void alc282_shutup(struct hda_codec *codec) alc_write_coef_idx(codec, 0x78, coef78); } +static struct coef_fw alc283_coefs[] = { + WRITE_COEF(0x03, 0x0002), /* Power Down Control */ + WRITE_COEF(0x05, 0x0700), /* FIFO and filter clock */ + WRITE_COEF(0x07, 0x0200), /* DMIC control */ + UPDATE_COEF(0x06, 0x00f0, 0), /* Analog clock */ + UPDATE_COEF(0x08, 0xfffc, 0x0c2c), /* JD */ + WRITE_COEF(0x0a, 0xcccc), /* JD offset1 */ + WRITE_COEF(0x0b, 0xcccc), /* JD offset2 */ + WRITE_COEF(0x0e, 0x6fc0), /* LDO1/2/3, DAC/ADC */ + UPDATE_COEF(0x0f, 0xf800, 0x1000), /* JD */ + UPDATE_COEF(0x10, 0xfc00, 0x0c00), /* Capless */ + WRITE_COEF(0x3a, 0x0), /* Class D test 4 */ + UPDATE_COEF(0x0c, 0xfe00, 0x0), /* IO power down directly */ + WRITE_COEF(0x22, 0xa0c0), /* ANC */ + UPDATE_COEFEX(0x53, 0x01, 0x000f, 0x0008), /* AGC MUX */ + UPDATE_COEF(0x1d, 0x00e0, 0), /* DAC simple content protection */ + UPDATE_COEF(0x1f, 0x00e0, 0), /* ADC simple content protection */ + WRITE_COEF(0x21, 0x8804), /* DAC ADC Zero Detection */ + WRITE_COEF(0x2e, 0x2902), /* PLL */ + WRITE_COEF(0x33, 0xa080), /* capless control 2 */ + WRITE_COEF(0x34, 0x3400), /* capless control 3 */ + WRITE_COEF(0x35, 0x2f3e), /* capless control 4 */ + WRITE_COEF(0x36, 0x0), /* capless control 5 */ + UPDATE_COEF(0x38, 0x0fff, 0x0900), /* class D test 2 */ + WRITE_COEF(0x39, 0x110a), /* class D test 3 */ + UPDATE_COEF(0x3b, 0x00f8, 0x00d8), /* class D test 5 */ + WRITE_COEF(0x3c, 0x0014), /* class D test 6 */ + WRITE_COEF(0x3d, 0xc2ba), /* classD OCP */ + UPDATE_COEF(0x42, 0x0f80, 0x0), /* classD pure DC test */ + WRITE_COEF(0x49, 0x0), /* test mode */ + UPDATE_COEF(0x40, 0xf800, 0x9800), /* Class D DC enable */ + UPDATE_COEF(0x42, 0xf000, 0x2000), /* DC offset */ + WRITE_COEF(0x37, 0xfc06), /* Class D amp control */ + {} +}; + static void alc283_restore_default_value(struct hda_codec *codec) { - int val; - - /* Power Down Control */ - alc_write_coef_idx(codec, 0x03, 0x0002); - /* FIFO and filter clock */ - alc_write_coef_idx(codec, 0x05, 0x0700); - /* DMIC control */ - alc_write_coef_idx(codec, 0x07, 0x0200); - /* Analog clock */ - val = alc_read_coef_idx(codec, 0x06); - alc_write_coef_idx(codec, 0x06, (val & ~0x00f0) | 0x0); - /* JD */ - val = alc_read_coef_idx(codec, 0x08); - alc_write_coef_idx(codec, 0x08, (val & ~0xfffc) | 0x0c2c); - /* JD offset1 */ - alc_write_coef_idx(codec, 0x0a, 0xcccc); - /* JD offset2 */ - alc_write_coef_idx(codec, 0x0b, 0xcccc); - /* LDO1/2/3, DAC/ADC */ - alc_write_coef_idx(codec, 0x0e, 0x6fc0); - /* JD */ - val = alc_read_coef_idx(codec, 0x0f); - alc_write_coef_idx(codec, 0x0f, (val & ~0xf800) | 0x1000); - /* Capless */ - val = alc_read_coef_idx(codec, 0x10); - alc_write_coef_idx(codec, 0x10, (val & ~0xfc00) | 0x0c00); - /* Class D test 4 */ - alc_write_coef_idx(codec, 0x3a, 0x0); - /* IO power down directly */ - val = alc_read_coef_idx(codec, 0x0c); - alc_write_coef_idx(codec, 0x0c, (val & ~0xfe00) | 0x0); - /* ANC */ - alc_write_coef_idx(codec, 0x22, 0xa0c0); - /* AGC MUX */ - val = alc_read_coefex_idx(codec, 0x53, 0x01); - alc_write_coefex_idx(codec, 0x53, 0x01, (val & ~0x000f) | 0x0008); - /* DAC simple content protection */ - val = alc_read_coef_idx(codec, 0x1d); - alc_write_coef_idx(codec, 0x1d, (val & ~0x00e0) | 0x0); - /* ADC simple content protection */ - val = alc_read_coef_idx(codec, 0x1f); - alc_write_coef_idx(codec, 0x1f, (val & ~0x00e0) | 0x0); - /* DAC ADC Zero Detection */ - alc_write_coef_idx(codec, 0x21, 0x8804); - /* PLL */ - alc_write_coef_idx(codec, 0x2e, 0x2902); - /* capless control 2 */ - alc_write_coef_idx(codec, 0x33, 0xa080); - /* capless control 3 */ - alc_write_coef_idx(codec, 0x34, 0x3400); - /* capless control 4 */ - alc_write_coef_idx(codec, 0x35, 0x2f3e); - /* capless control 5 */ - alc_write_coef_idx(codec, 0x36, 0x0); - /* class D test 2 */ - val = alc_read_coef_idx(codec, 0x38); - alc_write_coef_idx(codec, 0x38, (val & ~0x0fff) | 0x0900); - /* class D test 3 */ - alc_write_coef_idx(codec, 0x39, 0x110a); - /* class D test 5 */ - val = alc_read_coef_idx(codec, 0x3b); - alc_write_coef_idx(codec, 0x3b, (val & ~0x00f8) | 0x00d8); - /* class D test 6 */ - alc_write_coef_idx(codec, 0x3c, 0x0014); - /* classD OCP */ - alc_write_coef_idx(codec, 0x3d, 0xc2ba); - /* classD pure DC test */ - val = alc_read_coef_idx(codec, 0x42); - alc_write_coef_idx(codec, 0x42, (val & ~0x0f80) | 0x0); - /* test mode */ - alc_write_coef_idx(codec, 0x49, 0x0); - /* Class D DC enable */ - val = alc_read_coef_idx(codec, 0x40); - alc_write_coef_idx(codec, 0x40, (val & ~0xf800) | 0x9800); - /* DC offset */ - val = alc_read_coef_idx(codec, 0x42); - alc_write_coef_idx(codec, 0x42, (val & ~0xf000) | 0x2000); - /* Class D amp control */ - alc_write_coef_idx(codec, 0x37, 0xfc06); + alc_process_coef_fw(codec, alc283_coefs); } static void alc283_init(struct hda_codec *codec) @@ -3068,7 +2830,6 @@ static void alc283_init(struct hda_codec *codec) struct alc_spec *spec = codec->spec; hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; bool hp_pin_sense; - int val; if (!spec->gen.autocfg.hp_outs) { if (spec->gen.autocfg.line_out_type == AC_JACK_HP_OUT) @@ -3098,8 +2859,7 @@ static void alc283_init(struct hda_codec *codec) msleep(85); /* Index 0x46 Combo jack auto switch control 2 */ /* 3k pull low control for Headset jack. */ - val = alc_read_coef_idx(codec, 0x46); - alc_write_coef_idx(codec, 0x46, val & ~(3 << 12)); + alc_update_coef_idx(codec, 0x46, 3 << 12, 0); /* Headphone capless set to normal mode */ alc_write_coef_idx(codec, 0x43, 0x9614); } @@ -3109,7 +2869,6 @@ static void alc283_shutup(struct hda_codec *codec) struct alc_spec *spec = codec->spec; hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; bool hp_pin_sense; - int val; if (!spec->gen.autocfg.hp_outs) { if (spec->gen.autocfg.line_out_type == AC_JACK_HP_OUT) @@ -3134,8 +2893,7 @@ static void alc283_shutup(struct hda_codec *codec) snd_hda_codec_write(codec, hp_pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - val = alc_read_coef_idx(codec, 0x46); - alc_write_coef_idx(codec, 0x46, val | (3 << 12)); + alc_update_coef_idx(codec, 0x46, 0, 3 << 12); if (hp_pin_sense) msleep(100); @@ -3268,7 +3026,6 @@ static int alc269_resume(struct hda_codec *codec) snd_hda_codec_resume_amp(codec); snd_hda_codec_resume_cache(codec); - alc_inv_dmic_sync(codec, true); hda_call_check_power_status(codec, 0x01); /* on some machine, the BIOS will clear the codec gpio data when enter @@ -3298,12 +3055,8 @@ static void alc269_fixup_pincfg_no_hp_to_lineout(struct hda_codec *codec, static void alc269_fixup_hweq(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - int coef; - - if (action != HDA_FIXUP_ACT_INIT) - return; - coef = alc_read_coef_idx(codec, 0x1e); - alc_write_coef_idx(codec, 0x1e, coef | 0x80); + if (action == HDA_FIXUP_ACT_INIT) + alc_update_coef_idx(codec, 0x1e, 0, 0x80); } static void alc269_fixup_headset_mic(struct hda_codec *codec, @@ -3351,32 +3104,21 @@ static void alc269_fixup_pcm_44k(struct hda_codec *codec, static void alc269_fixup_stereo_dmic(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - int coef; - - if (action != HDA_FIXUP_ACT_INIT) - return; /* The digital-mic unit sends PDM (differential signal) instead of * the standard PCM, thus you can't record a valid mono stream as is. * Below is a workaround specific to ALC269 to control the dmic * signal source as mono. */ - coef = alc_read_coef_idx(codec, 0x07); - alc_write_coef_idx(codec, 0x07, coef | 0x80); + if (action == HDA_FIXUP_ACT_INIT) + alc_update_coef_idx(codec, 0x07, 0, 0x80); } static void alc269_quanta_automute(struct hda_codec *codec) { snd_hda_gen_update_outputs(codec); - snd_hda_codec_write(codec, 0x20, 0, - AC_VERB_SET_COEF_INDEX, 0x0c); - snd_hda_codec_write(codec, 0x20, 0, - AC_VERB_SET_PROC_COEF, 0x680); - - snd_hda_codec_write(codec, 0x20, 0, - AC_VERB_SET_COEF_INDEX, 0x0c); - snd_hda_codec_write(codec, 0x20, 0, - AC_VERB_SET_PROC_COEF, 0x480); + alc_write_coef_idx(codec, 0x0c, 0x680); + alc_write_coef_idx(codec, 0x0c, 0x480); } static void alc269_fixup_quanta_mute(struct hda_codec *codec, @@ -3389,7 +3131,7 @@ static void alc269_fixup_quanta_mute(struct hda_codec *codec, } static void alc269_x101_hp_automute_hook(struct hda_codec *codec, - struct hda_jack_tbl *jack) + struct hda_jack_callback *jack) { struct alc_spec *spec = codec->spec; int vref; @@ -3622,61 +3364,62 @@ static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec, static void alc_headset_mode_unplugged(struct hda_codec *codec) { - int val; + static struct coef_fw coef0255[] = { + WRITE_COEF(0x1b, 0x0c0b), /* LDO and MISC control */ + WRITE_COEF(0x45, 0xd089), /* UAJ function set to menual mode */ + UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), /* Direct Drive HP Amp control(Set to verb control)*/ + WRITE_COEF(0x06, 0x6104), /* Set MIC2 Vref gate with HP */ + WRITE_COEFEX(0x57, 0x03, 0x8aa6), /* Direct Drive HP Amp control */ + {} + }; + static struct coef_fw coef0233[] = { + WRITE_COEF(0x1b, 0x0c0b), + WRITE_COEF(0x45, 0xc429), + UPDATE_COEF(0x35, 0x4000, 0), + WRITE_COEF(0x06, 0x2104), + WRITE_COEF(0x1a, 0x0001), + WRITE_COEF(0x26, 0x0004), + WRITE_COEF(0x32, 0x42a3), + {} + }; + static struct coef_fw coef0292[] = { + WRITE_COEF(0x76, 0x000e), + WRITE_COEF(0x6c, 0x2400), + WRITE_COEF(0x18, 0x7308), + WRITE_COEF(0x6b, 0xc429), + {} + }; + static struct coef_fw coef0293[] = { + UPDATE_COEF(0x10, 7<<8, 6<<8), /* SET Line1 JD to 0 */ + UPDATE_COEFEX(0x57, 0x05, 1<<15|1<<13, 0x0), /* SET charge pump by verb */ + UPDATE_COEFEX(0x57, 0x03, 1<<10, 1<<10), /* SET EN_OSW to 1 */ + UPDATE_COEF(0x1a, 1<<3, 1<<3), /* Combo JD gating with LINE1-VREFO */ + WRITE_COEF(0x45, 0xc429), /* Set to TRS type */ + UPDATE_COEF(0x4a, 0x000f, 0x000e), /* Combo Jack auto detect */ + {} + }; + static struct coef_fw coef0668[] = { + WRITE_COEF(0x15, 0x0d40), + WRITE_COEF(0xb7, 0x802b), + {} + }; switch (codec->vendor_id) { case 0x10ec0255: - /* LDO and MISC control */ - alc_write_coef_idx(codec, 0x1b, 0x0c0b); - /* UAJ function set to menual mode */ - alc_write_coef_idx(codec, 0x45, 0xd089); - /* Direct Drive HP Amp control(Set to verb control)*/ - val = alc_read_coefex_idx(codec, 0x57, 0x05); - alc_write_coefex_idx(codec, 0x57, 0x05, val & ~(1<<14)); - /* Set MIC2 Vref gate with HP */ - alc_write_coef_idx(codec, 0x06, 0x6104); - /* Direct Drive HP Amp control */ - alc_write_coefex_idx(codec, 0x57, 0x03, 0x8aa6); + alc_process_coef_fw(codec, coef0255); break; case 0x10ec0233: case 0x10ec0283: - alc_write_coef_idx(codec, 0x1b, 0x0c0b); - alc_write_coef_idx(codec, 0x45, 0xc429); - val = alc_read_coef_idx(codec, 0x35); - alc_write_coef_idx(codec, 0x35, val & 0xbfff); - alc_write_coef_idx(codec, 0x06, 0x2104); - alc_write_coef_idx(codec, 0x1a, 0x0001); - alc_write_coef_idx(codec, 0x26, 0x0004); - alc_write_coef_idx(codec, 0x32, 0x42a3); + alc_process_coef_fw(codec, coef0233); break; case 0x10ec0292: - alc_write_coef_idx(codec, 0x76, 0x000e); - alc_write_coef_idx(codec, 0x6c, 0x2400); - alc_write_coef_idx(codec, 0x18, 0x7308); - alc_write_coef_idx(codec, 0x6b, 0xc429); + alc_process_coef_fw(codec, coef0292); break; case 0x10ec0293: - /* SET Line1 JD to 0 */ - val = alc_read_coef_idx(codec, 0x10); - alc_write_coef_idx(codec, 0x10, (val & ~(7<<8)) | 6<<8); - /* SET charge pump by verb */ - val = alc_read_coefex_idx(codec, 0x57, 0x05); - alc_write_coefex_idx(codec, 0x57, 0x05, (val & ~(1<<15|1<<13)) | 0x0); - /* SET EN_OSW to 1 */ - val = alc_read_coefex_idx(codec, 0x57, 0x03); - alc_write_coefex_idx(codec, 0x57, 0x03, (val & ~(1<<10)) | (1<<10) ); - /* Combo JD gating with LINE1-VREFO */ - val = alc_read_coef_idx(codec, 0x1a); - alc_write_coef_idx(codec, 0x1a, (val & ~(1<<3)) | (1<<3)); - /* Set to TRS type */ - alc_write_coef_idx(codec, 0x45, 0xc429); - /* Combo Jack auto detect */ - val = alc_read_coef_idx(codec, 0x4a); - alc_write_coef_idx(codec, 0x4a, (val & 0xfff0) | 0x000e); + alc_process_coef_fw(codec, coef0293); break; case 0x10ec0668: - alc_write_coef_idx(codec, 0x15, 0x0d40); - alc_write_coef_idx(codec, 0xb7, 0x802b); + alc_process_coef_fw(codec, coef0668); break; } codec_dbg(codec, "Headset jack set to unplugged mode.\n"); @@ -3686,55 +3429,65 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec) static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, hda_nid_t mic_pin) { - int val; + static struct coef_fw coef0255[] = { + WRITE_COEFEX(0x57, 0x03, 0x8aa6), + WRITE_COEF(0x06, 0x6100), /* Set MIC2 Vref gate to normal */ + {} + }; + static struct coef_fw coef0233[] = { + UPDATE_COEF(0x35, 0, 1<<14), + WRITE_COEF(0x06, 0x2100), + WRITE_COEF(0x1a, 0x0021), + WRITE_COEF(0x26, 0x008c), + {} + }; + static struct coef_fw coef0292[] = { + WRITE_COEF(0x19, 0xa208), + WRITE_COEF(0x2e, 0xacf0), + {} + }; + static struct coef_fw coef0293[] = { + UPDATE_COEFEX(0x57, 0x05, 0, 1<<15|1<<13), /* SET charge pump by verb */ + UPDATE_COEFEX(0x57, 0x03, 1<<10, 0), /* SET EN_OSW to 0 */ + UPDATE_COEF(0x1a, 1<<3, 0), /* Combo JD gating without LINE1-VREFO */ + {} + }; + static struct coef_fw coef0688[] = { + WRITE_COEF(0xb7, 0x802b), + WRITE_COEF(0xb5, 0x1040), + UPDATE_COEF(0xc3, 0, 1<<12), + {} + }; switch (codec->vendor_id) { case 0x10ec0255: alc_write_coef_idx(codec, 0x45, 0xc489); snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_write_coefex_idx(codec, 0x57, 0x03, 0x8aa6); - /* Set MIC2 Vref gate to normal */ - alc_write_coef_idx(codec, 0x06, 0x6100); + alc_process_coef_fw(codec, coef0255); snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); break; case 0x10ec0233: case 0x10ec0283: alc_write_coef_idx(codec, 0x45, 0xc429); snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - val = alc_read_coef_idx(codec, 0x35); - alc_write_coef_idx(codec, 0x35, val | 1<<14); - alc_write_coef_idx(codec, 0x06, 0x2100); - alc_write_coef_idx(codec, 0x1a, 0x0021); - alc_write_coef_idx(codec, 0x26, 0x008c); + alc_process_coef_fw(codec, coef0233); snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); break; case 0x10ec0292: snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_write_coef_idx(codec, 0x19, 0xa208); - alc_write_coef_idx(codec, 0x2e, 0xacf0); + alc_process_coef_fw(codec, coef0292); break; case 0x10ec0293: /* Set to TRS mode */ alc_write_coef_idx(codec, 0x45, 0xc429); snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - /* SET charge pump by verb */ - val = alc_read_coefex_idx(codec, 0x57, 0x05); - alc_write_coefex_idx(codec, 0x57, 0x05, (val & ~(1<<15|1<<13)) | (1<<15|1<<13)); - /* SET EN_OSW to 0 */ - val = alc_read_coefex_idx(codec, 0x57, 0x03); - alc_write_coefex_idx(codec, 0x57, 0x03, (val & ~(1<<10)) | 0x0); - /* Combo JD gating without LINE1-VREFO */ - val = alc_read_coef_idx(codec, 0x1a); - alc_write_coef_idx(codec, 0x1a, (val & ~(1<<3)) | 0x0); + alc_process_coef_fw(codec, coef0293); snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); break; case 0x10ec0668: alc_write_coef_idx(codec, 0x11, 0x0001); snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_write_coef_idx(codec, 0xb7, 0x802b); - alc_write_coef_idx(codec, 0xb5, 0x1040); - val = alc_read_coef_idx(codec, 0xc3); - alc_write_coef_idx(codec, 0xc3, val | 1<<12); + alc_process_coef_fw(codec, coef0688); snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); break; } @@ -3743,40 +3496,54 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, static void alc_headset_mode_default(struct hda_codec *codec) { - int val; + static struct coef_fw coef0255[] = { + WRITE_COEF(0x45, 0xc089), + WRITE_COEF(0x45, 0xc489), + WRITE_COEFEX(0x57, 0x03, 0x8ea6), + WRITE_COEF(0x49, 0x0049), + {} + }; + static struct coef_fw coef0233[] = { + WRITE_COEF(0x06, 0x2100), + WRITE_COEF(0x32, 0x4ea3), + {} + }; + static struct coef_fw coef0292[] = { + WRITE_COEF(0x76, 0x000e), + WRITE_COEF(0x6c, 0x2400), + WRITE_COEF(0x6b, 0xc429), + WRITE_COEF(0x18, 0x7308), + {} + }; + static struct coef_fw coef0293[] = { + UPDATE_COEF(0x4a, 0x000f, 0x000e), /* Combo Jack auto detect */ + WRITE_COEF(0x45, 0xC429), /* Set to TRS type */ + UPDATE_COEF(0x1a, 1<<3, 0), /* Combo JD gating without LINE1-VREFO */ + {} + }; + static struct coef_fw coef0688[] = { + WRITE_COEF(0x11, 0x0041), + WRITE_COEF(0x15, 0x0d40), + WRITE_COEF(0xb7, 0x802b), + {} + }; switch (codec->vendor_id) { case 0x10ec0255: - alc_write_coef_idx(codec, 0x45, 0xc089); - alc_write_coef_idx(codec, 0x45, 0xc489); - alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6); - alc_write_coef_idx(codec, 0x49, 0x0049); + alc_process_coef_fw(codec, coef0255); break; case 0x10ec0233: case 0x10ec0283: - alc_write_coef_idx(codec, 0x06, 0x2100); - alc_write_coef_idx(codec, 0x32, 0x4ea3); + alc_process_coef_fw(codec, coef0233); break; case 0x10ec0292: - alc_write_coef_idx(codec, 0x76, 0x000e); - alc_write_coef_idx(codec, 0x6c, 0x2400); - alc_write_coef_idx(codec, 0x6b, 0xc429); - alc_write_coef_idx(codec, 0x18, 0x7308); + alc_process_coef_fw(codec, coef0292); break; case 0x10ec0293: - /* Combo Jack auto detect */ - val = alc_read_coef_idx(codec, 0x4a); - alc_write_coef_idx(codec, 0x4a, (val & 0xfff0) | 0x000e); - /* Set to TRS type */ - alc_write_coef_idx(codec, 0x45, 0xC429); - /* Combo JD gating without LINE1-VREFO */ - val = alc_read_coef_idx(codec, 0x1a); - alc_write_coef_idx(codec, 0x1a, (val & ~(1<<3)) | 0x0); + alc_process_coef_fw(codec, coef0293); break; case 0x10ec0668: - alc_write_coef_idx(codec, 0x11, 0x0041); - alc_write_coef_idx(codec, 0x15, 0x0d40); - alc_write_coef_idx(codec, 0xb7, 0x802b); + alc_process_coef_fw(codec, coef0688); break; } codec_dbg(codec, "Headset jack set to headphone (default) mode.\n"); @@ -3785,37 +3552,52 @@ static void alc_headset_mode_default(struct hda_codec *codec) /* Iphone type */ static void alc_headset_mode_ctia(struct hda_codec *codec) { - int val; + static struct coef_fw coef0255[] = { + WRITE_COEF(0x45, 0xd489), /* Set to CTIA type */ + WRITE_COEF(0x1b, 0x0c2b), + WRITE_COEFEX(0x57, 0x03, 0x8ea6), + {} + }; + static struct coef_fw coef0233[] = { + WRITE_COEF(0x45, 0xd429), + WRITE_COEF(0x1b, 0x0c2b), + WRITE_COEF(0x32, 0x4ea3), + {} + }; + static struct coef_fw coef0292[] = { + WRITE_COEF(0x6b, 0xd429), + WRITE_COEF(0x76, 0x0008), + WRITE_COEF(0x18, 0x7388), + {} + }; + static struct coef_fw coef0293[] = { + WRITE_COEF(0x45, 0xd429), /* Set to ctia type */ + UPDATE_COEF(0x10, 7<<8, 7<<8), /* SET Line1 JD to 1 */ + {} + }; + static struct coef_fw coef0688[] = { + WRITE_COEF(0x11, 0x0001), + WRITE_COEF(0x15, 0x0d60), + WRITE_COEF(0xc3, 0x0000), + {} + }; switch (codec->vendor_id) { case 0x10ec0255: - /* Set to CTIA type */ - alc_write_coef_idx(codec, 0x45, 0xd489); - alc_write_coef_idx(codec, 0x1b, 0x0c2b); - alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6); + alc_process_coef_fw(codec, coef0255); break; case 0x10ec0233: case 0x10ec0283: - alc_write_coef_idx(codec, 0x45, 0xd429); - alc_write_coef_idx(codec, 0x1b, 0x0c2b); - alc_write_coef_idx(codec, 0x32, 0x4ea3); + alc_process_coef_fw(codec, coef0233); break; case 0x10ec0292: - alc_write_coef_idx(codec, 0x6b, 0xd429); - alc_write_coef_idx(codec, 0x76, 0x0008); - alc_write_coef_idx(codec, 0x18, 0x7388); + alc_process_coef_fw(codec, coef0292); break; case 0x10ec0293: - /* Set to ctia type */ - alc_write_coef_idx(codec, 0x45, 0xd429); - /* SET Line1 JD to 1 */ - val = alc_read_coef_idx(codec, 0x10); - alc_write_coef_idx(codec, 0x10, (val & ~(7<<8)) | 7<<8); + alc_process_coef_fw(codec, coef0293); break; case 0x10ec0668: - alc_write_coef_idx(codec, 0x11, 0x0001); - alc_write_coef_idx(codec, 0x15, 0x0d60); - alc_write_coef_idx(codec, 0xc3, 0x0000); + alc_process_coef_fw(codec, coef0688); break; } codec_dbg(codec, "Headset jack set to iPhone-style headset mode.\n"); @@ -3824,37 +3606,52 @@ static void alc_headset_mode_ctia(struct hda_codec *codec) /* Nokia type */ static void alc_headset_mode_omtp(struct hda_codec *codec) { - int val; + static struct coef_fw coef0255[] = { + WRITE_COEF(0x45, 0xe489), /* Set to OMTP Type */ + WRITE_COEF(0x1b, 0x0c2b), + WRITE_COEFEX(0x57, 0x03, 0x8ea6), + {} + }; + static struct coef_fw coef0233[] = { + WRITE_COEF(0x45, 0xe429), + WRITE_COEF(0x1b, 0x0c2b), + WRITE_COEF(0x32, 0x4ea3), + {} + }; + static struct coef_fw coef0292[] = { + WRITE_COEF(0x6b, 0xe429), + WRITE_COEF(0x76, 0x0008), + WRITE_COEF(0x18, 0x7388), + {} + }; + static struct coef_fw coef0293[] = { + WRITE_COEF(0x45, 0xe429), /* Set to omtp type */ + UPDATE_COEF(0x10, 7<<8, 7<<8), /* SET Line1 JD to 1 */ + {} + }; + static struct coef_fw coef0688[] = { + WRITE_COEF(0x11, 0x0001), + WRITE_COEF(0x15, 0x0d50), + WRITE_COEF(0xc3, 0x0000), + {} + }; switch (codec->vendor_id) { case 0x10ec0255: - /* Set to OMTP Type */ - alc_write_coef_idx(codec, 0x45, 0xe489); - alc_write_coef_idx(codec, 0x1b, 0x0c2b); - alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6); + alc_process_coef_fw(codec, coef0255); break; case 0x10ec0233: case 0x10ec0283: - alc_write_coef_idx(codec, 0x45, 0xe429); - alc_write_coef_idx(codec, 0x1b, 0x0c2b); - alc_write_coef_idx(codec, 0x32, 0x4ea3); + alc_process_coef_fw(codec, coef0233); break; case 0x10ec0292: - alc_write_coef_idx(codec, 0x6b, 0xe429); - alc_write_coef_idx(codec, 0x76, 0x0008); - alc_write_coef_idx(codec, 0x18, 0x7388); + alc_process_coef_fw(codec, coef0292); break; case 0x10ec0293: - /* Set to omtp type */ - alc_write_coef_idx(codec, 0x45, 0xe429); - /* SET Line1 JD to 1 */ - val = alc_read_coef_idx(codec, 0x10); - alc_write_coef_idx(codec, 0x10, (val & ~(7<<8)) | 7<<8); + alc_process_coef_fw(codec, coef0293); break; case 0x10ec0668: - alc_write_coef_idx(codec, 0x11, 0x0001); - alc_write_coef_idx(codec, 0x15, 0x0d50); - alc_write_coef_idx(codec, 0xc3, 0x0000); + alc_process_coef_fw(codec, coef0688); break; } codec_dbg(codec, "Headset jack set to Nokia-style headset mode.\n"); @@ -3865,13 +3662,28 @@ static void alc_determine_headset_type(struct hda_codec *codec) int val; bool is_ctia = false; struct alc_spec *spec = codec->spec; + static struct coef_fw coef0255[] = { + WRITE_COEF(0x45, 0xd089), /* combo jack auto switch control(Check type)*/ + WRITE_COEF(0x49, 0x0149), /* combo jack auto switch control(Vref + conteol) */ + {} + }; + static struct coef_fw coef0293[] = { + UPDATE_COEF(0x4a, 0x000f, 0x0008), /* Combo Jack auto detect */ + WRITE_COEF(0x45, 0xD429), /* Set to ctia type */ + {} + }; + static struct coef_fw coef0688[] = { + WRITE_COEF(0x11, 0x0001), + WRITE_COEF(0xb7, 0x802b), + WRITE_COEF(0x15, 0x0d60), + WRITE_COEF(0xc3, 0x0c00), + {} + }; switch (codec->vendor_id) { case 0x10ec0255: - /* combo jack auto switch control(Check type)*/ - alc_write_coef_idx(codec, 0x45, 0xd089); - /* combo jack auto switch control(Vref conteol) */ - alc_write_coef_idx(codec, 0x49, 0x0149); + alc_process_coef_fw(codec, coef0255); msleep(300); val = alc_read_coef_idx(codec, 0x46); is_ctia = (val & 0x0070) == 0x0070; @@ -3890,20 +3702,13 @@ static void alc_determine_headset_type(struct hda_codec *codec) is_ctia = (val & 0x001c) == 0x001c; break; case 0x10ec0293: - /* Combo Jack auto detect */ - val = alc_read_coef_idx(codec, 0x4a); - alc_write_coef_idx(codec, 0x4a, (val & 0xfff0) | 0x0008); - /* Set to ctia type */ - alc_write_coef_idx(codec, 0x45, 0xD429); + alc_process_coef_fw(codec, coef0293); msleep(300); val = alc_read_coef_idx(codec, 0x46); is_ctia = (val & 0x0070) == 0x0070; break; case 0x10ec0668: - alc_write_coef_idx(codec, 0x11, 0x0001); - alc_write_coef_idx(codec, 0xb7, 0x802b); - alc_write_coef_idx(codec, 0x15, 0x0d60); - alc_write_coef_idx(codec, 0xc3, 0x0c00); + alc_process_coef_fw(codec, coef0688); msleep(300); val = alc_read_coef_idx(codec, 0xbe); is_ctia = (val & 0x1c02) == 0x1c02; @@ -3980,7 +3785,8 @@ static void alc_update_headset_mode_hook(struct hda_codec *codec, alc_update_headset_mode(codec); } -static void alc_update_headset_jack_cb(struct hda_codec *codec, struct hda_jack_tbl *jack) +static void alc_update_headset_jack_cb(struct hda_codec *codec, + struct hda_jack_callback *jack) { struct alc_spec *spec = codec->spec; spec->current_headset_type = ALC_HEADSET_TYPE_UNKNOWN; @@ -4039,11 +3845,15 @@ static void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec, static void alc255_set_default_jack_type(struct hda_codec *codec) { /* Set to iphone type */ - alc_write_coef_idx(codec, 0x1b, 0x880b); - alc_write_coef_idx(codec, 0x45, 0xd089); - alc_write_coef_idx(codec, 0x1b, 0x080b); - alc_write_coef_idx(codec, 0x46, 0x0004); - alc_write_coef_idx(codec, 0x1b, 0x0c0b); + static struct coef_fw fw[] = { + WRITE_COEF(0x1b, 0x880b), + WRITE_COEF(0x45, 0xd089), + WRITE_COEF(0x1b, 0x080b), + WRITE_COEF(0x46, 0x0004), + WRITE_COEF(0x1b, 0x0c0b), + {} + }; + alc_process_coef_fw(codec, fw); msleep(30); } @@ -4138,10 +3948,8 @@ static void alc_fixup_headset_mode_alc668(struct hda_codec *codec, const struct hda_fixup *fix, int action) { if (action == HDA_FIXUP_ACT_PRE_PROBE) { - int val; alc_write_coef_idx(codec, 0xc4, 0x8000); - val = alc_read_coef_idx(codec, 0xc2); - alc_write_coef_idx(codec, 0xc2, val & 0xfe); + alc_update_coef_idx(codec, 0xc2, ~0xfe, 0); snd_hda_set_pin_ctl_cache(codec, 0x18, 0); } alc_fixup_headset_mode(codec, fix, action); @@ -4218,7 +4026,7 @@ static void alc269_fixup_limit_int_mic_boost(struct hda_codec *codec, } static void alc283_hp_automute_hook(struct hda_codec *codec, - struct hda_jack_tbl *jack) + struct hda_jack_callback *jack) { struct alc_spec *spec = codec->spec; int vref; @@ -4237,7 +4045,6 @@ static void alc283_fixup_chromebook(struct hda_codec *codec, const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - int val; switch (action) { case HDA_FIXUP_ACT_PRE_PROBE: @@ -4248,11 +4055,9 @@ static void alc283_fixup_chromebook(struct hda_codec *codec, case HDA_FIXUP_ACT_INIT: /* MIC2-VREF control */ /* Set to manual mode */ - val = alc_read_coef_idx(codec, 0x06); - alc_write_coef_idx(codec, 0x06, val & ~0x000c); + alc_update_coef_idx(codec, 0x06, 0x000c, 0); /* Enable Line1 input control by verb */ - val = alc_read_coef_idx(codec, 0x1a); - alc_write_coef_idx(codec, 0x1a, val | (1 << 4)); + alc_update_coef_idx(codec, 0x1a, 0, 1 << 4); break; } } @@ -4261,7 +4066,6 @@ static void alc283_fixup_sense_combo_jack(struct hda_codec *codec, const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - int val; switch (action) { case HDA_FIXUP_ACT_PRE_PROBE: @@ -4270,8 +4074,7 @@ static void alc283_fixup_sense_combo_jack(struct hda_codec *codec, case HDA_FIXUP_ACT_INIT: /* MIC2-VREF control */ /* Set to manual mode */ - val = alc_read_coef_idx(codec, 0x06); - alc_write_coef_idx(codec, 0x06, val & ~0x000c); + alc_update_coef_idx(codec, 0x06, 0x000c, 0); break; } } @@ -4309,7 +4112,6 @@ static void alc282_fixup_asus_tx300(struct hda_codec *codec, spec->gen.auto_mute_via_amp = 1; spec->gen.automute_hook = asus_tx300_automute; snd_hda_jack_detect_enable_callback(codec, 0x1b, - HDA_GEN_HP_EVENT, snd_hda_gen_hp_automute); break; case HDA_FIXUP_ACT_BUILD: @@ -4576,7 +4378,7 @@ static const struct hda_fixup alc269_fixups[] = { }, [ALC269_FIXUP_INV_DMIC] = { .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_inv_dmic_0x12, + .v.func = alc_fixup_inv_dmic, }, [ALC269_FIXUP_NO_SHUTUP] = { .type = HDA_FIXUP_FUNC, @@ -4887,122 +4689,45 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x0775, "Acer Aspire E1-572", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572), SND_PCI_QUIRK(0x1025, 0x079b, "Acer Aspire V5-573G", ALC282_FIXUP_ASPIRE_V5_PINS), SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z), - SND_PCI_QUIRK(0x1028, 0x05bd, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05be, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05c4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05c5, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05c6, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05c7, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05c8, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05c9, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05ca, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05cb, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05cc, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05cd, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05da, "Dell Vostro 5460", ALC290_FIXUP_SUBWOOFER), - SND_PCI_QUIRK(0x1028, 0x05de, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05e0, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05e9, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05ea, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05eb, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05ec, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05ed, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05ee, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05f3, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05f4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05f5, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05f6, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05f8, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05f9, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05fb, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0606, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0608, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0609, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0610, "Dell", ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED), - SND_PCI_QUIRK(0x1028, 0x0613, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0614, "Dell Inspiron 3135", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0615, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK), SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK), SND_PCI_QUIRK(0x1028, 0x061f, "Dell", ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED), SND_PCI_QUIRK(0x1028, 0x0638, "Dell Inspiron 5439", ALC290_FIXUP_MONO_SPEAKERS_HSJACK), - SND_PCI_QUIRK(0x1028, 0x063f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x064a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x064b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0668, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0669, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0684, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x15cc, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x15cd, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x1973, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x1983, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x218b, "HP", ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED), /* ALC282 */ - SND_PCI_QUIRK(0x103c, 0x21f8, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x21f9, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x220d, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x220e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x220f, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x2210, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2211, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2212, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2213, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x2214, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2234, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2235, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), SND_PCI_QUIRK(0x103c, 0x2236, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), SND_PCI_QUIRK(0x103c, 0x2237, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), SND_PCI_QUIRK(0x103c, 0x2238, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), SND_PCI_QUIRK(0x103c, 0x2239, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2246, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2247, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2248, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2249, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x224a, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), SND_PCI_QUIRK(0x103c, 0x224b, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x224c, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x224d, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2266, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2267, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x2268, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2269, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x226a, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x226b, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x226c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x226d, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x226e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x226f, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x227a, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x227b, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x229e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22a0, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x22b2, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x22b7, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x22bf, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22c0, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22c1, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22c2, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22cd, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22ce, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x22cf, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22d0, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22da, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x22db, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), SND_PCI_QUIRK(0x103c, 0x22dc, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), SND_PCI_QUIRK(0x103c, 0x22fb, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), SND_PCI_QUIRK(0x103c, 0x8004, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), /* ALC290 */ SND_PCI_QUIRK(0x103c, 0x221b, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x221c, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x221d, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2220, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), SND_PCI_QUIRK(0x103c, 0x2221, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2222, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2223, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2224, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), SND_PCI_QUIRK(0x103c, 0x2225, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), SND_PCI_QUIRK(0x103c, 0x2246, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), SND_PCI_QUIRK(0x103c, 0x2247, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), @@ -5017,8 +4742,6 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x2259, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), SND_PCI_QUIRK(0x103c, 0x225a, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), SND_PCI_QUIRK(0x103c, 0x2260, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2261, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2262, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x2263, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x2264, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x2265, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), @@ -5026,23 +4749,13 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x2273, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), SND_PCI_QUIRK(0x103c, 0x2277, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), SND_PCI_QUIRK(0x103c, 0x2278, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x227d, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x227e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x227f, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2280, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2281, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x2282, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2289, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x228a, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x228b, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x228c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x228d, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x228e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x22c5, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22c6, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x22c7, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x22c8, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22c3, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x22c4, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x2334, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x2335, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), @@ -5070,6 +4783,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x104d, 0x9099, "Sony VAIO S13", ALC275_FIXUP_SONY_DISABLE_AAMIX), SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook", ALC269_FIXUP_LIFEBOOK), SND_PCI_QUIRK(0x10cf, 0x1845, "Lifebook U904", ALC269_FIXUP_LIFEBOOK_EXTMIC), + SND_PCI_QUIRK(0x144d, 0xc109, "Samsung Ativ book 9 (NP900X3G)", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1458, 0xfa53, "Gigabyte BXBT-2807", ALC283_FIXUP_BXBT2807_MIC), SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE), @@ -5085,12 +4799,13 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad T440s", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x220e, "Thinkpad T440p", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2210, "Thinkpad T540p", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad T440", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP), SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC283_FIXUP_INT_MIC), + SND_PCI_QUIRK(0x17aa, 0x501e, "Thinkpad L440", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x5026, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K), @@ -5173,28 +4888,58 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {} }; +#define ALC255_STANDARD_PINS \ + {0x18, 0x411111f0}, \ + {0x19, 0x411111f0}, \ + {0x1a, 0x411111f0}, \ + {0x1b, 0x411111f0}, \ + {0x1e, 0x411111f0} + +#define ALC282_STANDARD_PINS \ + {0x14, 0x90170110}, \ + {0x18, 0x411111f0}, \ + {0x1a, 0x411111f0}, \ + {0x1b, 0x411111f0}, \ + {0x1e, 0x411111f0} + +#define ALC290_STANDARD_PINS \ + {0x12, 0x99a30130}, \ + {0x13, 0x40000000}, \ + {0x16, 0x411111f0}, \ + {0x17, 0x411111f0}, \ + {0x19, 0x411111f0}, \ + {0x1b, 0x411111f0}, \ + {0x1e, 0x411111f0} + +#define ALC292_STANDARD_PINS \ + {0x14, 0x90170110}, \ + {0x15, 0x0221401f}, \ + {0x1a, 0x411111f0}, \ + {0x1b, 0x411111f0}, \ + {0x1d, 0x40700001}, \ + {0x1e, 0x411111f0} + static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE, + ALC255_STANDARD_PINS, + {0x12, 0x40300000}, + {0x14, 0x90170110}, + {0x17, 0x411111f0}, + {0x1d, 0x40538029}, + {0x21, 0x02211020}), SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC255_STANDARD_PINS, {0x12, 0x90a60140}, {0x14, 0x90170110}, {0x17, 0x40000000}, - {0x18, 0x411111f0}, - {0x19, 0x411111f0}, - {0x1a, 0x411111f0}, - {0x1b, 0x411111f0}, {0x1d, 0x40700001}, - {0x1e, 0x411111f0}, {0x21, 0x02211020}), SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC255_STANDARD_PINS, {0x12, 0x90a60160}, {0x14, 0x90170120}, {0x17, 0x40000000}, - {0x18, 0x411111f0}, - {0x19, 0x411111f0}, - {0x1a, 0x411111f0}, - {0x1b, 0x411111f0}, {0x1d, 0x40700001}, - {0x1e, 0x411111f0}, {0x21, 0x02211030}), SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, {0x12, 0x90a60160}, @@ -5208,70 +4953,101 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x1e, 0x411111f0}, {0x21, 0x0321102f}), SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC255_STANDARD_PINS, {0x12, 0x90a60160}, {0x14, 0x90170130}, {0x17, 0x40000000}, - {0x18, 0x411111f0}, - {0x19, 0x411111f0}, - {0x1a, 0x411111f0}, - {0x1b, 0x411111f0}, {0x1d, 0x40700001}, - {0x1e, 0x411111f0}, {0x21, 0x02211040}), SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC255_STANDARD_PINS, {0x12, 0x90a60160}, {0x14, 0x90170140}, {0x17, 0x40000000}, - {0x18, 0x411111f0}, - {0x19, 0x411111f0}, - {0x1a, 0x411111f0}, - {0x1b, 0x411111f0}, {0x1d, 0x40700001}, - {0x1e, 0x411111f0}, {0x21, 0x02211050}), SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC255_STANDARD_PINS, {0x12, 0x90a60170}, {0x14, 0x90170120}, {0x17, 0x40000000}, - {0x18, 0x411111f0}, - {0x19, 0x411111f0}, - {0x1a, 0x411111f0}, - {0x1b, 0x411111f0}, {0x1d, 0x40700001}, - {0x1e, 0x411111f0}, {0x21, 0x02211030}), SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC255_STANDARD_PINS, {0x12, 0x90a60170}, {0x14, 0x90170130}, {0x17, 0x40000000}, - {0x18, 0x411111f0}, - {0x19, 0x411111f0}, - {0x1a, 0x411111f0}, - {0x1b, 0x411111f0}, {0x1d, 0x40700001}, - {0x1e, 0x411111f0}, {0x21, 0x02211040}), + SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED, + {0x12, 0x90a60140}, + {0x13, 0x40000000}, + {0x14, 0x90170110}, + {0x15, 0x0421101f}, + {0x16, 0x411111f0}, + {0x17, 0x411111f0}, + {0x18, 0x02811030}, + {0x19, 0x411111f0}, + {0x1a, 0x04a1103f}, + {0x1b, 0x02011020}, + {0x1d, 0x40700001}, + {0x1e, 0x411111f0}), SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP 15 Touchsmart", ALC269_FIXUP_HP_MUTE_LED_MIC1, + ALC282_STANDARD_PINS, {0x12, 0x99a30130}, - {0x14, 0x90170110}, {0x17, 0x40000000}, - {0x18, 0x411111f0}, {0x19, 0x03a11020}, - {0x1a, 0x411111f0}, - {0x1b, 0x411111f0}, {0x1d, 0x40f41905}, - {0x1e, 0x411111f0}, {0x21, 0x0321101f}), + SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, + ALC282_STANDARD_PINS, + {0x12, 0x99a30130}, + {0x17, 0x40020008}, + {0x19, 0x03a11020}, + {0x1d, 0x40e00001}, + {0x21, 0x03211040}), + SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, + ALC282_STANDARD_PINS, + {0x12, 0x99a30130}, + {0x17, 0x40000000}, + {0x19, 0x03a11030}, + {0x1d, 0x40e00001}, + {0x21, 0x03211020}), + SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, + ALC282_STANDARD_PINS, + {0x12, 0x99a30130}, + {0x17, 0x40000000}, + {0x19, 0x03a11030}, + {0x1d, 0x40f00001}, + {0x21, 0x03211020}), + SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, + ALC282_STANDARD_PINS, + {0x12, 0x99a30130}, + {0x17, 0x40000000}, + {0x19, 0x04a11020}, + {0x1d, 0x40f00001}, + {0x21, 0x0421101f}), + SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, + ALC282_STANDARD_PINS, + {0x12, 0x99a30130}, + {0x17, 0x40000000}, + {0x19, 0x03a11030}, + {0x1d, 0x40f00001}, + {0x21, 0x04211020}), + SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED, + ALC282_STANDARD_PINS, + {0x12, 0x90a60140}, + {0x17, 0x40000000}, + {0x19, 0x04a11030}, + {0x1d, 0x40f00001}, + {0x21, 0x04211020}), SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC282_STANDARD_PINS, {0x12, 0x90a60130}, - {0x14, 0x90170110}, {0x17, 0x40020008}, - {0x18, 0x411111f0}, {0x19, 0x411111f0}, - {0x1a, 0x411111f0}, - {0x1b, 0x411111f0}, {0x1d, 0x40e00001}, - {0x1e, 0x411111f0}, {0x21, 0x0321101f}), SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, {0x12, 0x90a60160}, @@ -5284,42 +5060,97 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x1d, 0x40700001}, {0x1e, 0x411111f0}, {0x21, 0x02211030}), + SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC282_STANDARD_PINS, + {0x12, 0x90a60130}, + {0x17, 0x40020008}, + {0x19, 0x03a11020}, + {0x1d, 0x40e00001}, + {0x21, 0x0321101f}), + SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, + ALC290_STANDARD_PINS, + {0x14, 0x411111f0}, + {0x15, 0x04211040}, + {0x18, 0x90170112}, + {0x1a, 0x04a11020}, + {0x1d, 0x4075812d}), + SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, + ALC290_STANDARD_PINS, + {0x14, 0x411111f0}, + {0x15, 0x04211040}, + {0x18, 0x90170110}, + {0x1a, 0x04a11020}, + {0x1d, 0x4075812d}), + SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, + ALC290_STANDARD_PINS, + {0x14, 0x411111f0}, + {0x15, 0x0421101f}, + {0x18, 0x411111f0}, + {0x1a, 0x04a11020}, + {0x1d, 0x4075812d}), + SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, + ALC290_STANDARD_PINS, + {0x14, 0x411111f0}, + {0x15, 0x04211020}, + {0x18, 0x411111f0}, + {0x1a, 0x04a11040}, + {0x1d, 0x4076a12d}), + SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, + ALC290_STANDARD_PINS, + {0x14, 0x90170110}, + {0x15, 0x04211020}, + {0x18, 0x411111f0}, + {0x1a, 0x04a11040}, + {0x1d, 0x4076a12d}), + SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, + ALC290_STANDARD_PINS, + {0x14, 0x90170110}, + {0x15, 0x04211020}, + {0x18, 0x411111f0}, + {0x1a, 0x04a11020}, + {0x1d, 0x4076a12d}), + SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, + ALC290_STANDARD_PINS, + {0x14, 0x90170110}, + {0x15, 0x0421101f}, + {0x18, 0x411111f0}, + {0x1a, 0x04a11020}, + {0x1d, 0x4075812d}), + SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, + ALC292_STANDARD_PINS, + {0x12, 0x90a60140}, + {0x13, 0x411111f0}, + {0x16, 0x01014020}, + {0x18, 0x411111f0}, + {0x19, 0x01a19030}), + SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, + ALC292_STANDARD_PINS, + {0x12, 0x90a60140}, + {0x13, 0x411111f0}, + {0x16, 0x01014020}, + {0x18, 0x02a19031}, + {0x19, 0x01a1903e}), SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, + ALC292_STANDARD_PINS, {0x12, 0x90a60140}, {0x13, 0x411111f0}, - {0x14, 0x90170110}, - {0x15, 0x0221401f}, {0x16, 0x411111f0}, {0x18, 0x411111f0}, - {0x19, 0x411111f0}, - {0x1a, 0x411111f0}, - {0x1b, 0x411111f0}, - {0x1d, 0x40700001}, - {0x1e, 0x411111f0}), + {0x19, 0x411111f0}), SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC292_STANDARD_PINS, {0x12, 0x40000000}, {0x13, 0x90a60140}, - {0x14, 0x90170110}, - {0x15, 0x0221401f}, {0x16, 0x21014020}, {0x18, 0x411111f0}, - {0x19, 0x21a19030}, - {0x1a, 0x411111f0}, - {0x1b, 0x411111f0}, - {0x1d, 0x40700001}, - {0x1e, 0x411111f0}), + {0x19, 0x21a19030}), SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC292_STANDARD_PINS, {0x12, 0x40000000}, {0x13, 0x90a60140}, - {0x14, 0x90170110}, - {0x15, 0x0221401f}, {0x16, 0x411111f0}, {0x18, 0x411111f0}, - {0x19, 0x411111f0}, - {0x1a, 0x411111f0}, - {0x1b, 0x411111f0}, - {0x1d, 0x40700001}, - {0x1e, 0x411111f0}), + {0x19, 0x411111f0}), {} }; @@ -5342,10 +5173,8 @@ static void alc269_fill_coef(struct hda_codec *codec) } if ((alc_get_coef0(codec) & 0x00ff) == 0x017) { - val = alc_read_coef_idx(codec, 0x04); /* Power up output pin */ - if (val != -1) - alc_write_coef_idx(codec, 0x04, val | (1<<11)); + alc_update_coef_idx(codec, 0x04, 0, 1<<11); } if ((alc_get_coef0(codec) & 0x00ff) == 0x018) { @@ -5361,13 +5190,11 @@ static void alc269_fill_coef(struct hda_codec *codec) } } - val = alc_read_coef_idx(codec, 0xd); /* Class D */ - if (val != -1) - alc_write_coef_idx(codec, 0xd, val | (1<<14)); + /* Class D */ + alc_update_coef_idx(codec, 0xd, 0, 1<<14); - val = alc_read_coef_idx(codec, 0x4); /* HP */ - if (val != -1) - alc_write_coef_idx(codec, 0x4, val | (1<<11)); + /* HP */ + alc_update_coef_idx(codec, 0x4, 0, 1<<11); } /* @@ -6012,7 +5839,7 @@ static const struct hda_fixup alc662_fixups[] = { }, [ALC662_FIXUP_INV_DMIC] = { .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_inv_dmic_0x12, + .v.func = alc_fixup_inv_dmic, }, [ALC668_FIXUP_DELL_XPS13] = { .type = HDA_FIXUP_FUNC, @@ -6247,16 +6074,14 @@ static const struct snd_hda_pin_quirk alc662_pin_fixup_tbl[] = { static void alc662_fill_coef(struct hda_codec *codec) { - int val, coef; + int coef; coef = alc_get_coef0(codec); switch (codec->vendor_id) { case 0x10ec0662: - if ((coef & 0x00f0) == 0x0030) { - val = alc_read_coef_idx(codec, 0x4); /* EAPD Ctrl */ - alc_write_coef_idx(codec, 0x4, val & ~(1<<10)); - } + if ((coef & 0x00f0) == 0x0030) + alc_update_coef_idx(codec, 0x4, 1<<10, 0); /* EAPD Ctrl */ break; case 0x10ec0272: case 0x10ec0273: @@ -6265,8 +6090,7 @@ static void alc662_fill_coef(struct hda_codec *codec) case 0x10ec0670: case 0x10ec0671: case 0x10ec0672: - val = alc_read_coef_idx(codec, 0xd); /* EAPD Ctrl */ - alc_write_coef_idx(codec, 0xd, val | (1<<14)); + alc_update_coef_idx(codec, 0xd, 0, 1<<14); /* EAPD Ctrl */ break; } } diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 98cd190..4f6413e 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -32,7 +32,6 @@ #include <linux/module.h> #include <sound/core.h> #include <sound/jack.h> -#include <sound/tlv.h> #include "hda_codec.h" #include "hda_local.h" #include "hda_auto_parser.h" @@ -41,11 +40,6 @@ #include "hda_generic.h" enum { - STAC_VREF_EVENT = 8, - STAC_PWR_EVENT, -}; - -enum { STAC_REF, STAC_9200_OQO, STAC_9200_DELL_D21, @@ -487,7 +481,7 @@ static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid, /* update power bit per jack plug/unplug */ static void jack_update_power(struct hda_codec *codec, - struct hda_jack_tbl *jack) + struct hda_jack_callback *jack) { struct sigmatel_spec *spec = codec->spec; int i; @@ -495,9 +489,9 @@ static void jack_update_power(struct hda_codec *codec, if (!spec->num_pwrs) return; - if (jack && jack->nid) { - stac_toggle_power_map(codec, jack->nid, - snd_hda_jack_detect(codec, jack->nid), + if (jack && jack->tbl->nid) { + stac_toggle_power_map(codec, jack->tbl->nid, + snd_hda_jack_detect(codec, jack->tbl->nid), true); return; } @@ -505,42 +499,19 @@ static void jack_update_power(struct hda_codec *codec, /* update all jacks */ for (i = 0; i < spec->num_pwrs; i++) { hda_nid_t nid = spec->pwr_nids[i]; - jack = snd_hda_jack_tbl_get(codec, nid); - if (!jack || !jack->action) + if (!snd_hda_jack_tbl_get(codec, nid)) continue; - if (jack->action == STAC_PWR_EVENT || - jack->action <= HDA_GEN_LAST_EVENT) - stac_toggle_power_map(codec, nid, - snd_hda_jack_detect(codec, nid), - false); + stac_toggle_power_map(codec, nid, + snd_hda_jack_detect(codec, nid), + false); } snd_hda_codec_write(codec, codec->afg, 0, AC_VERB_IDT_SET_POWER_MAP, spec->power_map_bits); } -static void stac_hp_automute(struct hda_codec *codec, - struct hda_jack_tbl *jack) -{ - snd_hda_gen_hp_automute(codec, jack); - jack_update_power(codec, jack); -} - -static void stac_line_automute(struct hda_codec *codec, - struct hda_jack_tbl *jack) -{ - snd_hda_gen_line_automute(codec, jack); - jack_update_power(codec, jack); -} - -static void stac_mic_autoswitch(struct hda_codec *codec, - struct hda_jack_tbl *jack) -{ - snd_hda_gen_mic_autoswitch(codec, jack); - jack_update_power(codec, jack); -} - -static void stac_vref_event(struct hda_codec *codec, struct hda_jack_tbl *event) +static void stac_vref_event(struct hda_codec *codec, + struct hda_jack_callback *event) { unsigned int data; @@ -563,13 +534,10 @@ static void stac_init_power_map(struct hda_codec *codec) hda_nid_t nid = spec->pwr_nids[i]; unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); def_conf = get_defcfg_connect(def_conf); - if (snd_hda_jack_tbl_get(codec, nid)) - continue; if (def_conf == AC_JACK_PORT_COMPLEX && spec->vref_mute_led_nid != nid && is_jack_detectable(codec, nid)) { snd_hda_jack_detect_enable_callback(codec, nid, - STAC_PWR_EVENT, jack_update_power); } else { if (def_conf == AC_JACK_PORT_NONE) @@ -3020,7 +2988,7 @@ static void stac92hd71bxx_fixup_hp_m4(struct hda_codec *codec, const struct hda_fixup *fix, int action) { struct sigmatel_spec *spec = codec->spec; - struct hda_jack_tbl *jack; + struct hda_jack_callback *jack; if (action != HDA_FIXUP_ACT_PRE_PROBE) return; @@ -3028,11 +2996,9 @@ static void stac92hd71bxx_fixup_hp_m4(struct hda_codec *codec, /* Enable VREF power saving on GPIO1 detect */ snd_hda_codec_write_cache(codec, codec->afg, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02); - snd_hda_jack_detect_enable_callback(codec, codec->afg, - STAC_VREF_EVENT, - stac_vref_event); - jack = snd_hda_jack_tbl_get(codec, codec->afg); - if (jack) + jack = snd_hda_jack_detect_enable_callback(codec, codec->afg, + stac_vref_event); + if (!IS_ERR(jack)) jack->private_data = 0x02; spec->gpio_mask |= 0x02; @@ -4044,7 +4010,7 @@ static void stac9205_fixup_dell_m43(struct hda_codec *codec, const struct hda_fixup *fix, int action) { struct sigmatel_spec *spec = codec->spec; - struct hda_jack_tbl *jack; + struct hda_jack_callback *jack; if (action == HDA_FIXUP_ACT_PRE_PROBE) { snd_hda_apply_pincfgs(codec, dell_9205_m43_pin_configs); @@ -4052,11 +4018,9 @@ static void stac9205_fixup_dell_m43(struct hda_codec *codec, /* Enable unsol response for GPIO4/Dock HP connection */ snd_hda_codec_write_cache(codec, codec->afg, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10); - snd_hda_jack_detect_enable_callback(codec, codec->afg, - STAC_VREF_EVENT, - stac_vref_event); - jack = snd_hda_jack_tbl_get(codec, codec->afg); - if (jack) + jack = snd_hda_jack_detect_enable_callback(codec, codec->afg, + stac_vref_event); + if (!IS_ERR(jack)) jack->private_data = 0x01; spec->gpio_dir = 0x0b; @@ -4219,17 +4183,11 @@ static int stac_parse_auto_config(struct hda_codec *codec) spec->gen.pcm_capture_hook = stac_capture_pcm_hook; spec->gen.automute_hook = stac_update_outputs; - spec->gen.hp_automute_hook = stac_hp_automute; - spec->gen.line_automute_hook = stac_line_automute; - spec->gen.mic_autoswitch_hook = stac_mic_autoswitch; err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); if (err < 0) return err; - /* minimum value is actually mute */ - spec->gen.vmaster_tlv[3] |= TLV_DB_SCALE_MUTE; - /* setup analog beep controls */ if (spec->anabeep_nid > 0) { err = stac_auto_create_beep_ctls(codec, @@ -4276,16 +4234,8 @@ static int stac_parse_auto_config(struct hda_codec *codec) return err; } - return 0; -} - -static int stac_build_controls(struct hda_codec *codec) -{ - int err = snd_hda_gen_build_controls(codec); - - if (err < 0) - return err; stac_init_power_map(codec); + return 0; } @@ -4399,7 +4349,7 @@ static int stac_suspend(struct hda_codec *codec) #endif /* CONFIG_PM */ static const struct hda_codec_ops stac_patch_ops = { - .build_controls = stac_build_controls, + .build_controls = snd_hda_gen_build_controls, .build_pcms = snd_hda_gen_build_pcms, .init = stac_init, .free = stac_free, @@ -4420,6 +4370,7 @@ static int alloc_stac_spec(struct hda_codec *codec) snd_hda_gen_spec_init(&spec->gen); codec->spec = spec; codec->no_trigger_sense = 1; /* seems common with STAC/IDT codecs */ + spec->gen.dac_min_mute = true; return 0; } diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 7781662..6c206b6 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -118,7 +118,6 @@ static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream, int action); -static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl); static struct via_spec *via_new_spec(struct hda_codec *codec) { @@ -575,25 +574,12 @@ static const struct snd_kcontrol_new vt1708_jack_detect_ctl[] = { {} /* terminator */ }; -static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl) +static void via_jack_powerstate_event(struct hda_codec *codec, + struct hda_jack_callback *tbl) { set_widgets_power_state(codec); - snd_hda_gen_hp_automute(codec, tbl); } -static void via_line_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl) -{ - set_widgets_power_state(codec); - snd_hda_gen_line_automute(codec, tbl); -} - -static void via_jack_powerstate_event(struct hda_codec *codec, struct hda_jack_tbl *tbl) -{ - set_widgets_power_state(codec); -} - -#define VIA_JACK_EVENT (HDA_GEN_LAST_EVENT + 1) - static void via_set_jack_unsol_events(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -601,25 +587,17 @@ static void via_set_jack_unsol_events(struct hda_codec *codec) hda_nid_t pin; int i; - spec->gen.hp_automute_hook = via_hp_automute; - if (cfg->speaker_pins[0]) - spec->gen.line_automute_hook = via_line_automute; - for (i = 0; i < cfg->line_outs; i++) { pin = cfg->line_out_pins[i]; - if (pin && !snd_hda_jack_tbl_get(codec, pin) && - is_jack_detectable(codec, pin)) + if (pin && is_jack_detectable(codec, pin)) snd_hda_jack_detect_enable_callback(codec, pin, - VIA_JACK_EVENT, via_jack_powerstate_event); } for (i = 0; i < cfg->num_inputs; i++) { pin = cfg->line_out_pins[i]; - if (pin && !snd_hda_jack_tbl_get(codec, pin) && - is_jack_detectable(codec, pin)) + if (pin && is_jack_detectable(codec, pin)) snd_hda_jack_detect_enable_callback(codec, pin, - VIA_JACK_EVENT, via_jack_powerstate_event); } } diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index 87f7fc4..206ed2c 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -2528,7 +2528,7 @@ static int snd_ice1712_free(struct snd_ice1712 *ice) if (!ice->port) goto __hw_end; /* mask all interrupts */ - outb(0xc0, ICEMT(ice, IRQ)); + outb(ICE1712_MULTI_CAPTURE | ICE1712_MULTI_PLAYBACK, ICEMT(ice, IRQ)); outb(0xff, ICEREG(ice, IRQMASK)); /* --- */ __hw_end: diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c index a671f08..601315a 100644 --- a/sound/pci/lx6464es/lx6464es.c +++ b/sound/pci/lx6464es/lx6464es.c @@ -279,7 +279,6 @@ static snd_pcm_uframes_t lx_pcm_stream_pointer(struct snd_pcm_substream { struct lx6464es *chip = snd_pcm_substream_chip(substream); snd_pcm_uframes_t pos; - unsigned long flags; int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); struct lx_stream *lx_stream = is_capture ? &chip->capture_stream : @@ -287,9 +286,9 @@ static snd_pcm_uframes_t lx_pcm_stream_pointer(struct snd_pcm_substream dev_dbg(chip->card->dev, "->lx_pcm_stream_pointer\n"); - spin_lock_irqsave(&chip->lock, flags); + mutex_lock(&chip->lock); pos = lx_stream->frame_pos * substream->runtime->period_size; - spin_unlock_irqrestore(&chip->lock, flags); + mutex_unlock(&chip->lock); dev_dbg(chip->card->dev, "stream_pointer at %ld\n", pos); return pos; @@ -485,8 +484,8 @@ static void lx_trigger_stop(struct lx6464es *chip, struct lx_stream *lx_stream) } -static void lx_trigger_tasklet_dispatch_stream(struct lx6464es *chip, - struct lx_stream *lx_stream) +static void lx_trigger_dispatch_stream(struct lx6464es *chip, + struct lx_stream *lx_stream) { switch (lx_stream->status) { case LX_STREAM_STATUS_SCHEDULE_RUN: @@ -502,24 +501,12 @@ static void lx_trigger_tasklet_dispatch_stream(struct lx6464es *chip, } } -static void lx_trigger_tasklet(unsigned long data) -{ - struct lx6464es *chip = (struct lx6464es *)data; - unsigned long flags; - - dev_dbg(chip->card->dev, "->lx_trigger_tasklet\n"); - - spin_lock_irqsave(&chip->lock, flags); - lx_trigger_tasklet_dispatch_stream(chip, &chip->capture_stream); - lx_trigger_tasklet_dispatch_stream(chip, &chip->playback_stream); - spin_unlock_irqrestore(&chip->lock, flags); -} - static int lx_pcm_trigger_dispatch(struct lx6464es *chip, struct lx_stream *lx_stream, int cmd) { int err = 0; + mutex_lock(&chip->lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: lx_stream->status = LX_STREAM_STATUS_SCHEDULE_RUN; @@ -533,9 +520,12 @@ static int lx_pcm_trigger_dispatch(struct lx6464es *chip, err = -EINVAL; goto exit; } - tasklet_schedule(&chip->trigger_tasklet); + + lx_trigger_dispatch_stream(chip, &chip->capture_stream); + lx_trigger_dispatch_stream(chip, &chip->playback_stream); exit: + mutex_unlock(&chip->lock); return err; } @@ -861,6 +851,7 @@ static int lx_pcm_create(struct lx6464es *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &lx_ops_capture); pcm->info_flags = 0; + pcm->nonatomic = true; strcpy(pcm->name, card_name); err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1009,15 +1000,9 @@ static int snd_lx6464es_create(struct snd_card *card, chip->irq = -1; /* initialize synchronization structs */ - spin_lock_init(&chip->lock); - spin_lock_init(&chip->msg_lock); + mutex_init(&chip->lock); + mutex_init(&chip->msg_lock); mutex_init(&chip->setup_mutex); - tasklet_init(&chip->trigger_tasklet, lx_trigger_tasklet, - (unsigned long)chip); - tasklet_init(&chip->tasklet_capture, lx_tasklet_capture, - (unsigned long)chip); - tasklet_init(&chip->tasklet_playback, lx_tasklet_playback, - (unsigned long)chip); /* request resources */ err = pci_request_regions(pci, card_name); @@ -1032,8 +1017,8 @@ static int snd_lx6464es_create(struct snd_card *card, /* dsp port */ chip->port_dsp_bar = pci_ioremap_bar(pci, 2); - err = request_irq(pci->irq, lx_interrupt, IRQF_SHARED, - KBUILD_MODNAME, chip); + err = request_threaded_irq(pci->irq, lx_interrupt, lx_threaded_irq, + IRQF_SHARED, KBUILD_MODNAME, chip); if (err) { dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq); goto request_irq_failed; diff --git a/sound/pci/lx6464es/lx6464es.h b/sound/pci/lx6464es/lx6464es.h index 6792eda..1bec187 100644 --- a/sound/pci/lx6464es/lx6464es.h +++ b/sound/pci/lx6464es/lx6464es.h @@ -71,14 +71,10 @@ struct lx6464es { u8 mac_address[6]; - spinlock_t lock; /* interrupt spinlock */ + struct mutex lock; /* interrupt lock */ struct mutex setup_mutex; /* mutex used in hw_params, open * and close */ - struct tasklet_struct trigger_tasklet; /* trigger tasklet */ - struct tasklet_struct tasklet_capture; - struct tasklet_struct tasklet_playback; - /* ports */ unsigned long port_plx; /* io port (size=256) */ void __iomem *port_plx_remapped; /* remapped plx port */ @@ -87,8 +83,9 @@ struct lx6464es { * size=8K) */ /* messaging */ - spinlock_t msg_lock; /* message spinlock */ + struct mutex msg_lock; /* message lock */ struct lx_rmh rmh; + u32 irqsrc; /* configuration */ uint freq_ratio : 2; diff --git a/sound/pci/lx6464es/lx_core.c b/sound/pci/lx6464es/lx_core.c index e8f38e5..f3d6202 100644 --- a/sound/pci/lx6464es/lx_core.c +++ b/sound/pci/lx6464es/lx_core.c @@ -332,27 +332,25 @@ polling_successful: int lx_dsp_get_version(struct lx6464es *chip, u32 *rdsp_version) { u16 ret; - unsigned long flags; - spin_lock_irqsave(&chip->msg_lock, flags); + mutex_lock(&chip->msg_lock); lx_message_init(&chip->rmh, CMD_01_GET_SYS_CFG); ret = lx_message_send_atomic(chip, &chip->rmh); *rdsp_version = chip->rmh.stat[1]; - spin_unlock_irqrestore(&chip->msg_lock, flags); + mutex_unlock(&chip->msg_lock); return ret; } int lx_dsp_get_clock_frequency(struct lx6464es *chip, u32 *rfreq) { u16 ret = 0; - unsigned long flags; u32 freq_raw = 0; u32 freq = 0; u32 frequency = 0; - spin_lock_irqsave(&chip->msg_lock, flags); + mutex_lock(&chip->msg_lock); lx_message_init(&chip->rmh, CMD_01_GET_SYS_CFG); ret = lx_message_send_atomic(chip, &chip->rmh); @@ -370,7 +368,7 @@ int lx_dsp_get_clock_frequency(struct lx6464es *chip, u32 *rfreq) frequency = 48000; } - spin_unlock_irqrestore(&chip->msg_lock, flags); + mutex_unlock(&chip->msg_lock); *rfreq = frequency * chip->freq_ratio; @@ -398,25 +396,23 @@ int lx_dsp_get_mac(struct lx6464es *chip) int lx_dsp_set_granularity(struct lx6464es *chip, u32 gran) { - unsigned long flags; int ret; - spin_lock_irqsave(&chip->msg_lock, flags); + mutex_lock(&chip->msg_lock); lx_message_init(&chip->rmh, CMD_02_SET_GRANULARITY); chip->rmh.cmd[0] |= gran; ret = lx_message_send_atomic(chip, &chip->rmh); - spin_unlock_irqrestore(&chip->msg_lock, flags); + mutex_unlock(&chip->msg_lock); return ret; } int lx_dsp_read_async_events(struct lx6464es *chip, u32 *data) { - unsigned long flags; int ret; - spin_lock_irqsave(&chip->msg_lock, flags); + mutex_lock(&chip->msg_lock); lx_message_init(&chip->rmh, CMD_04_GET_EVENT); chip->rmh.stat_len = 9; /* we don't necessarily need the full length */ @@ -426,7 +422,7 @@ int lx_dsp_read_async_events(struct lx6464es *chip, u32 *data) if (!ret) memcpy(data, chip->rmh.stat, chip->rmh.stat_len * sizeof(u32)); - spin_unlock_irqrestore(&chip->msg_lock, flags); + mutex_unlock(&chip->msg_lock); return ret; } @@ -440,18 +436,16 @@ int lx_pipe_allocate(struct lx6464es *chip, u32 pipe, int is_capture, int channels) { int err; - unsigned long flags; - u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); - spin_lock_irqsave(&chip->msg_lock, flags); + mutex_lock(&chip->msg_lock); lx_message_init(&chip->rmh, CMD_06_ALLOCATE_PIPE); chip->rmh.cmd[0] |= pipe_cmd; chip->rmh.cmd[0] |= channels; err = lx_message_send_atomic(chip, &chip->rmh); - spin_unlock_irqrestore(&chip->msg_lock, flags); + mutex_unlock(&chip->msg_lock); if (err != 0) dev_err(chip->card->dev, "could not allocate pipe\n"); @@ -462,17 +456,15 @@ int lx_pipe_allocate(struct lx6464es *chip, u32 pipe, int is_capture, int lx_pipe_release(struct lx6464es *chip, u32 pipe, int is_capture) { int err; - unsigned long flags; - u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); - spin_lock_irqsave(&chip->msg_lock, flags); + mutex_lock(&chip->msg_lock); lx_message_init(&chip->rmh, CMD_07_RELEASE_PIPE); chip->rmh.cmd[0] |= pipe_cmd; err = lx_message_send_atomic(chip, &chip->rmh); - spin_unlock_irqrestore(&chip->msg_lock, flags); + mutex_unlock(&chip->msg_lock); return err; } @@ -481,8 +473,6 @@ int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture, u32 *r_needed, u32 *r_freed, u32 *size_array) { int err; - unsigned long flags; - u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); #ifdef CONFIG_SND_DEBUG @@ -493,7 +483,7 @@ int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture, *r_needed = 0; *r_freed = 0; - spin_lock_irqsave(&chip->msg_lock, flags); + mutex_lock(&chip->msg_lock); lx_message_init(&chip->rmh, CMD_08_ASK_BUFFERS); chip->rmh.cmd[0] |= pipe_cmd; @@ -527,7 +517,7 @@ int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture, } } - spin_unlock_irqrestore(&chip->msg_lock, flags); + mutex_unlock(&chip->msg_lock); return err; } @@ -535,36 +525,32 @@ int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture, int lx_pipe_stop(struct lx6464es *chip, u32 pipe, int is_capture) { int err; - unsigned long flags; - u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); - spin_lock_irqsave(&chip->msg_lock, flags); + mutex_lock(&chip->msg_lock); lx_message_init(&chip->rmh, CMD_09_STOP_PIPE); chip->rmh.cmd[0] |= pipe_cmd; err = lx_message_send_atomic(chip, &chip->rmh); - spin_unlock_irqrestore(&chip->msg_lock, flags); + mutex_unlock(&chip->msg_lock); return err; } static int lx_pipe_toggle_state(struct lx6464es *chip, u32 pipe, int is_capture) { int err; - unsigned long flags; - u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); - spin_lock_irqsave(&chip->msg_lock, flags); + mutex_lock(&chip->msg_lock); lx_message_init(&chip->rmh, CMD_0B_TOGGLE_PIPE_STATE); chip->rmh.cmd[0] |= pipe_cmd; err = lx_message_send_atomic(chip, &chip->rmh); - spin_unlock_irqrestore(&chip->msg_lock, flags); + mutex_unlock(&chip->msg_lock); return err; } @@ -600,11 +586,9 @@ int lx_pipe_sample_count(struct lx6464es *chip, u32 pipe, int is_capture, u64 *rsample_count) { int err; - unsigned long flags; - u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); - spin_lock_irqsave(&chip->msg_lock, flags); + mutex_lock(&chip->msg_lock); lx_message_init(&chip->rmh, CMD_0A_GET_PIPE_SPL_COUNT); chip->rmh.cmd[0] |= pipe_cmd; @@ -621,18 +605,16 @@ int lx_pipe_sample_count(struct lx6464es *chip, u32 pipe, int is_capture, + chip->rmh.stat[1]; /* lo part */ } - spin_unlock_irqrestore(&chip->msg_lock, flags); + mutex_unlock(&chip->msg_lock); return err; } int lx_pipe_state(struct lx6464es *chip, u32 pipe, int is_capture, u16 *rstate) { int err; - unsigned long flags; - u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); - spin_lock_irqsave(&chip->msg_lock, flags); + mutex_lock(&chip->msg_lock); lx_message_init(&chip->rmh, CMD_0A_GET_PIPE_SPL_COUNT); chip->rmh.cmd[0] |= pipe_cmd; @@ -644,7 +626,7 @@ int lx_pipe_state(struct lx6464es *chip, u32 pipe, int is_capture, u16 *rstate) else *rstate = (chip->rmh.stat[0] >> PSTATE_OFFSET) & 0x0F; - spin_unlock_irqrestore(&chip->msg_lock, flags); + mutex_unlock(&chip->msg_lock); return err; } @@ -686,18 +668,16 @@ int lx_stream_set_state(struct lx6464es *chip, u32 pipe, int is_capture, enum stream_state_t state) { int err; - unsigned long flags; - u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); - spin_lock_irqsave(&chip->msg_lock, flags); + mutex_lock(&chip->msg_lock); lx_message_init(&chip->rmh, CMD_13_SET_STREAM_STATE); chip->rmh.cmd[0] |= pipe_cmd; chip->rmh.cmd[0] |= state; err = lx_message_send_atomic(chip, &chip->rmh); - spin_unlock_irqrestore(&chip->msg_lock, flags); + mutex_unlock(&chip->msg_lock); return err; } @@ -706,17 +686,14 @@ int lx_stream_set_format(struct lx6464es *chip, struct snd_pcm_runtime *runtime, u32 pipe, int is_capture) { int err; - unsigned long flags; - u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); - u32 channels = runtime->channels; if (runtime->channels != channels) dev_err(chip->card->dev, "channel count mismatch: %d vs %d", runtime->channels, channels); - spin_lock_irqsave(&chip->msg_lock, flags); + mutex_lock(&chip->msg_lock); lx_message_init(&chip->rmh, CMD_0C_DEF_STREAM); chip->rmh.cmd[0] |= pipe_cmd; @@ -732,7 +709,7 @@ int lx_stream_set_format(struct lx6464es *chip, struct snd_pcm_runtime *runtime, chip->rmh.cmd[0] |= channels-1; err = lx_message_send_atomic(chip, &chip->rmh); - spin_unlock_irqrestore(&chip->msg_lock, flags); + mutex_unlock(&chip->msg_lock); return err; } @@ -741,11 +718,9 @@ int lx_stream_state(struct lx6464es *chip, u32 pipe, int is_capture, int *rstate) { int err; - unsigned long flags; - u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); - spin_lock_irqsave(&chip->msg_lock, flags); + mutex_lock(&chip->msg_lock); lx_message_init(&chip->rmh, CMD_0E_GET_STREAM_SPL_COUNT); chip->rmh.cmd[0] |= pipe_cmd; @@ -754,7 +729,7 @@ int lx_stream_state(struct lx6464es *chip, u32 pipe, int is_capture, *rstate = (chip->rmh.stat[0] & SF_START) ? START_STATE : PAUSE_STATE; - spin_unlock_irqrestore(&chip->msg_lock, flags); + mutex_unlock(&chip->msg_lock); return err; } @@ -762,11 +737,9 @@ int lx_stream_sample_position(struct lx6464es *chip, u32 pipe, int is_capture, u64 *r_bytepos) { int err; - unsigned long flags; - u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); - spin_lock_irqsave(&chip->msg_lock, flags); + mutex_lock(&chip->msg_lock); lx_message_init(&chip->rmh, CMD_0E_GET_STREAM_SPL_COUNT); chip->rmh.cmd[0] |= pipe_cmd; @@ -777,7 +750,7 @@ int lx_stream_sample_position(struct lx6464es *chip, u32 pipe, int is_capture, << 32) /* hi part */ + chip->rmh.stat[1]; /* lo part */ - spin_unlock_irqrestore(&chip->msg_lock, flags); + mutex_unlock(&chip->msg_lock); return err; } @@ -787,11 +760,9 @@ int lx_buffer_give(struct lx6464es *chip, u32 pipe, int is_capture, u32 *r_buffer_index) { int err; - unsigned long flags; - u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); - spin_lock_irqsave(&chip->msg_lock, flags); + mutex_lock(&chip->msg_lock); lx_message_init(&chip->rmh, CMD_0F_UPDATE_BUFFER); chip->rmh.cmd[0] |= pipe_cmd; @@ -828,7 +799,7 @@ int lx_buffer_give(struct lx6464es *chip, u32 pipe, int is_capture, "lx_buffer_give EB_CMD_REFUSED\n"); done: - spin_unlock_irqrestore(&chip->msg_lock, flags); + mutex_unlock(&chip->msg_lock); return err; } @@ -836,11 +807,9 @@ int lx_buffer_free(struct lx6464es *chip, u32 pipe, int is_capture, u32 *r_buffer_size) { int err; - unsigned long flags; - u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); - spin_lock_irqsave(&chip->msg_lock, flags); + mutex_lock(&chip->msg_lock); lx_message_init(&chip->rmh, CMD_11_CANCEL_BUFFER); chip->rmh.cmd[0] |= pipe_cmd; @@ -852,7 +821,7 @@ int lx_buffer_free(struct lx6464es *chip, u32 pipe, int is_capture, if (err == 0) *r_buffer_size = chip->rmh.stat[0] & MASK_DATA_SIZE; - spin_unlock_irqrestore(&chip->msg_lock, flags); + mutex_unlock(&chip->msg_lock); return err; } @@ -860,11 +829,9 @@ int lx_buffer_cancel(struct lx6464es *chip, u32 pipe, int is_capture, u32 buffer_index) { int err; - unsigned long flags; - u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); - spin_lock_irqsave(&chip->msg_lock, flags); + mutex_lock(&chip->msg_lock); lx_message_init(&chip->rmh, CMD_11_CANCEL_BUFFER); chip->rmh.cmd[0] |= pipe_cmd; @@ -872,7 +839,7 @@ int lx_buffer_cancel(struct lx6464es *chip, u32 pipe, int is_capture, err = lx_message_send_atomic(chip, &chip->rmh); - spin_unlock_irqrestore(&chip->msg_lock, flags); + mutex_unlock(&chip->msg_lock); return err; } @@ -885,12 +852,10 @@ int lx_buffer_cancel(struct lx6464es *chip, u32 pipe, int is_capture, int lx_level_unmute(struct lx6464es *chip, int is_capture, int unmute) { int err; - unsigned long flags; - /* bit set to 1: channel muted */ u64 mute_mask = unmute ? 0 : 0xFFFFFFFFFFFFFFFFLLU; - spin_lock_irqsave(&chip->msg_lock, flags); + mutex_lock(&chip->msg_lock); lx_message_init(&chip->rmh, CMD_0D_SET_MUTE); chip->rmh.cmd[0] |= PIPE_INFO_TO_CMD(is_capture, 0); @@ -904,7 +869,7 @@ int lx_level_unmute(struct lx6464es *chip, int is_capture, int unmute) err = lx_message_send_atomic(chip, &chip->rmh); - spin_unlock_irqrestore(&chip->msg_lock, flags); + mutex_unlock(&chip->msg_lock); return err; } @@ -931,10 +896,9 @@ int lx_level_peaks(struct lx6464es *chip, int is_capture, int channels, u32 *r_levels) { int err = 0; - unsigned long flags; int i; - spin_lock_irqsave(&chip->msg_lock, flags); + mutex_lock(&chip->msg_lock); for (i = 0; i < channels; i += 4) { u32 s0, s1, s2, s3; @@ -959,7 +923,7 @@ int lx_level_peaks(struct lx6464es *chip, int is_capture, int channels, r_levels += 4; } - spin_unlock_irqrestore(&chip->msg_lock, flags); + mutex_unlock(&chip->msg_lock); return err; } @@ -1075,7 +1039,6 @@ static int lx_interrupt_request_new_buffer(struct lx6464es *chip, struct snd_pcm_substream *substream = lx_stream->stream; const unsigned int is_capture = lx_stream->is_capture; int err; - unsigned long flags; const u32 channels = substream->runtime->channels; const u32 bytes_per_frame = channels * 3; @@ -1095,7 +1058,7 @@ static int lx_interrupt_request_new_buffer(struct lx6464es *chip, dev_dbg(chip->card->dev, "->lx_interrupt_request_new_buffer\n"); - spin_lock_irqsave(&chip->lock, flags); + mutex_lock(&chip->lock); err = lx_buffer_ask(chip, 0, is_capture, &needed, &freed, size_array); dev_dbg(chip->card->dev, @@ -1109,85 +1072,28 @@ static int lx_interrupt_request_new_buffer(struct lx6464es *chip, buffer_index, (unsigned long)buf, period_bytes); lx_stream->frame_pos = next_pos; - spin_unlock_irqrestore(&chip->lock, flags); + mutex_unlock(&chip->lock); return err; } -void lx_tasklet_playback(unsigned long data) -{ - struct lx6464es *chip = (struct lx6464es *)data; - struct lx_stream *lx_stream = &chip->playback_stream; - int err; - - dev_dbg(chip->card->dev, "->lx_tasklet_playback\n"); - - err = lx_interrupt_request_new_buffer(chip, lx_stream); - if (err < 0) - dev_err(chip->card->dev, - "cannot request new buffer for playback\n"); - - snd_pcm_period_elapsed(lx_stream->stream); -} - -void lx_tasklet_capture(unsigned long data) -{ - struct lx6464es *chip = (struct lx6464es *)data; - struct lx_stream *lx_stream = &chip->capture_stream; - int err; - - dev_dbg(chip->card->dev, "->lx_tasklet_capture\n"); - err = lx_interrupt_request_new_buffer(chip, lx_stream); - if (err < 0) - dev_err(chip->card->dev, - "cannot request new buffer for capture\n"); - - snd_pcm_period_elapsed(lx_stream->stream); -} - - - -static int lx_interrupt_handle_audio_transfer(struct lx6464es *chip, - u64 notified_in_pipe_mask, - u64 notified_out_pipe_mask) -{ - int err = 0; - - if (notified_in_pipe_mask) { - dev_dbg(chip->card->dev, - "requesting audio transfer for capture\n"); - tasklet_hi_schedule(&chip->tasklet_capture); - } - - if (notified_out_pipe_mask) { - dev_dbg(chip->card->dev, - "requesting audio transfer for playback\n"); - tasklet_hi_schedule(&chip->tasklet_playback); - } - - return err; -} - - irqreturn_t lx_interrupt(int irq, void *dev_id) { struct lx6464es *chip = dev_id; int async_pending, async_escmd; u32 irqsrc; - - spin_lock(&chip->lock); + bool wake_thread = false; dev_dbg(chip->card->dev, "**************************************************\n"); if (!lx_interrupt_ack(chip, &irqsrc, &async_pending, &async_escmd)) { - spin_unlock(&chip->lock); dev_dbg(chip->card->dev, "IRQ_NONE\n"); return IRQ_NONE; /* this device did not cause the interrupt */ } if (irqsrc & MASK_SYS_STATUS_CMD_DONE) - goto exit; + return IRQ_HANDLED; if (irqsrc & MASK_SYS_STATUS_EOBI) dev_dbg(chip->card->dev, "interrupt: EOBI\n"); @@ -1202,27 +1108,8 @@ irqreturn_t lx_interrupt(int irq, void *dev_id) dev_dbg(chip->card->dev, "interrupt: ORUN\n"); if (async_pending) { - u64 notified_in_pipe_mask = 0; - u64 notified_out_pipe_mask = 0; - int freq_changed; - int err; - - /* handle async events */ - err = lx_interrupt_handle_async_events(chip, irqsrc, - &freq_changed, - ¬ified_in_pipe_mask, - ¬ified_out_pipe_mask); - if (err) - dev_err(chip->card->dev, - "error handling async events\n"); - - err = lx_interrupt_handle_audio_transfer(chip, - notified_in_pipe_mask, - notified_out_pipe_mask - ); - if (err) - dev_err(chip->card->dev, - "error during audio transfer\n"); + wake_thread = true; + chip->irqsrc = irqsrc; } if (async_escmd) { @@ -1235,9 +1122,50 @@ irqreturn_t lx_interrupt(int irq, void *dev_id) dev_dbg(chip->card->dev, "interrupt requests escmd handling\n"); } -exit: - spin_unlock(&chip->lock); - return IRQ_HANDLED; /* this device caused the interrupt */ + return wake_thread ? IRQ_WAKE_THREAD : IRQ_HANDLED; +} + +irqreturn_t lx_threaded_irq(int irq, void *dev_id) +{ + struct lx6464es *chip = dev_id; + u64 notified_in_pipe_mask = 0; + u64 notified_out_pipe_mask = 0; + int freq_changed; + int err; + + /* handle async events */ + err = lx_interrupt_handle_async_events(chip, chip->irqsrc, + &freq_changed, + ¬ified_in_pipe_mask, + ¬ified_out_pipe_mask); + if (err) + dev_err(chip->card->dev, "error handling async events\n"); + + if (notified_in_pipe_mask) { + struct lx_stream *lx_stream = &chip->capture_stream; + + dev_dbg(chip->card->dev, + "requesting audio transfer for capture\n"); + err = lx_interrupt_request_new_buffer(chip, lx_stream); + if (err < 0) + dev_err(chip->card->dev, + "cannot request new buffer for capture\n"); + snd_pcm_period_elapsed(lx_stream->stream); + } + + if (notified_out_pipe_mask) { + struct lx_stream *lx_stream = &chip->playback_stream; + + dev_dbg(chip->card->dev, + "requesting audio transfer for playback\n"); + err = lx_interrupt_request_new_buffer(chip, lx_stream); + if (err < 0) + dev_err(chip->card->dev, + "cannot request new buffer for playback\n"); + snd_pcm_period_elapsed(lx_stream->stream); + } + + return IRQ_HANDLED; } diff --git a/sound/pci/lx6464es/lx_core.h b/sound/pci/lx6464es/lx_core.h index 5ec5e04..0cc140c 100644 --- a/sound/pci/lx6464es/lx_core.h +++ b/sound/pci/lx6464es/lx_core.h @@ -181,12 +181,10 @@ int lx_level_peaks(struct lx6464es *chip, int is_capture, int channels, /* interrupt handling */ irqreturn_t lx_interrupt(int irq, void *dev_id); +irqreturn_t lx_threaded_irq(int irq, void *dev_id); void lx_irq_enable(struct lx6464es *chip); void lx_irq_disable(struct lx6464es *chip); -void lx_tasklet_capture(unsigned long data); -void lx_tasklet_playback(unsigned long data); - /* Stream Format Header Defines (for LIN and IEEE754) */ #define HEADER_FMT_BASE HEADER_FMT_BASE_LIN diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c index 75fc342..1faf47e 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -986,6 +986,7 @@ static int snd_mixart_pcm_analog(struct snd_mixart *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops); pcm->info_flags = 0; + pcm->nonatomic = true; strcpy(pcm->name, name); preallocate_buffers(chip, pcm); @@ -1018,6 +1019,7 @@ static int snd_mixart_pcm_digital(struct snd_mixart *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops); pcm->info_flags = 0; + pcm->nonatomic = true; strcpy(pcm->name, name); preallocate_buffers(chip, pcm); @@ -1303,8 +1305,9 @@ static int snd_mixart_probe(struct pci_dev *pci, } } - if (request_irq(pci->irq, snd_mixart_interrupt, IRQF_SHARED, - KBUILD_MODNAME, mgr)) { + if (request_threaded_irq(pci->irq, snd_mixart_interrupt, + snd_mixart_threaded_irq, IRQF_SHARED, + KBUILD_MODNAME, mgr)) { dev_err(&pci->dev, "unable to grab IRQ %d\n", pci->irq); snd_mixart_free(mgr); return -EBUSY; @@ -1314,24 +1317,18 @@ static int snd_mixart_probe(struct pci_dev *pci, sprintf(mgr->shortname, "Digigram miXart"); sprintf(mgr->longname, "%s at 0x%lx & 0x%lx, irq %i", mgr->shortname, mgr->mem[0].phys, mgr->mem[1].phys, mgr->irq); - /* ISR spinlock */ - spin_lock_init(&mgr->lock); - /* init mailbox */ mgr->msg_fifo_readptr = 0; mgr->msg_fifo_writeptr = 0; - spin_lock_init(&mgr->msg_lock); - mutex_init(&mgr->msg_mutex); + mutex_init(&mgr->lock); + mutex_init(&mgr->msg_lock); init_waitqueue_head(&mgr->msg_sleep); atomic_set(&mgr->msg_processed, 0); /* init setup mutex*/ mutex_init(&mgr->setup_mutex); - /* init message taslket */ - tasklet_init(&mgr->msg_taskq, snd_mixart_msg_tasklet, (unsigned long) mgr); - /* card assignment */ mgr->num_cards = MIXART_MAX_CARDS; /* 4 FIXME: configurable? */ for (i = 0; i < mgr->num_cards; i++) { diff --git a/sound/pci/mixart/mixart.h b/sound/pci/mixart/mixart.h index 561634d..0cc17e0 100644 --- a/sound/pci/mixart/mixart.h +++ b/sound/pci/mixart/mixart.h @@ -78,22 +78,18 @@ struct mixart_mgr { char shortname[32]; /* short name of this soundcard */ char longname[80]; /* name of this soundcard */ - /* message tasklet */ - struct tasklet_struct msg_taskq; - /* one and only blocking message or notification may be pending */ u32 pending_event; wait_queue_head_t msg_sleep; - /* messages stored for tasklet */ + /* messages fifo */ u32 msg_fifo[MSG_FIFO_SIZE]; int msg_fifo_readptr; int msg_fifo_writeptr; atomic_t msg_processed; /* number of messages to be processed in takslet */ - spinlock_t lock; /* interrupt spinlock */ - spinlock_t msg_lock; /* mailbox spinlock */ - struct mutex msg_mutex; /* mutex for blocking_requests */ + struct mutex lock; /* interrupt lock */ + struct mutex msg_lock; /* mailbox lock */ struct mutex setup_mutex; /* mutex used in hw_params, open and close */ diff --git a/sound/pci/mixart/mixart_core.c b/sound/pci/mixart/mixart_core.c index 84f6745..fe80313 100644 --- a/sound/pci/mixart/mixart_core.c +++ b/sound/pci/mixart/mixart_core.c @@ -76,7 +76,6 @@ static int retrieve_msg_frame(struct mixart_mgr *mgr, u32 *msg_frame) static int get_msg(struct mixart_mgr *mgr, struct mixart_msg *resp, u32 msg_frame_address ) { - unsigned long flags; u32 headptr; u32 size; int err; @@ -84,7 +83,7 @@ static int get_msg(struct mixart_mgr *mgr, struct mixart_msg *resp, unsigned int i; #endif - spin_lock_irqsave(&mgr->msg_lock, flags); + mutex_lock(&mgr->msg_lock); err = 0; /* copy message descriptor from miXart to driver */ @@ -133,7 +132,7 @@ static int get_msg(struct mixart_mgr *mgr, struct mixart_msg *resp, writel_be(headptr, MIXART_MEM(mgr, MSG_OUTBOUND_FREE_HEAD)); _clean_exit: - spin_unlock_irqrestore(&mgr->msg_lock, flags); + mutex_unlock(&mgr->msg_lock); return err; } @@ -243,28 +242,24 @@ int snd_mixart_send_msg(struct mixart_mgr *mgr, struct mixart_msg *request, int wait_queue_t wait; long timeout; - mutex_lock(&mgr->msg_mutex); - init_waitqueue_entry(&wait, current); - spin_lock_irq(&mgr->msg_lock); + mutex_lock(&mgr->msg_lock); /* send the message */ err = send_msg(mgr, request, max_resp_size, 1, &msg_frame); /* send and mark the answer pending */ if (err) { - spin_unlock_irq(&mgr->msg_lock); - mutex_unlock(&mgr->msg_mutex); + mutex_unlock(&mgr->msg_lock); return err; } set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&mgr->msg_sleep, &wait); - spin_unlock_irq(&mgr->msg_lock); + mutex_unlock(&mgr->msg_lock); timeout = schedule_timeout(MSG_TIMEOUT_JIFFIES); remove_wait_queue(&mgr->msg_sleep, &wait); if (! timeout) { /* error - no ack */ - mutex_unlock(&mgr->msg_mutex); dev_err(&mgr->pci->dev, "error: no response on msg %x\n", msg_frame); return -EIO; @@ -281,7 +276,6 @@ int snd_mixart_send_msg(struct mixart_mgr *mgr, struct mixart_msg *request, int if( request->message_id != resp.message_id ) dev_err(&mgr->pci->dev, "RESPONSE ERROR!\n"); - mutex_unlock(&mgr->msg_mutex); return err; } @@ -300,34 +294,29 @@ int snd_mixart_send_msg_wait_notif(struct mixart_mgr *mgr, if (snd_BUG_ON(notif_event & MSG_CANCEL_NOTIFY_MASK)) return -EINVAL; - mutex_lock(&mgr->msg_mutex); - init_waitqueue_entry(&wait, current); - spin_lock_irq(&mgr->msg_lock); + mutex_lock(&mgr->msg_lock); /* send the message */ err = send_msg(mgr, request, MSG_DEFAULT_SIZE, 1, ¬if_event); /* send and mark the notification event pending */ if(err) { - spin_unlock_irq(&mgr->msg_lock); - mutex_unlock(&mgr->msg_mutex); + mutex_unlock(&mgr->msg_lock); return err; } set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&mgr->msg_sleep, &wait); - spin_unlock_irq(&mgr->msg_lock); + mutex_unlock(&mgr->msg_lock); timeout = schedule_timeout(MSG_TIMEOUT_JIFFIES); remove_wait_queue(&mgr->msg_sleep, &wait); if (! timeout) { /* error - no ack */ - mutex_unlock(&mgr->msg_mutex); dev_err(&mgr->pci->dev, "error: notification %x not received\n", notif_event); return -EIO; } - mutex_unlock(&mgr->msg_mutex); return 0; } @@ -335,13 +324,12 @@ int snd_mixart_send_msg_wait_notif(struct mixart_mgr *mgr, int snd_mixart_send_msg_nonblock(struct mixart_mgr *mgr, struct mixart_msg *request) { u32 message_frame; - unsigned long flags; int err; /* just send the message (do not mark it as a pending one) */ - spin_lock_irqsave(&mgr->msg_lock, flags); + mutex_lock(&mgr->msg_lock); err = send_msg(mgr, request, MSG_DEFAULT_SIZE, 0, &message_frame); - spin_unlock_irqrestore(&mgr->msg_lock, flags); + mutex_unlock(&mgr->msg_lock); /* the answer will be handled by snd_struct mixart_msgasklet() */ atomic_inc(&mgr->msg_processed); @@ -350,19 +338,16 @@ int snd_mixart_send_msg_nonblock(struct mixart_mgr *mgr, struct mixart_msg *requ } -/* common buffer of tasklet and interrupt to send/receive messages */ +/* common buffer of interrupt to send/receive messages */ static u32 mixart_msg_data[MSG_DEFAULT_SIZE / 4]; -void snd_mixart_msg_tasklet(unsigned long arg) +static void snd_mixart_process_msg(struct mixart_mgr *mgr) { - struct mixart_mgr *mgr = ( struct mixart_mgr*)(arg); struct mixart_msg resp; u32 msg, addr, type; int err; - spin_lock(&mgr->lock); - while (mgr->msg_fifo_readptr != mgr->msg_fifo_writeptr) { msg = mgr->msg_fifo[mgr->msg_fifo_readptr]; mgr->msg_fifo_readptr++; @@ -381,7 +366,7 @@ void snd_mixart_msg_tasklet(unsigned long arg) err = get_msg(mgr, &resp, addr); if( err < 0 ) { dev_err(&mgr->pci->dev, - "tasklet: error(%d) reading mf %x\n", + "error(%d) reading mf %x\n", err, msg); break; } @@ -393,12 +378,12 @@ void snd_mixart_msg_tasklet(unsigned long arg) case MSG_STREAM_STOP_OUTPUT_STAGE_PACKET: if(mixart_msg_data[0]) dev_err(&mgr->pci->dev, - "tasklet : error MSG_STREAM_ST***_***PUT_STAGE_PACKET status=%x\n", + "error MSG_STREAM_ST***_***PUT_STAGE_PACKET status=%x\n", mixart_msg_data[0]); break; default: dev_dbg(&mgr->pci->dev, - "tasklet received mf(%x) : msg_id(%x) uid(%x, %x) size(%zd)\n", + "received mf(%x) : msg_id(%x) uid(%x, %x) size(%zd)\n", msg, resp.message_id, resp.uid.object_id, resp.uid.desc, resp.size); break; } @@ -409,7 +394,7 @@ void snd_mixart_msg_tasklet(unsigned long arg) /* get_msg() necessary */ default: dev_err(&mgr->pci->dev, - "tasklet doesn't know what to do with message %x\n", + "doesn't know what to do with message %x\n", msg); } /* switch type */ @@ -417,26 +402,17 @@ void snd_mixart_msg_tasklet(unsigned long arg) atomic_dec(&mgr->msg_processed); } /* while there is a msg in fifo */ - - spin_unlock(&mgr->lock); } irqreturn_t snd_mixart_interrupt(int irq, void *dev_id) { struct mixart_mgr *mgr = dev_id; - int err; - struct mixart_msg resp; - - u32 msg; u32 it_reg; - spin_lock(&mgr->lock); - it_reg = readl_le(MIXART_REG(mgr, MIXART_PCI_OMISR_OFFSET)); if( !(it_reg & MIXART_OIDI) ) { /* this device did not cause the interrupt */ - spin_unlock(&mgr->lock); return IRQ_NONE; } @@ -450,6 +426,17 @@ irqreturn_t snd_mixart_interrupt(int irq, void *dev_id) /* clear interrupt */ writel_le( MIXART_OIDI, MIXART_REG(mgr, MIXART_PCI_OMISR_OFFSET) ); + return IRQ_WAKE_THREAD; +} + +irqreturn_t snd_mixart_threaded_irq(int irq, void *dev_id) +{ + struct mixart_mgr *mgr = dev_id; + int err; + struct mixart_msg resp; + u32 msg; + + mutex_lock(&mgr->lock); /* process interrupt */ while (retrieve_msg_frame(mgr, &msg)) { @@ -518,9 +505,9 @@ irqreturn_t snd_mixart_interrupt(int irq, void *dev_id) stream->buf_period_frag = (u32)( sample_count - stream->abs_period_elapsed ); if(elapsed) { - spin_unlock(&mgr->lock); + mutex_unlock(&mgr->lock); snd_pcm_period_elapsed(stream->substream); - spin_lock(&mgr->lock); + mutex_lock(&mgr->lock); } } } @@ -556,7 +543,7 @@ irqreturn_t snd_mixart_interrupt(int irq, void *dev_id) /* no break, continue ! */ case MSG_TYPE_ANSWER: /* answer or notification to a message we are waiting for*/ - spin_lock(&mgr->msg_lock); + mutex_lock(&mgr->msg_lock); if( (msg & ~MSG_TYPE_MASK) == mgr->pending_event ) { wake_up(&mgr->msg_sleep); mgr->pending_event = 0; @@ -566,9 +553,9 @@ irqreturn_t snd_mixart_interrupt(int irq, void *dev_id) mgr->msg_fifo[mgr->msg_fifo_writeptr] = msg; mgr->msg_fifo_writeptr++; mgr->msg_fifo_writeptr %= MSG_FIFO_SIZE; - tasklet_schedule(&mgr->msg_taskq); + snd_mixart_process_msg(mgr); } - spin_unlock(&mgr->msg_lock); + mutex_unlock(&mgr->msg_lock); break; case MSG_TYPE_REQUEST: default: @@ -582,7 +569,7 @@ irqreturn_t snd_mixart_interrupt(int irq, void *dev_id) /* allow interrupt again */ writel_le( MIXART_ALLOW_OUTBOUND_DOORBELL, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET)); - spin_unlock(&mgr->lock); + mutex_unlock(&mgr->lock); return IRQ_HANDLED; } diff --git a/sound/pci/mixart/mixart_core.h b/sound/pci/mixart/mixart_core.h index c919b73..d1722e5 100644 --- a/sound/pci/mixart/mixart_core.h +++ b/sound/pci/mixart/mixart_core.h @@ -564,7 +564,7 @@ int snd_mixart_send_msg_wait_notif(struct mixart_mgr *mgr, struct mixart_msg *r int snd_mixart_send_msg_nonblock(struct mixart_mgr *mgr, struct mixart_msg *request); irqreturn_t snd_mixart_interrupt(int irq, void *dev_id); -void snd_mixart_msg_tasklet(unsigned long arg); +irqreturn_t snd_mixart_threaded_irq(int irq, void *dev_id); void snd_mixart_reset_board(struct mixart_mgr *mgr); diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c index cc0bcd9..0282824 100644 --- a/sound/pci/oxygen/oxygen_pcm.c +++ b/sound/pci/oxygen/oxygen_pcm.c @@ -29,6 +29,9 @@ /* the multichannel DMA channel has a 24-bit counter */ #define BUFFER_BYTES_MAX_MULTICH ((1 << 24) * 4) +#define FIFO_BYTES 256 +#define FIFO_BYTES_MULTICH 1024 + #define PERIOD_BYTES_MIN 64 #define DEFAULT_BUFFER_BYTES (BUFFER_BYTES_MAX / 2) @@ -60,6 +63,7 @@ static const struct snd_pcm_hardware oxygen_stereo_hardware = { .period_bytes_max = BUFFER_BYTES_MAX, .periods_min = 1, .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN, + .fifo_size = FIFO_BYTES, }; static const struct snd_pcm_hardware oxygen_multichannel_hardware = { .info = SNDRV_PCM_INFO_MMAP | @@ -87,6 +91,7 @@ static const struct snd_pcm_hardware oxygen_multichannel_hardware = { .period_bytes_max = BUFFER_BYTES_MAX_MULTICH, .periods_min = 1, .periods_max = BUFFER_BYTES_MAX_MULTICH / PERIOD_BYTES_MIN, + .fifo_size = FIFO_BYTES_MULTICH, }; static const struct snd_pcm_hardware oxygen_ac97_hardware = { .info = SNDRV_PCM_INFO_MMAP | @@ -106,6 +111,7 @@ static const struct snd_pcm_hardware oxygen_ac97_hardware = { .period_bytes_max = BUFFER_BYTES_MAX, .periods_min = 1, .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN, + .fifo_size = FIFO_BYTES, }; static const struct snd_pcm_hardware *const oxygen_hardware[PCM_COUNT] = { @@ -141,6 +147,10 @@ static int oxygen_open(struct snd_pcm_substream *substream, runtime->hw.rates &= ~(SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_64000); runtime->hw.rate_min = 44100; + /* fall through */ + case PCM_A: + case PCM_B: + runtime->hw.fifo_size = 0; break; case PCM_MULTICH: runtime->hw.channels_max = chip->model.dac_channels_pcm; diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c index 7b317a2..83de6fb 100644 --- a/sound/pci/oxygen/virtuoso.c +++ b/sound/pci/oxygen/virtuoso.c @@ -52,6 +52,7 @@ static const struct pci_device_id xonar_ids[] = { { OXYGEN_PCI_SUBID(0x1043, 0x835d) }, { OXYGEN_PCI_SUBID(0x1043, 0x835e) }, { OXYGEN_PCI_SUBID(0x1043, 0x838e) }, + { OXYGEN_PCI_SUBID(0x1043, 0x8428) }, { OXYGEN_PCI_SUBID(0x1043, 0x8522) }, { OXYGEN_PCI_SUBID(0x1043, 0x85f4) }, { OXYGEN_PCI_SUBID_BROKEN_EEPROM }, diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c index e026059..24109d3 100644 --- a/sound/pci/oxygen/xonar_pcm179x.c +++ b/sound/pci/oxygen/xonar_pcm179x.c @@ -212,6 +212,9 @@ #define GPIO_ST_MAGIC 0x0040 #define GPIO_ST_HP 0x0080 +#define GPIO_XENSE_OUTPUT_ENABLE (0x0001 | 0x0010 | 0x0020) +#define GPIO_XENSE_SPEAKERS 0x0080 + #define I2C_DEVICE_PCM1796(i) (0x98 + ((i) << 1)) /* 10011, ii, /W=0 */ #define I2C_DEVICE_CS2000 0x9c /* 100111, 0, /W=0 */ @@ -419,6 +422,7 @@ static void xonar_st_init_common(struct oxygen *chip) data->generic.output_enable_bit = GPIO_ST_OUTPUT_ENABLE; data->dacs = chip->model.dac_channels_mixer / 2; + data->h6 = chip->model.dac_channels_mixer > 2; data->hp_gain_offset = 2*-18; pcm1796_init(chip); @@ -499,6 +503,51 @@ static void xonar_stx_init(struct oxygen *chip) xonar_st_init_common(chip); } +static void xonar_xense_init(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; + + data->generic.ext_power_reg = OXYGEN_GPI_DATA; + data->generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK; + data->generic.ext_power_bit = GPI_EXT_POWER; + xonar_init_ext_power(chip); + + data->generic.anti_pop_delay = 100; + data->has_cs2000 = 1; + data->cs2000_regs[CS2000_FUN_CFG_1] = CS2000_REF_CLK_DIV_1; + + oxygen_write16(chip, OXYGEN_I2S_A_FORMAT, + OXYGEN_RATE_48000 | + OXYGEN_I2S_FORMAT_I2S | + OXYGEN_I2S_MCLK(MCLK_512) | + OXYGEN_I2S_BITS_16 | + OXYGEN_I2S_MASTER | + OXYGEN_I2S_BCLK_64); + + xonar_st_init_i2c(chip); + cs2000_registers_init(chip); + + data->generic.output_enable_bit = GPIO_XENSE_OUTPUT_ENABLE; + data->dacs = 1; + data->hp_gain_offset = 2*-18; + + pcm1796_init(chip); + + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, + GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR | + GPIO_ST_MAGIC | GPIO_XENSE_SPEAKERS); + oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, + GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR | + GPIO_XENSE_SPEAKERS); + + xonar_init_cs53x1(chip); + xonar_enable_output(chip); + + snd_component_add(chip->card, "PCM1796"); + snd_component_add(chip->card, "CS5381"); + snd_component_add(chip->card, "CS2000"); +} + static void xonar_d2_cleanup(struct oxygen *chip) { xonar_disable_output(chip); @@ -795,11 +844,11 @@ static int st_output_switch_put(struct snd_kcontrol *ctl, static int st_hp_volume_offset_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) { - static const char *const names[3] = { - "< 64 ohms", "64-300 ohms", "300-600 ohms" + static const char *const names[4] = { + "< 32 ohms", "32-64 ohms", "64-300 ohms", "300-600 ohms" }; - return snd_ctl_enum_info(info, 1, 3, names); + return snd_ctl_enum_info(info, 1, 4, names); } static int st_hp_volume_offset_get(struct snd_kcontrol *ctl, @@ -809,12 +858,14 @@ static int st_hp_volume_offset_get(struct snd_kcontrol *ctl, struct xonar_pcm179x *data = chip->model_data; mutex_lock(&chip->mutex); - if (data->hp_gain_offset < 2*-6) + if (data->hp_gain_offset < 2*-12) value->value.enumerated.item[0] = 0; - else if (data->hp_gain_offset < 0) + else if (data->hp_gain_offset < 2*-6) value->value.enumerated.item[0] = 1; - else + else if (data->hp_gain_offset < 0) value->value.enumerated.item[0] = 2; + else + value->value.enumerated.item[0] = 3; mutex_unlock(&chip->mutex); return 0; } @@ -823,13 +874,13 @@ static int st_hp_volume_offset_get(struct snd_kcontrol *ctl, static int st_hp_volume_offset_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) { - static const s8 offsets[] = { 2*-18, 2*-6, 0 }; + static const s8 offsets[] = { 2*-18, 2*-12, 2*-6, 0 }; struct oxygen *chip = ctl->private_data; struct xonar_pcm179x *data = chip->model_data; s8 offset; int changed; - if (value->value.enumerated.item[0] > 2) + if (value->value.enumerated.item[0] > 3) return -EINVAL; offset = offsets[value->value.enumerated.item[0]]; mutex_lock(&chip->mutex); @@ -859,6 +910,67 @@ static const struct snd_kcontrol_new st_controls[] = { }, }; +static int xense_output_switch_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u16 gpio; + + gpio = oxygen_read16(chip, OXYGEN_GPIO_DATA); + if (gpio & GPIO_XENSE_SPEAKERS) + value->value.enumerated.item[0] = 0; + else if (!(gpio & GPIO_XENSE_SPEAKERS) && (gpio & GPIO_ST_HP_REAR)) + value->value.enumerated.item[0] = 1; + else + value->value.enumerated.item[0] = 2; + return 0; +} + +static int xense_output_switch_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_pcm179x *data = chip->model_data; + u16 gpio_old, gpio; + + mutex_lock(&chip->mutex); + gpio_old = oxygen_read16(chip, OXYGEN_GPIO_DATA); + gpio = gpio_old; + switch (value->value.enumerated.item[0]) { + case 0: + gpio |= GPIO_XENSE_SPEAKERS | GPIO_ST_HP_REAR; + break; + case 1: + gpio = (gpio | GPIO_ST_HP_REAR) & ~GPIO_XENSE_SPEAKERS; + break; + case 2: + gpio &= ~(GPIO_XENSE_SPEAKERS | GPIO_ST_HP_REAR); + break; + } + oxygen_write16(chip, OXYGEN_GPIO_DATA, gpio); + data->hp_active = !(gpio & GPIO_XENSE_SPEAKERS); + update_pcm1796_volume(chip); + mutex_unlock(&chip->mutex); + return gpio != gpio_old; +} + +static const struct snd_kcontrol_new xense_controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Output", + .info = st_output_switch_info, + .get = xense_output_switch_get, + .put = xense_output_switch_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Headphones Impedance Playback Enum", + .info = st_hp_volume_offset_info, + .get = st_hp_volume_offset_get, + .put = st_hp_volume_offset_put, + }, +}; + static void xonar_line_mic_ac97_switch(struct oxygen *chip, unsigned int reg, unsigned int mute) { @@ -946,6 +1058,23 @@ static int xonar_st_mixer_init(struct oxygen *chip) return 0; } +static int xonar_xense_mixer_init(struct oxygen *chip) +{ + unsigned int i; + int err; + + for (i = 0; i < ARRAY_SIZE(xense_controls); ++i) { + err = snd_ctl_add(chip->card, + snd_ctl_new1(&xense_controls[i], chip)); + if (err < 0) + return err; + } + err = add_pcm1796_controls(chip); + if (err < 0) + return err; + return 0; +} + static void dump_pcm1796_registers(struct oxygen *chip, struct snd_info_buffer *buffer) { @@ -1140,12 +1269,29 @@ int get_xonar_pcm179x_model(struct oxygen *chip, break; case 0x85f4: chip->model = model_xonar_st; - /* TODO: daughterboard support */ - chip->model.shortname = "Xonar STX II"; + oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK); + switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DB_MASK) { + default: + chip->model.shortname = "Xonar STX II"; + break; + case GPIO_DB_H6: + chip->model.shortname = "Xonar STX II+H6"; + chip->model.dac_channels_pcm = 8; + chip->model.dac_channels_mixer = 8; + chip->model.dac_mclks = OXYGEN_MCLKS(256, 128, 128); + break; + } chip->model.init = xonar_stx_init; chip->model.resume = xonar_stx_resume; chip->model.set_dac_params = set_pcm1796_params; break; + case 0x8428: + chip->model = model_xonar_st; + chip->model.shortname = "Xonar Xense"; + chip->model.chip = "AV100"; + chip->model.init = xonar_xense_init; + chip->model.mixer_init = xonar_xense_mixer_init; + break; default: return -EINVAL; } diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c index 68a37a7..b854fc5 100644 --- a/sound/pci/pcxhr/pcxhr.c +++ b/sound/pci/pcxhr/pcxhr.c @@ -702,13 +702,11 @@ static inline int pcxhr_stream_scheduled_get_pipe(struct pcxhr_stream *stream, return 0; } -static void pcxhr_trigger_tasklet(unsigned long arg) +static void pcxhr_start_linked_stream(struct pcxhr_mgr *mgr) { - unsigned long flags; int i, j, err; struct pcxhr_pipe *pipe; struct snd_pcxhr *chip; - struct pcxhr_mgr *mgr = (struct pcxhr_mgr*)(arg); int capture_mask = 0; int playback_mask = 0; @@ -736,11 +734,11 @@ static void pcxhr_trigger_tasklet(unsigned long arg) } if (capture_mask == 0 && playback_mask == 0) { mutex_unlock(&mgr->setup_mutex); - dev_err(&mgr->pci->dev, "pcxhr_trigger_tasklet : no pipes\n"); + dev_err(&mgr->pci->dev, "pcxhr_start_linked_stream : no pipes\n"); return; } - dev_dbg(&mgr->pci->dev, "pcxhr_trigger_tasklet : " + dev_dbg(&mgr->pci->dev, "pcxhr_start_linked_stream : " "playback_mask=%x capture_mask=%x\n", playback_mask, capture_mask); @@ -748,7 +746,7 @@ static void pcxhr_trigger_tasklet(unsigned long arg) err = pcxhr_set_pipe_state(mgr, playback_mask, capture_mask, 0); if (err) { mutex_unlock(&mgr->setup_mutex); - dev_err(&mgr->pci->dev, "pcxhr_trigger_tasklet : " + dev_err(&mgr->pci->dev, "pcxhr_start_linked_stream : " "error stop pipes (P%x C%x)\n", playback_mask, capture_mask); return; @@ -793,7 +791,7 @@ static void pcxhr_trigger_tasklet(unsigned long arg) err = pcxhr_set_pipe_state(mgr, playback_mask, capture_mask, 1); if (err) { mutex_unlock(&mgr->setup_mutex); - dev_err(&mgr->pci->dev, "pcxhr_trigger_tasklet : " + dev_err(&mgr->pci->dev, "pcxhr_start_linked_stream : " "error start pipes (P%x C%x)\n", playback_mask, capture_mask); return; @@ -802,7 +800,7 @@ static void pcxhr_trigger_tasklet(unsigned long arg) /* put the streams into the running state now * (increment pointer by interrupt) */ - spin_lock_irqsave(&mgr->lock, flags); + mutex_lock(&mgr->lock); for ( i =0; i < mgr->num_cards; i++) { struct pcxhr_stream *stream; chip = mgr->chip[i]; @@ -820,13 +818,13 @@ static void pcxhr_trigger_tasklet(unsigned long arg) } } } - spin_unlock_irqrestore(&mgr->lock, flags); + mutex_unlock(&mgr->lock); mutex_unlock(&mgr->setup_mutex); #ifdef CONFIG_SND_DEBUG_VERBOSE do_gettimeofday(&my_tv2); - dev_dbg(&mgr->pci->dev, "***TRIGGER TASKLET*** TIME = %ld (err = %x)\n", + dev_dbg(&mgr->pci->dev, "***TRIGGER START*** TIME = %ld (err = %x)\n", (long)(my_tv2.tv_usec - my_tv1.tv_usec), err); #endif } @@ -853,7 +851,7 @@ static int pcxhr_trigger(struct snd_pcm_substream *subs, int cmd) PCXHR_STREAM_STATUS_SCHEDULE_RUN; snd_pcm_trigger_done(s, subs); } - tasklet_schedule(&chip->mgr->trigger_taskq); + pcxhr_start_linked_stream(chip->mgr); } else { stream = subs->runtime->private_data; snd_printdd("Only one Substream %c %d\n", @@ -1127,20 +1125,19 @@ static int pcxhr_close(struct snd_pcm_substream *subs) static snd_pcm_uframes_t pcxhr_stream_pointer(struct snd_pcm_substream *subs) { - unsigned long flags; u_int32_t timer_period_frag; int timer_buf_periods; struct snd_pcxhr *chip = snd_pcm_substream_chip(subs); struct snd_pcm_runtime *runtime = subs->runtime; struct pcxhr_stream *stream = runtime->private_data; - spin_lock_irqsave(&chip->mgr->lock, flags); + mutex_lock(&chip->mgr->lock); /* get the period fragment and the nb of periods in the buffer */ timer_period_frag = stream->timer_period_frag; timer_buf_periods = stream->timer_buf_periods; - spin_unlock_irqrestore(&chip->mgr->lock, flags); + mutex_unlock(&chip->mgr->lock); return (snd_pcm_uframes_t)((timer_buf_periods * runtime->period_size) + timer_period_frag); @@ -1181,6 +1178,7 @@ int pcxhr_create_pcm(struct snd_pcxhr *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcxhr_ops); pcm->info_flags = 0; + pcm->nonatomic = true; strcpy(pcm->name, name); snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1588,8 +1586,9 @@ static int pcxhr_probe(struct pci_dev *pci, mgr->pci = pci; mgr->irq = -1; - if (request_irq(pci->irq, pcxhr_interrupt, IRQF_SHARED, - KBUILD_MODNAME, mgr)) { + if (request_threaded_irq(pci->irq, pcxhr_interrupt, + pcxhr_threaded_irq, IRQF_SHARED, + KBUILD_MODNAME, mgr)) { dev_err(&pci->dev, "unable to grab IRQ %d\n", pci->irq); pcxhr_free(mgr); return -EBUSY; @@ -1601,19 +1600,13 @@ static int pcxhr_probe(struct pci_dev *pci, mgr->shortname, mgr->port[0], mgr->port[1], mgr->port[2], mgr->irq); - /* ISR spinlock */ - spin_lock_init(&mgr->lock); - spin_lock_init(&mgr->msg_lock); + /* ISR lock */ + mutex_init(&mgr->lock); + mutex_init(&mgr->msg_lock); /* init setup mutex*/ mutex_init(&mgr->setup_mutex); - /* init taslket */ - tasklet_init(&mgr->msg_taskq, pcxhr_msg_tasklet, - (unsigned long) mgr); - tasklet_init(&mgr->trigger_taskq, pcxhr_trigger_tasklet, - (unsigned long) mgr); - mgr->prmh = kmalloc(sizeof(*mgr->prmh) + sizeof(u32) * (PCXHR_SIZE_MAX_LONG_STATUS - PCXHR_SIZE_MAX_STATUS), diff --git a/sound/pci/pcxhr/pcxhr.h b/sound/pci/pcxhr/pcxhr.h index a4c602c..9e39e50 100644 --- a/sound/pci/pcxhr/pcxhr.h +++ b/sound/pci/pcxhr/pcxhr.h @@ -78,14 +78,10 @@ struct pcxhr_mgr { char shortname[32]; /* short name of this soundcard */ char longname[96]; /* name of this soundcard */ - /* message tasklet */ - struct tasklet_struct msg_taskq; struct pcxhr_rmh *prmh; - /* trigger tasklet */ - struct tasklet_struct trigger_taskq; - spinlock_t lock; /* interrupt spinlock */ - spinlock_t msg_lock; /* message spinlock */ + struct mutex lock; /* interrupt lock */ + struct mutex msg_lock; /* message lock */ struct mutex setup_mutex; /* mutex used in hw_params, open and close */ struct mutex mixer_mutex; /* mutex for mixer */ diff --git a/sound/pci/pcxhr/pcxhr_core.c b/sound/pci/pcxhr/pcxhr_core.c index df93719..a584acb 100644 --- a/sound/pci/pcxhr/pcxhr_core.c +++ b/sound/pci/pcxhr/pcxhr_core.c @@ -767,11 +767,11 @@ void pcxhr_set_pipe_cmd_params(struct pcxhr_rmh *rmh, int capture, */ int pcxhr_send_msg(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh) { - unsigned long flags; int err; - spin_lock_irqsave(&mgr->msg_lock, flags); + + mutex_lock(&mgr->msg_lock); err = pcxhr_send_msg_nolock(mgr, rmh); - spin_unlock_irqrestore(&mgr->msg_lock, flags); + mutex_unlock(&mgr->msg_lock); return err; } @@ -971,17 +971,16 @@ int pcxhr_write_io_num_reg_cont(struct pcxhr_mgr *mgr, unsigned int mask, unsigned int value, int *changed) { struct pcxhr_rmh rmh; - unsigned long flags; int err; - spin_lock_irqsave(&mgr->msg_lock, flags); + mutex_lock(&mgr->msg_lock); if ((mgr->io_num_reg_cont & mask) == value) { dev_dbg(&mgr->pci->dev, "IO_NUM_REG_CONT mask %x already is set to %x\n", mask, value); if (changed) *changed = 0; - spin_unlock_irqrestore(&mgr->msg_lock, flags); + mutex_unlock(&mgr->msg_lock); return 0; /* already programmed */ } pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE); @@ -996,7 +995,7 @@ int pcxhr_write_io_num_reg_cont(struct pcxhr_mgr *mgr, unsigned int mask, if (changed) *changed = 1; } - spin_unlock_irqrestore(&mgr->msg_lock, flags); + mutex_unlock(&mgr->msg_lock); return err; } @@ -1043,22 +1042,21 @@ static int pcxhr_handle_async_err(struct pcxhr_mgr *mgr, u32 err, } -void pcxhr_msg_tasklet(unsigned long arg) +static void pcxhr_msg_thread(struct pcxhr_mgr *mgr) { - struct pcxhr_mgr *mgr = (struct pcxhr_mgr *)(arg); struct pcxhr_rmh *prmh = mgr->prmh; int err; int i, j; if (mgr->src_it_dsp & PCXHR_IRQ_FREQ_CHANGE) dev_dbg(&mgr->pci->dev, - "TASKLET : PCXHR_IRQ_FREQ_CHANGE event occurred\n"); + "PCXHR_IRQ_FREQ_CHANGE event occurred\n"); if (mgr->src_it_dsp & PCXHR_IRQ_TIME_CODE) dev_dbg(&mgr->pci->dev, - "TASKLET : PCXHR_IRQ_TIME_CODE event occurred\n"); + "PCXHR_IRQ_TIME_CODE event occurred\n"); if (mgr->src_it_dsp & PCXHR_IRQ_NOTIFY) dev_dbg(&mgr->pci->dev, - "TASKLET : PCXHR_IRQ_NOTIFY event occurred\n"); + "PCXHR_IRQ_NOTIFY event occurred\n"); if (mgr->src_it_dsp & (PCXHR_IRQ_FREQ_CHANGE | PCXHR_IRQ_TIME_CODE)) { /* clear events FREQ_CHANGE and TIME_CODE */ pcxhr_init_rmh(prmh, CMD_TEST_IT); @@ -1068,7 +1066,7 @@ void pcxhr_msg_tasklet(unsigned long arg) } if (mgr->src_it_dsp & PCXHR_IRQ_ASYNC) { dev_dbg(&mgr->pci->dev, - "TASKLET : PCXHR_IRQ_ASYNC event occurred\n"); + "PCXHR_IRQ_ASYNC event occurred\n"); pcxhr_init_rmh(prmh, CMD_ASYNC); prmh->cmd[0] |= 1; /* add SEL_ASYNC_EVENTS */ @@ -1076,7 +1074,7 @@ void pcxhr_msg_tasklet(unsigned long arg) prmh->stat_len = PCXHR_SIZE_MAX_LONG_STATUS; err = pcxhr_send_msg(mgr, prmh); if (err) - dev_err(&mgr->pci->dev, "ERROR pcxhr_msg_tasklet=%x;\n", + dev_err(&mgr->pci->dev, "ERROR pcxhr_msg_thread=%x;\n", err); i = 1; while (i < prmh->stat_len) { @@ -1220,9 +1218,9 @@ static void pcxhr_update_timer_pos(struct pcxhr_mgr *mgr, } if (elapsed) { - spin_unlock(&mgr->lock); + mutex_unlock(&mgr->lock); snd_pcm_period_elapsed(stream->substream); - spin_lock(&mgr->lock); + mutex_lock(&mgr->lock); } } } @@ -1231,14 +1229,10 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id) { struct pcxhr_mgr *mgr = dev_id; unsigned int reg; - int i, j; - struct snd_pcxhr *chip; - - spin_lock(&mgr->lock); + bool wake_thread = false; reg = PCXHR_INPL(mgr, PCXHR_PLX_IRQCS); if (! (reg & PCXHR_IRQCS_ACTIVE_PCIDB)) { - spin_unlock(&mgr->lock); /* this device did not cause the interrupt */ return IRQ_NONE; } @@ -1250,6 +1244,44 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id) /* timer irq occurred */ if (reg & PCXHR_IRQ_TIMER) { int timer_toggle = reg & PCXHR_IRQ_TIMER; + if (timer_toggle == mgr->timer_toggle) { + dev_dbg(&mgr->pci->dev, "ERROR TIMER TOGGLE\n"); + mgr->dsp_time_err++; + } + + mgr->timer_toggle = timer_toggle; + mgr->src_it_dsp = reg; + wake_thread = true; + } + + /* other irq's handled in the thread */ + if (reg & PCXHR_IRQ_MASK) { + if (reg & PCXHR_IRQ_ASYNC) { + /* as we didn't request any async notifications, + * some kind of xrun error will probably occurred + */ + /* better resynchronize all streams next interrupt : */ + mgr->dsp_time_last = PCXHR_DSP_TIME_INVALID; + } + mgr->src_it_dsp = reg; + wake_thread = true; + } +#ifdef CONFIG_SND_DEBUG_VERBOSE + if (reg & PCXHR_FATAL_DSP_ERR) + dev_dbg(&mgr->pci->dev, "FATAL DSP ERROR : %x\n", reg); +#endif + + return wake_thread ? IRQ_WAKE_THREAD : IRQ_HANDLED; +} + +irqreturn_t pcxhr_threaded_irq(int irq, void *dev_id) +{ + struct pcxhr_mgr *mgr = dev_id; + int i, j; + struct snd_pcxhr *chip; + + mutex_lock(&mgr->lock); + if (mgr->src_it_dsp & PCXHR_IRQ_TIMER) { /* is a 24 bit counter */ int dsp_time_new = PCXHR_INPL(mgr, PCXHR_PLX_MBOX4) & PCXHR_DSP_TIME_MASK; @@ -1290,13 +1322,6 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id) #endif mgr->dsp_time_last = dsp_time_new; - if (timer_toggle == mgr->timer_toggle) { - dev_dbg(&mgr->pci->dev, "ERROR TIMER TOGGLE\n"); - mgr->dsp_time_err++; - } - mgr->timer_toggle = timer_toggle; - - reg &= ~PCXHR_IRQ_TIMER; for (i = 0; i < mgr->num_cards; i++) { chip = mgr->chip[i]; for (j = 0; j < chip->nb_streams_capt; j++) @@ -1312,22 +1337,7 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id) dsp_time_diff); } } - /* other irq's handled in the tasklet */ - if (reg & PCXHR_IRQ_MASK) { - if (reg & PCXHR_IRQ_ASYNC) { - /* as we didn't request any async notifications, - * some kind of xrun error will probably occurred - */ - /* better resynchronize all streams next interrupt : */ - mgr->dsp_time_last = PCXHR_DSP_TIME_INVALID; - } - mgr->src_it_dsp = reg; - tasklet_schedule(&mgr->msg_taskq); - } -#ifdef CONFIG_SND_DEBUG_VERBOSE - if (reg & PCXHR_FATAL_DSP_ERR) - dev_dbg(&mgr->pci->dev, "FATAL DSP ERROR : %x\n", reg); -#endif - spin_unlock(&mgr->lock); - return IRQ_HANDLED; /* this device caused the interrupt */ + + pcxhr_msg_thread(mgr); + return IRQ_HANDLED; } diff --git a/sound/pci/pcxhr/pcxhr_core.h b/sound/pci/pcxhr/pcxhr_core.h index a81ab6b..dc267e4 100644 --- a/sound/pci/pcxhr/pcxhr_core.h +++ b/sound/pci/pcxhr/pcxhr_core.h @@ -200,6 +200,6 @@ int pcxhr_write_io_num_reg_cont(struct pcxhr_mgr *mgr, unsigned int mask, /* interrupt handling */ irqreturn_t pcxhr_interrupt(int irq, void *dev_id); -void pcxhr_msg_tasklet(unsigned long arg); +irqreturn_t pcxhr_threaded_irq(int irq, void *dev_id); #endif /* __SOUND_PCXHR_CORE_H */ diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c index 3dc4732..c5a25e3 100644 --- a/sound/pci/vx222/vx222.c +++ b/sound/pci/vx222/vx222.c @@ -168,8 +168,9 @@ static int snd_vx222_create(struct snd_card *card, struct pci_dev *pci, for (i = 0; i < 2; i++) vx->port[i] = pci_resource_start(pci, i + 1); - if (request_irq(pci->irq, snd_vx_irq_handler, IRQF_SHARED, - KBUILD_MODNAME, chip)) { + if (request_threaded_irq(pci->irq, snd_vx_irq_handler, + snd_vx_threaded_irq_handler, IRQF_SHARED, + KBUILD_MODNAME, chip)) { dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq); snd_vx222_free(chip); return -EBUSY; diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf.c b/sound/pcmcia/pdaudiocf/pdaudiocf.c index 56bda12..07f4b33 100644 --- a/sound/pcmcia/pdaudiocf/pdaudiocf.c +++ b/sound/pcmcia/pdaudiocf/pdaudiocf.c @@ -61,6 +61,7 @@ static void snd_pdacf_detach(struct pcmcia_device *p_dev); static void pdacf_release(struct pcmcia_device *link) { + free_irq(link->irq, link->priv); pcmcia_disable_device(link); } @@ -220,11 +221,13 @@ static int pdacf_config(struct pcmcia_device *link) ret = pcmcia_request_io(link); if (ret) - goto failed; + goto failed_preirq; - ret = pcmcia_request_irq(link, pdacf_interrupt); + ret = request_threaded_irq(link->irq, pdacf_interrupt, + pdacf_threaded_irq, + IRQF_SHARED, link->devname, link->priv); if (ret) - goto failed; + goto failed_preirq; ret = pcmcia_enable_device(link); if (ret) @@ -236,7 +239,9 @@ static int pdacf_config(struct pcmcia_device *link) return 0; -failed: + failed: + free_irq(link->irq, link->priv); +failed_preirq: pcmcia_disable_device(link); return -ENODEV; } diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf.h b/sound/pcmcia/pdaudiocf/pdaudiocf.h index ea41e57..e9a7d3a 100644 --- a/sound/pcmcia/pdaudiocf/pdaudiocf.h +++ b/sound/pcmcia/pdaudiocf/pdaudiocf.h @@ -88,10 +88,9 @@ struct snd_pdacf { unsigned long port; int irq; - spinlock_t reg_lock; + struct mutex reg_lock; unsigned short regmap[8]; unsigned short suspend_reg_scr; - struct tasklet_struct tq; spinlock_t ak4117_lock; struct ak4117 *ak4117; @@ -136,7 +135,7 @@ int snd_pdacf_resume(struct snd_pdacf *chip); #endif int snd_pdacf_pcm_new(struct snd_pdacf *chip); irqreturn_t pdacf_interrupt(int irq, void *dev); -void pdacf_tasklet(unsigned long private_data); +irqreturn_t pdacf_threaded_irq(int irq, void *dev); void pdacf_reinit(struct snd_pdacf *chip, int resume); #endif /* __PDAUDIOCF_H */ diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_core.c b/sound/pcmcia/pdaudiocf/pdaudiocf_core.c index ea0adfb..d724ab0 100644 --- a/sound/pcmcia/pdaudiocf/pdaudiocf_core.c +++ b/sound/pcmcia/pdaudiocf/pdaudiocf_core.c @@ -162,9 +162,8 @@ struct snd_pdacf *snd_pdacf_create(struct snd_card *card) if (chip == NULL) return NULL; chip->card = card; - spin_lock_init(&chip->reg_lock); + mutex_init(&chip->reg_lock); spin_lock_init(&chip->ak4117_lock); - tasklet_init(&chip->tq, pdacf_tasklet, (unsigned long)chip); card->private_data = chip; pdacf_proc_init(chip); @@ -174,19 +173,18 @@ struct snd_pdacf *snd_pdacf_create(struct snd_card *card) static void snd_pdacf_ak4117_change(struct ak4117 *ak4117, unsigned char c0, unsigned char c1) { struct snd_pdacf *chip = ak4117->change_callback_private; - unsigned long flags; u16 val; if (!(c0 & AK4117_UNLCK)) return; - spin_lock_irqsave(&chip->reg_lock, flags); + mutex_lock(&chip->reg_lock); val = chip->regmap[PDAUDIOCF_REG_SCR>>1]; if (ak4117->rcs0 & AK4117_UNLCK) val |= PDAUDIOCF_BLUE_LED_OFF; else val &= ~PDAUDIOCF_BLUE_LED_OFF; pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); - spin_unlock_irqrestore(&chip->reg_lock, flags); + mutex_unlock(&chip->reg_lock); } int snd_pdacf_ak4117_create(struct snd_pdacf *chip) diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c b/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c index dcd3220..ecf0fbd 100644 --- a/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c +++ b/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c @@ -30,6 +30,7 @@ irqreturn_t pdacf_interrupt(int irq, void *dev) { struct snd_pdacf *chip = dev; unsigned short stat; + bool wake_thread = false; if ((chip->chip_status & (PDAUDIOCF_STAT_IS_STALE| PDAUDIOCF_STAT_IS_CONFIGURED| @@ -41,13 +42,13 @@ irqreturn_t pdacf_interrupt(int irq, void *dev) if (stat & PDAUDIOCF_IRQOVR) /* should never happen */ snd_printk(KERN_ERR "PDAUDIOCF SRAM buffer overrun detected!\n"); if (chip->pcm_substream) - tasklet_schedule(&chip->tq); + wake_thread = true; if (!(stat & PDAUDIOCF_IRQAKM)) stat |= PDAUDIOCF_IRQAKM; /* check rate */ } if (get_irq_regs() != NULL) snd_ak4117_check_rate_and_errors(chip->ak4117, 0); - return IRQ_HANDLED; + return wake_thread ? IRQ_WAKE_THREAD : IRQ_HANDLED; } static inline void pdacf_transfer_mono16(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port) @@ -256,16 +257,16 @@ static void pdacf_transfer(struct snd_pdacf *chip, unsigned int size, unsigned i } } -void pdacf_tasklet(unsigned long private_data) +irqreturn_t pdacf_threaded_irq(int irq, void *dev) { - struct snd_pdacf *chip = (struct snd_pdacf *) private_data; + struct snd_pdacf *chip = dev; int size, off, cont, rdp, wdp; if ((chip->chip_status & (PDAUDIOCF_STAT_IS_STALE|PDAUDIOCF_STAT_IS_CONFIGURED)) != PDAUDIOCF_STAT_IS_CONFIGURED) - return; + return IRQ_HANDLED; if (chip->pcm_substream == NULL || chip->pcm_substream->runtime == NULL || !snd_pcm_running(chip->pcm_substream)) - return; + return IRQ_HANDLED; rdp = inw(chip->port + PDAUDIOCF_REG_RDP); wdp = inw(chip->port + PDAUDIOCF_REG_WDP); @@ -311,15 +312,15 @@ void pdacf_tasklet(unsigned long private_data) size -= cont; } #endif - spin_lock(&chip->reg_lock); + mutex_lock(&chip->reg_lock); while (chip->pcm_tdone >= chip->pcm_period) { chip->pcm_hwptr += chip->pcm_period; chip->pcm_hwptr %= chip->pcm_size; chip->pcm_tdone -= chip->pcm_period; - spin_unlock(&chip->reg_lock); + mutex_unlock(&chip->reg_lock); snd_pcm_period_elapsed(chip->pcm_substream); - spin_lock(&chip->reg_lock); + mutex_lock(&chip->reg_lock); } - spin_unlock(&chip->reg_lock); - /* printk(KERN_DEBUG "TASKLET: end\n"); */ + mutex_unlock(&chip->reg_lock); + return IRQ_HANDLED; } diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c b/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c index 43f995a..b48aa0a 100644 --- a/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c +++ b/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c @@ -77,7 +77,7 @@ static int pdacf_pcm_trigger(struct snd_pcm_substream *subs, int cmd) default: return -EINVAL; } - spin_lock(&chip->reg_lock); + mutex_lock(&chip->reg_lock); chip->pcm_running += inc; tmp = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); if (chip->pcm_running) { @@ -91,7 +91,7 @@ static int pdacf_pcm_trigger(struct snd_pcm_substream *subs, int cmd) tmp |= val; pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, tmp); __end: - spin_unlock(&chip->reg_lock); + mutex_unlock(&chip->reg_lock); snd_ak4117_check_rate_and_errors(chip->ak4117, AK4117_CHECK_NO_RATE); return ret; } @@ -296,6 +296,7 @@ int snd_pdacf_pcm_new(struct snd_pdacf *chip) pcm->private_data = chip; pcm->info_flags = 0; + pcm->nonatomic = true; strcpy(pcm->name, chip->card->shortname); chip->pcm = pcm; diff --git a/sound/pcmcia/vx/vxp_ops.c b/sound/pcmcia/vx/vxp_ops.c index fe33e12..2819729 100644 --- a/sound/pcmcia/vx/vxp_ops.c +++ b/sound/pcmcia/vx/vxp_ops.c @@ -468,12 +468,11 @@ static void vxp_write_codec_reg(struct vx_core *chip, int codec, unsigned int da void vx_set_mic_boost(struct vx_core *chip, int boost) { struct snd_vxpocket *pchip = (struct snd_vxpocket *)chip; - unsigned long flags; if (chip->chip_status & VX_STAT_IS_STALE) return; - spin_lock_irqsave(&chip->lock, flags); + mutex_lock(&chip->lock); if (pchip->regCDSP & P24_CDSP_MICS_SEL_MASK) { if (boost) { /* boost: 38 dB */ @@ -486,7 +485,7 @@ void vx_set_mic_boost(struct vx_core *chip, int boost) } vx_outb(chip, CDSP, pchip->regCDSP); } - spin_unlock_irqrestore(&chip->lock, flags); + mutex_unlock(&chip->lock); } /* @@ -511,17 +510,16 @@ static int vx_compute_mic_level(int level) void vx_set_mic_level(struct vx_core *chip, int level) { struct snd_vxpocket *pchip = (struct snd_vxpocket *)chip; - unsigned long flags; if (chip->chip_status & VX_STAT_IS_STALE) return; - spin_lock_irqsave(&chip->lock, flags); + mutex_lock(&chip->lock); if (pchip->regCDSP & VXP_CDSP_MIC_SEL_MASK) { level = vx_compute_mic_level(level); vx_outb(chip, MICRO, level); } - spin_unlock_irqrestore(&chip->lock, flags); + mutex_unlock(&chip->lock); } diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c index 786e7e1..92ec114 100644 --- a/sound/pcmcia/vx/vxpocket.c +++ b/sound/pcmcia/vx/vxpocket.c @@ -62,6 +62,7 @@ static unsigned int card_alloc; */ static void vxpocket_release(struct pcmcia_device *link) { + free_irq(link->irq, link->priv); pcmcia_disable_device(link); } @@ -227,11 +228,13 @@ static int vxpocket_config(struct pcmcia_device *link) ret = pcmcia_request_io(link); if (ret) - goto failed; + goto failed_preirq; - ret = pcmcia_request_irq(link, snd_vx_irq_handler); + ret = request_threaded_irq(link->irq, snd_vx_irq_handler, + snd_vx_threaded_irq_handler, + IRQF_SHARED, link->devname, link->priv); if (ret) - goto failed; + goto failed_preirq; ret = pcmcia_enable_device(link); if (ret) @@ -245,7 +248,9 @@ static int vxpocket_config(struct pcmcia_device *link) return 0; -failed: + failed: + free_irq(link->irq, link->priv); +failed_preirq: pcmcia_disable_device(link); return -ENODEV; } diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c index 922006d..e88a6b6 100644 --- a/sound/soc/codecs/88pm860x-codec.c +++ b/sound/soc/codecs/88pm860x-codec.c @@ -146,7 +146,7 @@ struct pm860x_priv { struct pm860x_det det; int irq[4]; - unsigned char name[4][MAX_NAME_LEN]; + unsigned char name[4][MAX_NAME_LEN+1]; }; /* -9450dB to 0dB in 150dB steps ( mute instead of -9450dB) */ @@ -1337,8 +1337,6 @@ static int pm860x_probe(struct snd_soc_codec *codec) } } - pm860x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; out: @@ -1354,7 +1352,6 @@ static int pm860x_remove(struct snd_soc_codec *codec) for (i = 3; i >= 0; i--) free_irq(pm860x->irq[i], pm860x); - pm860x_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 8838838e..a68d173 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -43,6 +43,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_ALC5623 if I2C select SND_SOC_ALC5632 if I2C select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC + select SND_SOC_CS35L32 if I2C select SND_SOC_CS42L51_I2C if I2C select SND_SOC_CS42L52 if I2C && INPUT select SND_SOC_CS42L56 if I2C && INPUT @@ -56,7 +57,10 @@ config SND_SOC_ALL_CODECS select SND_SOC_DA7213 if I2C select SND_SOC_DA732X if I2C select SND_SOC_DA9055 if I2C + select SND_SOC_DMIC select SND_SOC_BT_SCO + select SND_SOC_ES8328_SPI if SPI_MASTER + select SND_SOC_ES8328_I2C if I2C select SND_SOC_ISABELLE if I2C select SND_SOC_JZ4740_CODEC select SND_SOC_LM4857 if I2C @@ -90,6 +94,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_SSM2518 if I2C select SND_SOC_SSM2602_SPI if SPI_MASTER select SND_SOC_SSM2602_I2C if I2C + select SND_SOC_SSM4567 if I2C select SND_SOC_STA32X if I2C select SND_SOC_STA350 if I2C select SND_SOC_STA529 if I2C @@ -323,6 +328,10 @@ config SND_SOC_ALC5632 config SND_SOC_CQ0093VC tristate +config SND_SOC_CS35L32 + tristate "Cirrus Logic CS35L32 CODEC" + depends on I2C + config SND_SOC_CS42L51 tristate @@ -405,6 +414,17 @@ config SND_SOC_DMIC config SND_SOC_HDMI_CODEC tristate "HDMI stub CODEC" +config SND_SOC_ES8328 + tristate "Everest Semi ES8328 CODEC" + +config SND_SOC_ES8328_I2C + tristate + select SND_SOC_ES8328 + +config SND_SOC_ES8328_SPI + tristate + select SND_SOC_ES8328 + config SND_SOC_ISABELLE tristate @@ -464,6 +484,7 @@ config SND_SOC_RL6231 config SND_SOC_RT286 tristate + depends on I2C config SND_SOC_RT5631 tristate @@ -520,12 +541,20 @@ config SND_SOC_SSM2602 tristate config SND_SOC_SSM2602_SPI + tristate "Analog Devices SSM2602 CODEC - SPI" + depends on SPI_MASTER select SND_SOC_SSM2602 - tristate + select REGMAP_SPI config SND_SOC_SSM2602_I2C + tristate "Analog Devices SSM2602 CODEC - I2C" + depends on I2C select SND_SOC_SSM2602 - tristate + select REGMAP_I2C + +config SND_SOC_SSM4567 + tristate "Analog Devices ssm4567 amplifier driver support" + depends on I2C config SND_SOC_STA32X tristate @@ -712,7 +741,8 @@ config SND_SOC_WM8974 tristate config SND_SOC_WM8978 - tristate + tristate "Wolfson Microelectronics WM8978 codec" + depends on I2C config SND_SOC_WM8983 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 20afe0f..5dce451 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -32,6 +32,7 @@ snd-soc-ak4671-objs := ak4671.o snd-soc-ak5386-objs := ak5386.o snd-soc-arizona-objs := arizona.o snd-soc-cq93vc-objs := cq93vc.o +snd-soc-cs35l32-objs := cs35l32.o snd-soc-cs42l51-objs := cs42l51.o snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o snd-soc-cs42l52-objs := cs42l52.o @@ -49,6 +50,9 @@ snd-soc-da732x-objs := da732x.o snd-soc-da9055-objs := da9055.o snd-soc-bt-sco-objs := bt-sco.o snd-soc-dmic-objs := dmic.o +snd-soc-es8328-objs := es8328.o +snd-soc-es8328-i2c-objs := es8328-i2c.o +snd-soc-es8328-spi-objs := es8328-spi.o snd-soc-isabelle-objs := isabelle.o snd-soc-jz4740-codec-objs := jz4740.o snd-soc-l3-objs := l3.o @@ -91,6 +95,7 @@ snd-soc-ssm2518-objs := ssm2518.o snd-soc-ssm2602-objs := ssm2602.o snd-soc-ssm2602-spi-objs := ssm2602-spi.o snd-soc-ssm2602-i2c-objs := ssm2602-i2c.o +snd-soc-ssm4567-objs := ssm4567.o snd-soc-sta32x-objs := sta32x.o snd-soc-sta350-objs := sta350.o snd-soc-sta529-objs := sta529.o @@ -203,6 +208,7 @@ obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o obj-$(CONFIG_SND_SOC_ALC5632) += snd-soc-alc5632.o obj-$(CONFIG_SND_SOC_ARIZONA) += snd-soc-arizona.o obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o +obj-$(CONFIG_SND_SOC_CS35L32) += snd-soc-cs35l32.o obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o @@ -220,6 +226,9 @@ obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o +obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o +obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o +obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o @@ -258,6 +267,7 @@ obj-$(CONFIG_SND_SOC_SSM2518) += snd-soc-ssm2518.o obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o obj-$(CONFIG_SND_SOC_SSM2602_SPI) += snd-soc-ssm2602-spi.o obj-$(CONFIG_SND_SOC_SSM2602_I2C) += snd-soc-ssm2602-i2c.o +obj-$(CONFIG_SND_SOC_SSM4567) += snd-soc-ssm4567.o obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o obj-$(CONFIG_SND_SOC_STA350) += snd-soc-sta350.o obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index 1fb4402..fd43827 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -56,8 +56,7 @@ #define GPIO31_DIR_OUTPUT 0x40 /* Macrocell register definitions */ -#define AB8500_CTRL3_REG 0x0200 -#define AB8500_GPIO_DIR4_REG 0x1013 +#define AB8500_GPIO_DIR4_REG 0x13 /* Bank AB8500_MISC */ /* Nr of FIR/IIR-coeff banks in ANC-block */ #define AB8500_NR_OF_ANC_COEFF_BANKS 2 @@ -126,6 +125,8 @@ struct ab8500_codec_drvdata_dbg { /* Private data for AB8500 device-driver */ struct ab8500_codec_drvdata { + struct regmap *regmap; + /* Sidetone */ long *sid_fir_values; enum sid_state sid_status; @@ -166,49 +167,35 @@ static inline const char *amic_type_str(enum amic_type type) */ /* Read a register from the audio-bank of AB8500 */ -static unsigned int ab8500_codec_read_reg(struct snd_soc_codec *codec, - unsigned int reg) +static int ab8500_codec_read_reg(void *context, unsigned int reg, + unsigned int *value) { + struct device *dev = context; int status; - unsigned int value = 0; u8 value8; - status = abx500_get_register_interruptible(codec->dev, AB8500_AUDIO, - reg, &value8); - if (status < 0) { - dev_err(codec->dev, - "%s: ERROR: Register (0x%02x:0x%02x) read failed (%d).\n", - __func__, (u8)AB8500_AUDIO, (u8)reg, status); - } else { - dev_dbg(codec->dev, - "%s: Read 0x%02x from register 0x%02x:0x%02x\n", - __func__, value8, (u8)AB8500_AUDIO, (u8)reg); - value = (unsigned int)value8; - } + status = abx500_get_register_interruptible(dev, AB8500_AUDIO, + reg, &value8); + *value = (unsigned int)value8; - return value; + return status; } /* Write to a register in the audio-bank of AB8500 */ -static int ab8500_codec_write_reg(struct snd_soc_codec *codec, - unsigned int reg, unsigned int value) +static int ab8500_codec_write_reg(void *context, unsigned int reg, + unsigned int value) { - int status; - - status = abx500_set_register_interruptible(codec->dev, AB8500_AUDIO, - reg, value); - if (status < 0) - dev_err(codec->dev, - "%s: ERROR: Register (%02x:%02x) write failed (%d).\n", - __func__, (u8)AB8500_AUDIO, (u8)reg, status); - else - dev_dbg(codec->dev, - "%s: Wrote 0x%02x into register %02x:%02x\n", - __func__, (u8)value, (u8)AB8500_AUDIO, (u8)reg); + struct device *dev = context; - return status; + return abx500_set_register_interruptible(dev, AB8500_AUDIO, + reg, value); } +static const struct regmap_config ab8500_codec_regmap = { + .reg_read = ab8500_codec_read_reg, + .reg_write = ab8500_codec_write_reg, +}; + /* * Controls - DAPM */ @@ -1968,16 +1955,16 @@ static int ab8500_audio_setup_mics(struct snd_soc_codec *codec, dev_dbg(codec->dev, "%s: Enter.\n", __func__); /* Set DMic-clocks to outputs */ - status = abx500_get_register_interruptible(codec->dev, (u8)AB8500_MISC, - (u8)AB8500_GPIO_DIR4_REG, + status = abx500_get_register_interruptible(codec->dev, AB8500_MISC, + AB8500_GPIO_DIR4_REG, &value8); if (status < 0) return status; value = value8 | GPIO27_DIR_OUTPUT | GPIO29_DIR_OUTPUT | GPIO31_DIR_OUTPUT; status = abx500_set_register_interruptible(codec->dev, - (u8)AB8500_MISC, - (u8)AB8500_GPIO_DIR4_REG, + AB8500_MISC, + AB8500_GPIO_DIR4_REG, value); if (status < 0) return status; @@ -2565,9 +2552,6 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec) static struct snd_soc_codec_driver ab8500_codec_driver = { .probe = ab8500_codec_probe, - .read = ab8500_codec_read_reg, - .write = ab8500_codec_write_reg, - .reg_word_size = sizeof(u8), .controls = ab8500_ctrls, .num_controls = ARRAY_SIZE(ab8500_ctrls), .dapm_widgets = ab8500_dapm_widgets, @@ -2592,6 +2576,15 @@ static int ab8500_codec_driver_probe(struct platform_device *pdev) drvdata->anc_status = ANC_UNCONFIGURED; dev_set_drvdata(&pdev->dev, drvdata); + drvdata->regmap = devm_regmap_init(&pdev->dev, NULL, &pdev->dev, + &ab8500_codec_regmap); + if (IS_ERR(drvdata->regmap)) { + status = PTR_ERR(drvdata->regmap); + dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n", + __func__, status); + return status; + } + dev_dbg(&pdev->dev, "%s: Register codec.\n", __func__); status = snd_soc_register_codec(&pdev->dev, &ab8500_codec_driver, ab8500_codec_dai, diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c index e889e1b..bd9b183 100644 --- a/sound/soc/codecs/ac97.c +++ b/sound/soc/codecs/ac97.c @@ -69,19 +69,6 @@ static struct snd_soc_dai_driver ac97_dai = { .ops = &ac97_dai_ops, }; -static unsigned int ac97_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - return soc_ac97_ops->read(codec->ac97, reg); -} - -static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int val) -{ - soc_ac97_ops->write(codec->ac97, reg, val); - return 0; -} - static int ac97_soc_probe(struct snd_soc_codec *codec) { struct snd_ac97_bus *ac97_bus; @@ -122,8 +109,6 @@ static int ac97_soc_resume(struct snd_soc_codec *codec) #endif static struct snd_soc_codec_driver soc_codec_dev_ac97 = { - .write = ac97_write, - .read = ac97_read, .probe = ac97_soc_probe, .suspend = ac97_soc_suspend, .resume = ac97_soc_resume, diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c index 1ff7d4d..7c784ad 100644 --- a/sound/soc/codecs/adau1373.c +++ b/sound/soc/codecs/adau1373.c @@ -1448,29 +1448,10 @@ static int adau1373_set_bias_level(struct snd_soc_codec *codec, return 0; } -static int adau1373_remove(struct snd_soc_codec *codec) -{ - adau1373_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int adau1373_suspend(struct snd_soc_codec *codec) -{ - struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec); - int ret; - - ret = adau1373_set_bias_level(codec, SND_SOC_BIAS_OFF); - regcache_cache_only(adau1373->regmap, true); - - return ret; -} - static int adau1373_resume(struct snd_soc_codec *codec) { struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec); - regcache_cache_only(adau1373->regmap, false); - adau1373_set_bias_level(codec, SND_SOC_BIAS_STANDBY); regcache_sync(adau1373->regmap); return 0; @@ -1501,8 +1482,6 @@ static const struct regmap_config adau1373_regmap_config = { static struct snd_soc_codec_driver adau1373_codec_driver = { .probe = adau1373_probe, - .remove = adau1373_remove, - .suspend = adau1373_suspend, .resume = adau1373_resume, .set_bias_level = adau1373_set_bias_level, .idle_bias_off = true, diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c index 848cab8..5518ebd 100644 --- a/sound/soc/codecs/adau1761.c +++ b/sound/soc/codecs/adau1761.c @@ -714,9 +714,9 @@ static int adau1761_codec_probe(struct snd_soc_codec *codec) static const struct snd_soc_codec_driver adau1761_codec_driver = { .probe = adau1761_codec_probe, - .suspend = adau17x1_suspend, .resume = adau17x1_resume, .set_bias_level = adau1761_set_bias_level, + .suspend_bias_off = true, .controls = adau1761_controls, .num_controls = ARRAY_SIZE(adau1761_controls), diff --git a/sound/soc/codecs/adau1781.c b/sound/soc/codecs/adau1781.c index 045a614..e9fc00f 100644 --- a/sound/soc/codecs/adau1781.c +++ b/sound/soc/codecs/adau1781.c @@ -446,9 +446,9 @@ static int adau1781_codec_probe(struct snd_soc_codec *codec) static const struct snd_soc_codec_driver adau1781_codec_driver = { .probe = adau1781_codec_probe, - .suspend = adau17x1_suspend, .resume = adau17x1_resume, .set_bias_level = adau1781_set_bias_level, + .suspend_bias_off = true, .controls = adau1781_controls, .num_controls = ARRAY_SIZE(adau1781_controls), diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c index 0b65970..3e16c1c 100644 --- a/sound/soc/codecs/adau17x1.c +++ b/sound/soc/codecs/adau17x1.c @@ -815,13 +815,6 @@ int adau17x1_add_routes(struct snd_soc_codec *codec) } EXPORT_SYMBOL_GPL(adau17x1_add_routes); -int adau17x1_suspend(struct snd_soc_codec *codec) -{ - codec->driver->set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} -EXPORT_SYMBOL_GPL(adau17x1_suspend); - int adau17x1_resume(struct snd_soc_codec *codec) { struct adau *adau = snd_soc_codec_get_drvdata(codec); @@ -829,7 +822,6 @@ int adau17x1_resume(struct snd_soc_codec *codec) if (adau->switch_mode) adau->switch_mode(codec->dev); - codec->driver->set_bias_level(codec, SND_SOC_BIAS_STANDBY); regcache_sync(adau->regmap); return 0; diff --git a/sound/soc/codecs/adau17x1.h b/sound/soc/codecs/adau17x1.h index 3ffabaf..e4a557f 100644 --- a/sound/soc/codecs/adau17x1.h +++ b/sound/soc/codecs/adau17x1.h @@ -52,7 +52,6 @@ int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec, enum adau17x1_micbias_voltage micbias); bool adau17x1_readable_register(struct device *dev, unsigned int reg); bool adau17x1_volatile_register(struct device *dev, unsigned int reg); -int adau17x1_suspend(struct snd_soc_codec *codec); int adau17x1_resume(struct snd_soc_codec *codec); extern const struct snd_soc_dai_ops adau17x1_dai_ops; diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c index c43b93f..ce3cdca 100644 --- a/sound/soc/codecs/adav80x.c +++ b/sound/soc/codecs/adav80x.c @@ -812,42 +812,23 @@ static int adav80x_probe(struct snd_soc_codec *codec) /* Disable DAC zero flag */ regmap_write(adav80x->regmap, ADAV80X_DAC_CTRL3, 0x6); - return adav80x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); -} - -static int adav80x_suspend(struct snd_soc_codec *codec) -{ - struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); - int ret; - - ret = adav80x_set_bias_level(codec, SND_SOC_BIAS_OFF); - regcache_cache_only(adav80x->regmap, true); - - return ret; + return 0; } static int adav80x_resume(struct snd_soc_codec *codec) { struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); - regcache_cache_only(adav80x->regmap, false); - adav80x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); regcache_sync(adav80x->regmap); return 0; } -static int adav80x_remove(struct snd_soc_codec *codec) -{ - return adav80x_set_bias_level(codec, SND_SOC_BIAS_OFF); -} - static struct snd_soc_codec_driver adav80x_codec_driver = { .probe = adav80x_probe, - .remove = adav80x_remove, - .suspend = adav80x_suspend, .resume = adav80x_resume, .set_bias_level = adav80x_set_bias_level, + .suspend_bias_off = true, .set_pll = adav80x_set_pll, .set_sysclk = adav80x_set_sysclk, diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c new file mode 100644 index 0000000..c125925 --- /dev/null +++ b/sound/soc/codecs/cs35l32.c @@ -0,0 +1,631 @@ +/* + * cs35l32.c -- CS35L32 ALSA SoC audio driver + * + * Copyright 2014 CirrusLogic, Inc. + * + * Author: Brian Austin <brian.austin@cirrus.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/gpio/consumer.h> +#include <linux/of_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <dt-bindings/sound/cs35l32.h> + +#include "cs35l32.h" + +#define CS35L32_NUM_SUPPLIES 2 +static const char *const cs35l32_supply_names[CS35L32_NUM_SUPPLIES] = { + "VA", + "VP", +}; + +struct cs35l32_private { + struct regmap *regmap; + struct snd_soc_codec *codec; + struct regulator_bulk_data supplies[CS35L32_NUM_SUPPLIES]; + struct cs35l32_platform_data pdata; + struct gpio_desc *reset_gpio; +}; + +static const struct reg_default cs35l32_reg_defaults[] = { + + { 0x06, 0x04 }, /* Power Ctl 1 */ + { 0x07, 0xE8 }, /* Power Ctl 2 */ + { 0x08, 0x40 }, /* Clock Ctl */ + { 0x09, 0x20 }, /* Low Battery Threshold */ + { 0x0A, 0x00 }, /* Voltage Monitor [RO] */ + { 0x0B, 0x40 }, /* Conv Peak Curr Protection CTL */ + { 0x0C, 0x07 }, /* IMON Scaling */ + { 0x0D, 0x03 }, /* Audio/LED Pwr Manager */ + { 0x0F, 0x20 }, /* Serial Port Control */ + { 0x10, 0x14 }, /* Class D Amp CTL */ + { 0x11, 0x00 }, /* Protection Release CTL */ + { 0x12, 0xFF }, /* Interrupt Mask 1 */ + { 0x13, 0xFF }, /* Interrupt Mask 2 */ + { 0x14, 0xFF }, /* Interrupt Mask 3 */ + { 0x19, 0x00 }, /* LED Flash Mode Current */ + { 0x1A, 0x00 }, /* LED Movie Mode Current */ + { 0x1B, 0x20 }, /* LED Flash Timer */ + { 0x1C, 0x00 }, /* LED Flash Inhibit Current */ +}; + +static bool cs35l32_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L32_DEVID_AB: + case CS35L32_DEVID_CD: + case CS35L32_DEVID_E: + case CS35L32_FAB_ID: + case CS35L32_REV_ID: + case CS35L32_PWRCTL1: + case CS35L32_PWRCTL2: + case CS35L32_CLK_CTL: + case CS35L32_BATT_THRESHOLD: + case CS35L32_VMON: + case CS35L32_BST_CPCP_CTL: + case CS35L32_IMON_SCALING: + case CS35L32_AUDIO_LED_MNGR: + case CS35L32_ADSP_CTL: + case CS35L32_CLASSD_CTL: + case CS35L32_PROTECT_CTL: + case CS35L32_INT_MASK_1: + case CS35L32_INT_MASK_2: + case CS35L32_INT_MASK_3: + case CS35L32_INT_STATUS_1: + case CS35L32_INT_STATUS_2: + case CS35L32_INT_STATUS_3: + case CS35L32_LED_STATUS: + case CS35L32_FLASH_MODE: + case CS35L32_MOVIE_MODE: + case CS35L32_FLASH_TIMER: + case CS35L32_FLASH_INHIBIT: + return true; + default: + return false; + } +} + +static bool cs35l32_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L32_DEVID_AB: + case CS35L32_DEVID_CD: + case CS35L32_DEVID_E: + case CS35L32_FAB_ID: + case CS35L32_REV_ID: + case CS35L32_INT_STATUS_1: + case CS35L32_INT_STATUS_2: + case CS35L32_INT_STATUS_3: + case CS35L32_LED_STATUS: + return true; + default: + return false; + } +} + +static bool cs35l32_precious_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L32_INT_STATUS_1: + case CS35L32_INT_STATUS_2: + case CS35L32_INT_STATUS_3: + case CS35L32_LED_STATUS: + return true; + default: + return false; + } +} + +static DECLARE_TLV_DB_SCALE(classd_ctl_tlv, 900, 300, 0); + +static const struct snd_kcontrol_new imon_ctl = + SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 6, 1, 1); + +static const struct snd_kcontrol_new vmon_ctl = + SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 7, 1, 1); + +static const struct snd_kcontrol_new vpmon_ctl = + SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 5, 1, 1); + +static const struct snd_kcontrol_new cs35l32_snd_controls[] = { + SOC_SINGLE_TLV("Speaker Volume", CS35L32_CLASSD_CTL, + 3, 0x04, 1, classd_ctl_tlv), + SOC_SINGLE("Zero Cross Switch", CS35L32_CLASSD_CTL, 2, 1, 0), + SOC_SINGLE("Gain Manager Switch", CS35L32_AUDIO_LED_MNGR, 3, 1, 0), +}; + +static const struct snd_soc_dapm_widget cs35l32_dapm_widgets[] = { + + SND_SOC_DAPM_SUPPLY("BOOST", CS35L32_PWRCTL1, 2, 1, NULL, 0), + SND_SOC_DAPM_OUT_DRV("Speaker", CS35L32_PWRCTL1, 7, 1, NULL, 0), + + SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, CS35L32_PWRCTL2, 3, 1), + + SND_SOC_DAPM_INPUT("VP"), + SND_SOC_DAPM_INPUT("ISENSE"), + SND_SOC_DAPM_INPUT("VSENSE"), + + SND_SOC_DAPM_SWITCH("VMON ADC", CS35L32_PWRCTL2, 7, 1, &vmon_ctl), + SND_SOC_DAPM_SWITCH("IMON ADC", CS35L32_PWRCTL2, 6, 1, &imon_ctl), + SND_SOC_DAPM_SWITCH("VPMON ADC", CS35L32_PWRCTL2, 5, 1, &vpmon_ctl), +}; + +static const struct snd_soc_dapm_route cs35l32_audio_map[] = { + + {"Speaker", NULL, "BOOST"}, + + {"VMON ADC", NULL, "VSENSE"}, + {"IMON ADC", NULL, "ISENSE"}, + {"VPMON ADC", NULL, "VP"}, + + {"SDOUT", "Switch", "VMON ADC"}, + {"SDOUT", "Switch", "IMON ADC"}, + {"SDOUT", "Switch", "VPMON ADC"}, + + {"Capture", NULL, "SDOUT"}, +}; + +static int cs35l32_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + snd_soc_update_bits(codec, CS35L32_ADSP_CTL, + CS35L32_ADSP_MASTER_MASK, + CS35L32_ADSP_MASTER_MASK); + break; + case SND_SOC_DAIFMT_CBS_CFS: + snd_soc_update_bits(codec, CS35L32_ADSP_CTL, + CS35L32_ADSP_MASTER_MASK, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int cs35l32_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct snd_soc_codec *codec = dai->codec; + + return snd_soc_update_bits(codec, CS35L32_PWRCTL2, + CS35L32_SDOUT_3ST, tristate << 3); +} + +static const struct snd_soc_dai_ops cs35l32_ops = { + .set_fmt = cs35l32_set_dai_fmt, + .set_tristate = cs35l32_set_tristate, +}; + +static struct snd_soc_dai_driver cs35l32_dai[] = { + { + .name = "cs35l32-monitor", + .id = 0, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = CS35L32_RATES, + .formats = CS35L32_FORMATS, + }, + .ops = &cs35l32_ops, + .symmetric_rates = 1, + } +}; + +static int cs35l32_codec_set_sysclk(struct snd_soc_codec *codec, + int clk_id, int source, unsigned int freq, int dir) +{ + unsigned int val; + + switch (freq) { + case 6000000: + val = CS35L32_MCLK_RATIO; + break; + case 12000000: + val = CS35L32_MCLK_DIV2_MASK | CS35L32_MCLK_RATIO; + break; + case 6144000: + val = 0; + break; + case 12288000: + val = CS35L32_MCLK_DIV2_MASK; + break; + default: + return -EINVAL; + } + + return snd_soc_update_bits(codec, CS35L32_CLK_CTL, + CS35L32_MCLK_DIV2_MASK | CS35L32_MCLK_RATIO_MASK, val); +} + +static struct snd_soc_codec_driver soc_codec_dev_cs35l32 = { + .set_sysclk = cs35l32_codec_set_sysclk, + + .dapm_widgets = cs35l32_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs35l32_dapm_widgets), + .dapm_routes = cs35l32_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs35l32_audio_map), + + .controls = cs35l32_snd_controls, + .num_controls = ARRAY_SIZE(cs35l32_snd_controls), +}; + +/* Current and threshold powerup sequence Pg37 in datasheet */ +static const struct reg_default cs35l32_monitor_patch[] = { + + { 0x00, 0x99 }, + { 0x48, 0x17 }, + { 0x49, 0x56 }, + { 0x43, 0x01 }, + { 0x3B, 0x62 }, + { 0x3C, 0x80 }, + { 0x00, 0x00 }, +}; + +static struct regmap_config cs35l32_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = CS35L32_MAX_REGISTER, + .reg_defaults = cs35l32_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs35l32_reg_defaults), + .volatile_reg = cs35l32_volatile_register, + .readable_reg = cs35l32_readable_register, + .precious_reg = cs35l32_precious_register, + .cache_type = REGCACHE_RBTREE, +}; + +static int cs35l32_handle_of_data(struct i2c_client *i2c_client, + struct cs35l32_platform_data *pdata) +{ + struct device_node *np = i2c_client->dev.of_node; + unsigned int val; + + if (of_property_read_u32(np, "cirrus,sdout-share", &val) >= 0) + pdata->sdout_share = val; + + of_property_read_u32(np, "cirrus,boost-manager", &val); + switch (val) { + case CS35L32_BOOST_MGR_AUTO: + case CS35L32_BOOST_MGR_AUTO_AUDIO: + case CS35L32_BOOST_MGR_BYPASS: + case CS35L32_BOOST_MGR_FIXED: + pdata->boost_mng = val; + break; + default: + dev_err(&i2c_client->dev, + "Wrong cirrus,boost-manager DT value %d\n", val); + pdata->boost_mng = CS35L32_BOOST_MGR_BYPASS; + } + + of_property_read_u32(np, "cirrus,sdout-datacfg", &val); + switch (val) { + case CS35L32_DATA_CFG_LR_VP: + case CS35L32_DATA_CFG_LR_STAT: + case CS35L32_DATA_CFG_LR: + case CS35L32_DATA_CFG_LR_VPSTAT: + pdata->sdout_datacfg = val; + break; + default: + dev_err(&i2c_client->dev, + "Wrong cirrus,sdout-datacfg DT value %d\n", val); + pdata->sdout_datacfg = CS35L32_DATA_CFG_LR; + } + + of_property_read_u32(np, "cirrus,battery-threshold", &val); + switch (val) { + case CS35L32_BATT_THRESH_3_1V: + case CS35L32_BATT_THRESH_3_2V: + case CS35L32_BATT_THRESH_3_3V: + case CS35L32_BATT_THRESH_3_4V: + pdata->batt_thresh = val; + break; + default: + dev_err(&i2c_client->dev, + "Wrong cirrus,battery-threshold DT value %d\n", val); + pdata->batt_thresh = CS35L32_BATT_THRESH_3_3V; + } + + of_property_read_u32(np, "cirrus,battery-recovery", &val); + switch (val) { + case CS35L32_BATT_RECOV_3_1V: + case CS35L32_BATT_RECOV_3_2V: + case CS35L32_BATT_RECOV_3_3V: + case CS35L32_BATT_RECOV_3_4V: + case CS35L32_BATT_RECOV_3_5V: + case CS35L32_BATT_RECOV_3_6V: + pdata->batt_recov = val; + break; + default: + dev_err(&i2c_client->dev, + "Wrong cirrus,battery-recovery DT value %d\n", val); + pdata->batt_recov = CS35L32_BATT_RECOV_3_4V; + } + + return 0; +} + +static int cs35l32_i2c_probe(struct i2c_client *i2c_client, + const struct i2c_device_id *id) +{ + struct cs35l32_private *cs35l32; + struct cs35l32_platform_data *pdata = + dev_get_platdata(&i2c_client->dev); + int ret, i; + unsigned int devid = 0; + unsigned int reg; + + + cs35l32 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs35l32_private), + GFP_KERNEL); + if (!cs35l32) { + dev_err(&i2c_client->dev, "could not allocate codec\n"); + return -ENOMEM; + } + + i2c_set_clientdata(i2c_client, cs35l32); + + cs35l32->regmap = devm_regmap_init_i2c(i2c_client, &cs35l32_regmap); + if (IS_ERR(cs35l32->regmap)) { + ret = PTR_ERR(cs35l32->regmap); + dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + if (pdata) { + cs35l32->pdata = *pdata; + } else { + pdata = devm_kzalloc(&i2c_client->dev, + sizeof(struct cs35l32_platform_data), + GFP_KERNEL); + if (!pdata) { + dev_err(&i2c_client->dev, "could not allocate pdata\n"); + return -ENOMEM; + } + if (i2c_client->dev.of_node) { + ret = cs35l32_handle_of_data(i2c_client, + &cs35l32->pdata); + if (ret != 0) + return ret; + } + } + + for (i = 0; i < ARRAY_SIZE(cs35l32->supplies); i++) + cs35l32->supplies[i].supply = cs35l32_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c_client->dev, + ARRAY_SIZE(cs35l32->supplies), + cs35l32->supplies); + if (ret != 0) { + dev_err(&i2c_client->dev, + "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(cs35l32->supplies), + cs35l32->supplies); + if (ret != 0) { + dev_err(&i2c_client->dev, + "Failed to enable supplies: %d\n", ret); + return ret; + } + + /* Reset the Device */ + cs35l32->reset_gpio = devm_gpiod_get(&i2c_client->dev, + "reset-gpios"); + if (IS_ERR(cs35l32->reset_gpio)) { + ret = PTR_ERR(cs35l32->reset_gpio); + if (ret != -ENOENT && ret != -ENOSYS) + return ret; + + cs35l32->reset_gpio = NULL; + } else { + ret = gpiod_direction_output(cs35l32->reset_gpio, 0); + if (ret) + return ret; + gpiod_set_value_cansleep(cs35l32->reset_gpio, 1); + } + + /* initialize codec */ + ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_AB, ®); + devid = (reg & 0xFF) << 12; + + ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_CD, ®); + devid |= (reg & 0xFF) << 4; + + ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_E, ®); + devid |= (reg & 0xF0) >> 4; + + if (devid != CS35L32_CHIP_ID) { + ret = -ENODEV; + dev_err(&i2c_client->dev, + "CS35L32 Device ID (%X). Expected %X\n", + devid, CS35L32_CHIP_ID); + return ret; + } + + ret = regmap_read(cs35l32->regmap, CS35L32_REV_ID, ®); + if (ret < 0) { + dev_err(&i2c_client->dev, "Get Revision ID failed\n"); + return ret; + } + + ret = regmap_register_patch(cs35l32->regmap, cs35l32_monitor_patch, + ARRAY_SIZE(cs35l32_monitor_patch)); + if (ret < 0) { + dev_err(&i2c_client->dev, "Failed to apply errata patch\n"); + return ret; + } + + dev_info(&i2c_client->dev, + "Cirrus Logic CS35L32, Revision: %02X\n", reg & 0xFF); + + /* Setup VBOOST Management */ + if (cs35l32->pdata.boost_mng) + regmap_update_bits(cs35l32->regmap, CS35L32_AUDIO_LED_MNGR, + CS35L32_BOOST_MASK, + cs35l32->pdata.boost_mng); + + /* Setup ADSP Format Config */ + if (cs35l32->pdata.sdout_share) + regmap_update_bits(cs35l32->regmap, CS35L32_ADSP_CTL, + CS35L32_ADSP_SHARE_MASK, + cs35l32->pdata.sdout_share << 3); + + /* Setup ADSP Data Configuration */ + if (cs35l32->pdata.sdout_datacfg) + regmap_update_bits(cs35l32->regmap, CS35L32_ADSP_CTL, + CS35L32_ADSP_DATACFG_MASK, + cs35l32->pdata.sdout_datacfg << 4); + + /* Setup Low Battery Recovery */ + if (cs35l32->pdata.batt_recov) + regmap_update_bits(cs35l32->regmap, CS35L32_BATT_THRESHOLD, + CS35L32_BATT_REC_MASK, + cs35l32->pdata.batt_recov << 1); + + /* Setup Low Battery Threshold */ + if (cs35l32->pdata.batt_thresh) + regmap_update_bits(cs35l32->regmap, CS35L32_BATT_THRESHOLD, + CS35L32_BATT_THRESH_MASK, + cs35l32->pdata.batt_thresh << 4); + + /* Power down the AMP */ + regmap_update_bits(cs35l32->regmap, CS35L32_PWRCTL1, CS35L32_PDN_AMP, + CS35L32_PDN_AMP); + + /* Clear MCLK Error Bit since we don't have the clock yet */ + ret = regmap_read(cs35l32->regmap, CS35L32_INT_STATUS_1, ®); + + ret = snd_soc_register_codec(&i2c_client->dev, + &soc_codec_dev_cs35l32, cs35l32_dai, + ARRAY_SIZE(cs35l32_dai)); + if (ret < 0) + goto err_disable; + + return 0; + +err_disable: + regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies), + cs35l32->supplies); + return ret; +} + +static int cs35l32_i2c_remove(struct i2c_client *i2c_client) +{ + struct cs35l32_private *cs35l32 = i2c_get_clientdata(i2c_client); + + snd_soc_unregister_codec(&i2c_client->dev); + + /* Hold down reset */ + if (cs35l32->reset_gpio) + gpiod_set_value_cansleep(cs35l32->reset_gpio, 0); + + return 0; +} + +#ifdef CONFIG_PM_RUNTIME +static int cs35l32_runtime_suspend(struct device *dev) +{ + struct cs35l32_private *cs35l32 = dev_get_drvdata(dev); + + regcache_cache_only(cs35l32->regmap, true); + regcache_mark_dirty(cs35l32->regmap); + + /* Hold down reset */ + if (cs35l32->reset_gpio) + gpiod_set_value_cansleep(cs35l32->reset_gpio, 0); + + /* remove power */ + regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies), + cs35l32->supplies); + + return 0; +} + +static int cs35l32_runtime_resume(struct device *dev) +{ + struct cs35l32_private *cs35l32 = dev_get_drvdata(dev); + int ret; + + /* Enable power */ + ret = regulator_bulk_enable(ARRAY_SIZE(cs35l32->supplies), + cs35l32->supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable supplies: %d\n", + ret); + return ret; + } + + if (cs35l32->reset_gpio) + gpiod_set_value_cansleep(cs35l32->reset_gpio, 1); + + regcache_cache_only(cs35l32->regmap, false); + regcache_sync(cs35l32->regmap); + + return 0; +} +#endif + +static const struct dev_pm_ops cs35l32_runtime_pm = { + SET_RUNTIME_PM_OPS(cs35l32_runtime_suspend, cs35l32_runtime_resume, + NULL) +}; + +static const struct of_device_id cs35l32_of_match[] = { + { .compatible = "cirrus,cs35l32", }, + {}, +}; +MODULE_DEVICE_TABLE(of, cs35l32_of_match); + + +static const struct i2c_device_id cs35l32_id[] = { + {"cs35l32", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, cs35l32_id); + +static struct i2c_driver cs35l32_i2c_driver = { + .driver = { + .name = "cs35l32", + .owner = THIS_MODULE, + .pm = &cs35l32_runtime_pm, + .of_match_table = cs35l32_of_match, + }, + .id_table = cs35l32_id, + .probe = cs35l32_i2c_probe, + .remove = cs35l32_i2c_remove, +}; + +module_i2c_driver(cs35l32_i2c_driver); + +MODULE_DESCRIPTION("ASoC CS35L32 driver"); +MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l32.h b/sound/soc/codecs/cs35l32.h new file mode 100644 index 0000000..31ab804 --- /dev/null +++ b/sound/soc/codecs/cs35l32.h @@ -0,0 +1,93 @@ +/* + * cs35l32.h -- CS35L32 ALSA SoC audio driver + * + * Copyright 2014 CirrusLogic, Inc. + * + * Author: Brian Austin <brian.austin@cirrus.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __CS35L32_H__ +#define __CS35L32_H__ + +struct cs35l32_platform_data { + /* Low Battery Threshold */ + unsigned int batt_thresh; + /* Low Battery Recovery */ + unsigned int batt_recov; + /* LED Current Management*/ + unsigned int led_mng; + /* Audio Gain w/ LED */ + unsigned int audiogain_mng; + /* Boost Management */ + unsigned int boost_mng; + /* Data CFG for DUAL device */ + unsigned int sdout_datacfg; + /* SDOUT Sharing */ + unsigned int sdout_share; +}; + +#define CS35L32_CHIP_ID 0x00035A32 +#define CS35L32_DEVID_AB 0x01 /* Device ID A & B [RO] */ +#define CS35L32_DEVID_CD 0x02 /* Device ID C & D [RO] */ +#define CS35L32_DEVID_E 0x03 /* Device ID E [RO] */ +#define CS35L32_FAB_ID 0x04 /* Fab ID [RO] */ +#define CS35L32_REV_ID 0x05 /* Revision ID [RO] */ +#define CS35L32_PWRCTL1 0x06 /* Power Ctl 1 */ +#define CS35L32_PWRCTL2 0x07 /* Power Ctl 2 */ +#define CS35L32_CLK_CTL 0x08 /* Clock Ctl */ +#define CS35L32_BATT_THRESHOLD 0x09 /* Low Battery Threshold */ +#define CS35L32_VMON 0x0A /* Voltage Monitor [RO] */ +#define CS35L32_BST_CPCP_CTL 0x0B /* Conv Peak Curr Protection CTL */ +#define CS35L32_IMON_SCALING 0x0C /* IMON Scaling */ +#define CS35L32_AUDIO_LED_MNGR 0x0D /* Audio/LED Pwr Manager */ +#define CS35L32_ADSP_CTL 0x0F /* Serial Port Control */ +#define CS35L32_CLASSD_CTL 0x10 /* Class D Amp CTL */ +#define CS35L32_PROTECT_CTL 0x11 /* Protection Release CTL */ +#define CS35L32_INT_MASK_1 0x12 /* Interrupt Mask 1 */ +#define CS35L32_INT_MASK_2 0x13 /* Interrupt Mask 2 */ +#define CS35L32_INT_MASK_3 0x14 /* Interrupt Mask 3 */ +#define CS35L32_INT_STATUS_1 0x15 /* Interrupt Status 1 [RO] */ +#define CS35L32_INT_STATUS_2 0x16 /* Interrupt Status 2 [RO] */ +#define CS35L32_INT_STATUS_3 0x17 /* Interrupt Status 3 [RO] */ +#define CS35L32_LED_STATUS 0x18 /* LED Lighting Status [RO] */ +#define CS35L32_FLASH_MODE 0x19 /* LED Flash Mode Current */ +#define CS35L32_MOVIE_MODE 0x1A /* LED Movie Mode Current */ +#define CS35L32_FLASH_TIMER 0x1B /* LED Flash Timer */ +#define CS35L32_FLASH_INHIBIT 0x1C /* LED Flash Inhibit Current */ +#define CS35L32_MAX_REGISTER 0x1C + +#define CS35L32_MCLK_DIV2 0x01 +#define CS35L32_MCLK_RATIO 0x01 +#define CS35L32_MCLKDIS 0x80 +#define CS35L32_PDN_ALL 0x01 +#define CS35L32_PDN_AMP 0x80 +#define CS35L32_PDN_BOOST 0x04 +#define CS35L32_PDN_IMON 0x40 +#define CS35L32_PDN_VMON 0x80 +#define CS35L32_PDN_VPMON 0x20 +#define CS35L32_PDN_ADSP 0x08 + +#define CS35L32_MCLK_DIV2_MASK 0x40 +#define CS35L32_MCLK_RATIO_MASK 0x01 +#define CS35L32_MCLK_MASK 0x41 +#define CS35L32_ADSP_MASTER_MASK 0x40 +#define CS35L32_BOOST_MASK 0x03 +#define CS35L32_GAIN_MGR_MASK 0x08 +#define CS35L32_ADSP_SHARE_MASK 0x08 +#define CS35L32_ADSP_DATACFG_MASK 0x30 +#define CS35L32_SDOUT_3ST 0x80 +#define CS35L32_BATT_REC_MASK 0x0E +#define CS35L32_BATT_THRESH_MASK 0x30 + +#define CS35L32_RATES (SNDRV_PCM_RATE_48000) +#define CS35L32_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + + +#endif diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c index 69a8516..4fdd47d 100644 --- a/sound/soc/codecs/cs4265.c +++ b/sound/soc/codecs/cs4265.c @@ -77,6 +77,7 @@ static bool cs4265_readable_register(struct device *dev, unsigned int reg) case CS4265_INT_MASK: case CS4265_STATUS_MODE_MSB: case CS4265_STATUS_MODE_LSB: + case CS4265_CHIP_ID: return true; default: return false; diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index 969167d..35fbef7 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -176,9 +176,9 @@ static bool cs42l52_volatile_register(struct device *dev, unsigned int reg) case CS42L52_BATT_LEVEL: case CS42L52_SPK_STATUS: case CS42L52_CHARGE_PUMP: - return 1; + return true; default: - return 0; + return false; } } @@ -946,20 +946,6 @@ static struct snd_soc_dai_driver cs42l52_dai = { .ops = &cs42l52_ops, }; -static int cs42l52_suspend(struct snd_soc_codec *codec) -{ - cs42l52_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - -static int cs42l52_resume(struct snd_soc_codec *codec) -{ - cs42l52_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} - static int beep_rates[] = { 261, 522, 585, 667, 706, 774, 889, 1000, 1043, 1200, 1333, 1412, 1600, 1714, 2000, 2182 @@ -1104,8 +1090,6 @@ static int cs42l52_probe(struct snd_soc_codec *codec) cs42l52_init_beep(codec); - cs42l52_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - cs42l52->sysclk = CS42L52_DEFAULT_CLK; cs42l52->config.format = CS42L52_DEFAULT_FORMAT; @@ -1115,7 +1099,6 @@ static int cs42l52_probe(struct snd_soc_codec *codec) static int cs42l52_remove(struct snd_soc_codec *codec) { cs42l52_free_beep(codec); - cs42l52_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } @@ -1123,9 +1106,8 @@ static int cs42l52_remove(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_dev_cs42l52 = { .probe = cs42l52_probe, .remove = cs42l52_remove, - .suspend = cs42l52_suspend, - .resume = cs42l52_resume, .set_bias_level = cs42l52_set_bias_level, + .suspend_bias_off = true, .dapm_widgets = cs42l52_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(cs42l52_dapm_widgets), diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c index c766a5a..2ddc7ac 100644 --- a/sound/soc/codecs/cs42l56.c +++ b/sound/soc/codecs/cs42l56.c @@ -171,9 +171,9 @@ static bool cs42l56_volatile_register(struct device *dev, unsigned int reg) { switch (reg) { case CS42L56_INT_STATUS: - return 1; + return true; default: - return 0; + return false; } } @@ -1016,20 +1016,6 @@ static struct snd_soc_dai_driver cs42l56_dai = { .ops = &cs42l56_ops, }; -static int cs42l56_suspend(struct snd_soc_codec *codec) -{ - cs42l56_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - -static int cs42l56_resume(struct snd_soc_codec *codec) -{ - cs42l56_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} - static int beep_freq[] = { 261, 522, 585, 667, 706, 774, 889, 1000, 1043, 1200, 1333, 1412, 1600, 1714, 2000, 2182 @@ -1168,18 +1154,12 @@ static int cs42l56_probe(struct snd_soc_codec *codec) { cs42l56_init_beep(codec); - cs42l56_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; } static int cs42l56_remove(struct snd_soc_codec *codec) { - struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec); - cs42l56_free_beep(codec); - cs42l56_set_bias_level(codec, SND_SOC_BIAS_OFF); - regulator_bulk_free(ARRAY_SIZE(cs42l56->supplies), cs42l56->supplies); return 0; } @@ -1187,9 +1167,8 @@ static int cs42l56_remove(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_dev_cs42l56 = { .probe = cs42l56_probe, .remove = cs42l56_remove, - .suspend = cs42l56_suspend, - .resume = cs42l56_resume, .set_bias_level = cs42l56_set_bias_level, + .suspend_bias_off = true, .dapm_widgets = cs42l56_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(cs42l56_dapm_widgets), diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c index 0e7b9eb..2f8b946 100644 --- a/sound/soc/codecs/cs42l73.c +++ b/sound/soc/codecs/cs42l73.c @@ -1330,25 +1330,10 @@ static struct snd_soc_dai_driver cs42l73_dai[] = { } }; -static int cs42l73_suspend(struct snd_soc_codec *codec) -{ - cs42l73_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - -static int cs42l73_resume(struct snd_soc_codec *codec) -{ - cs42l73_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} - static int cs42l73_probe(struct snd_soc_codec *codec) { struct cs42l73_private *cs42l73 = snd_soc_codec_get_drvdata(codec); - cs42l73_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - /* Set Charge Pump Frequency */ if (cs42l73->pdata.chgfreq) snd_soc_update_bits(codec, CS42L73_CPFCHC, @@ -1362,18 +1347,10 @@ static int cs42l73_probe(struct snd_soc_codec *codec) return 0; } -static int cs42l73_remove(struct snd_soc_codec *codec) -{ - cs42l73_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_cs42l73 = { .probe = cs42l73_probe, - .remove = cs42l73_remove, - .suspend = cs42l73_suspend, - .resume = cs42l73_resume, .set_bias_level = cs42l73_set_bias_level, + .suspend_bias_off = true, .dapm_widgets = cs42l73_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(cs42l73_dapm_widgets), diff --git a/sound/soc/codecs/da732x.c b/sound/soc/codecs/da732x.c index 2fae31c..61b2f9a 100644 --- a/sound/soc/codecs/da732x.c +++ b/sound/soc/codecs/da732x.c @@ -35,7 +35,6 @@ struct da732x_priv { struct regmap *regmap; - struct snd_soc_codec *codec; unsigned int sysclk; bool pll_en; @@ -217,7 +216,7 @@ static void da732x_set_charge_pump(struct snd_soc_codec *codec, int state) snd_soc_write(codec, DA732X_REG_CP_CTRL1, DA723X_CP_DIS); break; default: - pr_err(KERN_ERR "Wrong charge pump state\n"); + pr_err("Wrong charge pump state\n"); break; } } @@ -1508,31 +1507,7 @@ static int da732x_set_bias_level(struct snd_soc_codec *codec, return 0; } -static int da732x_probe(struct snd_soc_codec *codec) -{ - struct da732x_priv *da732x = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; - - da732x->codec = codec; - - dapm->idle_bias_off = false; - - da732x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} - -static int da732x_remove(struct snd_soc_codec *codec) -{ - - da732x_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_da732x = { - .probe = da732x_probe, - .remove = da732x_remove, .set_bias_level = da732x_set_bias_level, .controls = da732x_snd_controls, .num_controls = ARRAY_SIZE(da732x_snd_controls), diff --git a/sound/soc/codecs/es8328-i2c.c b/sound/soc/codecs/es8328-i2c.c new file mode 100644 index 0000000..aae410d --- /dev/null +++ b/sound/soc/codecs/es8328-i2c.c @@ -0,0 +1,60 @@ +/* + * es8328-i2c.c -- ES8328 ALSA SoC I2C Audio driver + * + * Copyright 2014 Sutajio Ko-Usagi PTE LTD + * + * Author: Sean Cross <xobs@kosagi.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/regmap.h> + +#include <sound/soc.h> + +#include "es8328.h" + +static const struct i2c_device_id es8328_id[] = { + { "everest,es8328", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, es8328_id); + +static const struct of_device_id es8328_of_match[] = { + { .compatible = "everest,es8328", }, + { } +}; +MODULE_DEVICE_TABLE(of, es8328_of_match); + +static int es8328_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + return es8328_probe(&i2c->dev, + devm_regmap_init_i2c(i2c, &es8328_regmap_config)); +} + +static int es8328_i2c_remove(struct i2c_client *i2c) +{ + snd_soc_unregister_codec(&i2c->dev); + return 0; +} + +static struct i2c_driver es8328_i2c_driver = { + .driver = { + .name = "es8328", + .of_match_table = es8328_of_match, + }, + .probe = es8328_i2c_probe, + .remove = es8328_i2c_remove, + .id_table = es8328_id, +}; + +module_i2c_driver(es8328_i2c_driver); + +MODULE_DESCRIPTION("ASoC ES8328 audio CODEC I2C driver"); +MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/es8328-spi.c b/sound/soc/codecs/es8328-spi.c new file mode 100644 index 0000000..8fbd935 --- /dev/null +++ b/sound/soc/codecs/es8328-spi.c @@ -0,0 +1,49 @@ +/* + * es8328.c -- ES8328 ALSA SoC SPI Audio driver + * + * Copyright 2014 Sutajio Ko-Usagi PTE LTD + * + * Author: Sean Cross <xobs@kosagi.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> +#include <sound/soc.h> +#include "es8328.h" + +static const struct of_device_id es8328_of_match[] = { + { .compatible = "everest,es8328", }, + { } +}; +MODULE_DEVICE_TABLE(of, es8328_of_match); + +static int es8328_spi_probe(struct spi_device *spi) +{ + return es8328_probe(&spi->dev, + devm_regmap_init_spi(spi, &es8328_regmap_config)); +} + +static int es8328_spi_remove(struct spi_device *spi) +{ + snd_soc_unregister_codec(&spi->dev); + return 0; +} + +static struct spi_driver es8328_spi_driver = { + .driver = { + .name = "es8328", + .of_match_table = es8328_of_match, + }, + .probe = es8328_spi_probe, + .remove = es8328_spi_remove, +}; + +module_spi_driver(es8328_spi_driver); +MODULE_DESCRIPTION("ASoC ES8328 audio CODEC SPI driver"); +MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c new file mode 100644 index 0000000..f273251 --- /dev/null +++ b/sound/soc/codecs/es8328.c @@ -0,0 +1,756 @@ +/* + * es8328.c -- ES8328 ALSA SoC Audio driver + * + * Copyright 2014 Sutajio Ko-Usagi PTE LTD + * + * Author: Sean Cross <xobs@kosagi.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/of_device.h> +#include <linux/module.h> +#include <linux/pm.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/regulator/consumer.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include "es8328.h" + +#define ES8328_SYSCLK_RATE_1X 11289600 +#define ES8328_SYSCLK_RATE_2X 22579200 + +/* Run the codec at 22.5792 or 11.2896 MHz to support these rates */ +static struct { + int rate; + u8 ratio; +} mclk_ratios[] = { + { 8000, 9 }, + {11025, 7 }, + {22050, 4 }, + {44100, 2 }, +}; + +/* regulator supplies for sgtl5000, VDDD is an optional external supply */ +enum sgtl5000_regulator_supplies { + DVDD, + AVDD, + PVDD, + HPVDD, + ES8328_SUPPLY_NUM +}; + +/* vddd is optional supply */ +static const char * const supply_names[ES8328_SUPPLY_NUM] = { + "DVDD", + "AVDD", + "PVDD", + "HPVDD", +}; + +#define ES8328_RATES (SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_22050 | \ + SNDRV_PCM_RATE_11025) +#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) + +struct es8328_priv { + struct regmap *regmap; + struct clk *clk; + int playback_fs; + bool deemph; + struct regulator_bulk_data supplies[ES8328_SUPPLY_NUM]; +}; + +/* + * ES8328 Controls + */ + +static const char * const adcpol_txt[] = {"Normal", "L Invert", "R Invert", + "L + R Invert"}; +static SOC_ENUM_SINGLE_DECL(adcpol, + ES8328_ADCCONTROL6, 6, adcpol_txt); + +static const DECLARE_TLV_DB_SCALE(play_tlv, -3000, 100, 0); +static const DECLARE_TLV_DB_SCALE(dac_adc_tlv, -9600, 50, 0); +static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 300, 0); +static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0); +static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 300, 0); + +static const int deemph_settings[] = { 0, 32000, 44100, 48000 }; + +static int es8328_set_deemph(struct snd_soc_codec *codec) +{ + struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); + int val, i, best; + + /* + * If we're using deemphasis select the nearest available sample + * rate. + */ + if (es8328->deemph) { + best = 1; + for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) { + if (abs(deemph_settings[i] - es8328->playback_fs) < + abs(deemph_settings[best] - es8328->playback_fs)) + best = i; + } + + val = best << 1; + } else { + val = 0; + } + + dev_dbg(codec->dev, "Set deemphasis %d\n", val); + + return snd_soc_update_bits(codec, ES8328_DACCONTROL6, 0x6, val); +} + +static int es8328_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = es8328->deemph; + return 0; +} + +static int es8328_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); + int deemph = ucontrol->value.enumerated.item[0]; + int ret; + + if (deemph > 1) + return -EINVAL; + + ret = es8328_set_deemph(codec); + if (ret < 0) + return ret; + + es8328->deemph = deemph; + + return 0; +} + + + +static const struct snd_kcontrol_new es8328_snd_controls[] = { + SOC_DOUBLE_R_TLV("Capture Digital Volume", + ES8328_ADCCONTROL8, ES8328_ADCCONTROL9, + 0, 0xc0, 1, dac_adc_tlv), + SOC_SINGLE("Capture ZC Switch", ES8328_ADCCONTROL7, 6, 1, 0), + + SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0, + es8328_get_deemph, es8328_put_deemph), + + SOC_ENUM("Capture Polarity", adcpol), + + SOC_SINGLE_TLV("Left Mixer Left Bypass Volume", + ES8328_DACCONTROL17, 3, 7, 1, bypass_tlv), + SOC_SINGLE_TLV("Left Mixer Right Bypass Volume", + ES8328_DACCONTROL19, 3, 7, 1, bypass_tlv), + SOC_SINGLE_TLV("Right Mixer Left Bypass Volume", + ES8328_DACCONTROL18, 3, 7, 1, bypass_tlv), + SOC_SINGLE_TLV("Right Mixer Right Bypass Volume", + ES8328_DACCONTROL20, 3, 7, 1, bypass_tlv), + + SOC_DOUBLE_R_TLV("PCM Volume", + ES8328_LDACVOL, ES8328_RDACVOL, + 0, ES8328_DACVOL_MAX, 1, dac_adc_tlv), + + SOC_DOUBLE_R_TLV("Output 1 Playback Volume", + ES8328_LOUT1VOL, ES8328_ROUT1VOL, + 0, ES8328_OUT1VOL_MAX, 0, play_tlv), + + SOC_DOUBLE_R_TLV("Output 2 Playback Volume", + ES8328_LOUT2VOL, ES8328_ROUT2VOL, + 0, ES8328_OUT2VOL_MAX, 0, play_tlv), + + SOC_DOUBLE_TLV("Mic PGA Volume", ES8328_ADCCONTROL1, + 4, 0, 8, 0, mic_tlv), +}; + +/* + * DAPM Controls + */ + +static const char * const es8328_line_texts[] = { + "Line 1", "Line 2", "PGA", "Differential"}; + +static const struct soc_enum es8328_lline_enum = + SOC_ENUM_SINGLE(ES8328_DACCONTROL16, 3, + ARRAY_SIZE(es8328_line_texts), + es8328_line_texts); +static const struct snd_kcontrol_new es8328_left_line_controls = + SOC_DAPM_ENUM("Route", es8328_lline_enum); + +static const struct soc_enum es8328_rline_enum = + SOC_ENUM_SINGLE(ES8328_DACCONTROL16, 0, + ARRAY_SIZE(es8328_line_texts), + es8328_line_texts); +static const struct snd_kcontrol_new es8328_right_line_controls = + SOC_DAPM_ENUM("Route", es8328_lline_enum); + +/* Left Mixer */ +static const struct snd_kcontrol_new es8328_left_mixer_controls[] = { + SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL17, 8, 1, 0), + SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL17, 7, 1, 0), + SOC_DAPM_SINGLE("Right Playback Switch", ES8328_DACCONTROL18, 8, 1, 0), + SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL18, 7, 1, 0), +}; + +/* Right Mixer */ +static const struct snd_kcontrol_new es8328_right_mixer_controls[] = { + SOC_DAPM_SINGLE("Left Playback Switch", ES8328_DACCONTROL19, 8, 1, 0), + SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL19, 7, 1, 0), + SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL20, 8, 1, 0), + SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL20, 7, 1, 0), +}; + +static const char * const es8328_pga_sel[] = { + "Line 1", "Line 2", "Line 3", "Differential"}; + +/* Left PGA Mux */ +static const struct soc_enum es8328_lpga_enum = + SOC_ENUM_SINGLE(ES8328_ADCCONTROL2, 6, + ARRAY_SIZE(es8328_pga_sel), + es8328_pga_sel); +static const struct snd_kcontrol_new es8328_left_pga_controls = + SOC_DAPM_ENUM("Route", es8328_lpga_enum); + +/* Right PGA Mux */ +static const struct soc_enum es8328_rpga_enum = + SOC_ENUM_SINGLE(ES8328_ADCCONTROL2, 4, + ARRAY_SIZE(es8328_pga_sel), + es8328_pga_sel); +static const struct snd_kcontrol_new es8328_right_pga_controls = + SOC_DAPM_ENUM("Route", es8328_rpga_enum); + +/* Differential Mux */ +static const char * const es8328_diff_sel[] = {"Line 1", "Line 2"}; +static SOC_ENUM_SINGLE_DECL(diffmux, + ES8328_ADCCONTROL3, 7, es8328_diff_sel); +static const struct snd_kcontrol_new es8328_diffmux_controls = + SOC_DAPM_ENUM("Route", diffmux); + +/* Mono ADC Mux */ +static const char * const es8328_mono_mux[] = {"Stereo", "Mono (Left)", + "Mono (Right)", "Digital Mono"}; +static SOC_ENUM_SINGLE_DECL(monomux, + ES8328_ADCCONTROL3, 3, es8328_mono_mux); +static const struct snd_kcontrol_new es8328_monomux_controls = + SOC_DAPM_ENUM("Route", monomux); + +static const struct snd_soc_dapm_widget es8328_dapm_widgets[] = { + SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0, + &es8328_diffmux_controls), + SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0, + &es8328_monomux_controls), + SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0, + &es8328_monomux_controls), + + SND_SOC_DAPM_MUX("Left PGA Mux", ES8328_ADCPOWER, + ES8328_ADCPOWER_AINL_OFF, 1, + &es8328_left_pga_controls), + SND_SOC_DAPM_MUX("Right PGA Mux", ES8328_ADCPOWER, + ES8328_ADCPOWER_AINR_OFF, 1, + &es8328_right_pga_controls), + + SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0, + &es8328_left_line_controls), + SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0, + &es8328_right_line_controls), + + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", ES8328_ADCPOWER, + ES8328_ADCPOWER_ADCR_OFF, 1), + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", ES8328_ADCPOWER, + ES8328_ADCPOWER_ADCL_OFF, 1), + + SND_SOC_DAPM_SUPPLY("Mic Bias", ES8328_ADCPOWER, + ES8328_ADCPOWER_MIC_BIAS_OFF, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("Mic Bias Gen", ES8328_ADCPOWER, + ES8328_ADCPOWER_ADC_BIAS_GEN_OFF, 1, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DAC STM", ES8328_CHIPPOWER, + ES8328_CHIPPOWER_DACSTM_RESET, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC STM", ES8328_CHIPPOWER, + ES8328_CHIPPOWER_ADCSTM_RESET, 1, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DAC DIG", ES8328_CHIPPOWER, + ES8328_CHIPPOWER_DACDIG_OFF, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC DIG", ES8328_CHIPPOWER, + ES8328_CHIPPOWER_ADCDIG_OFF, 1, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DAC DLL", ES8328_CHIPPOWER, + ES8328_CHIPPOWER_DACDLL_OFF, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC DLL", ES8328_CHIPPOWER, + ES8328_CHIPPOWER_ADCDLL_OFF, 1, NULL, 0), + + SND_SOC_DAPM_SUPPLY("ADC Vref", ES8328_CHIPPOWER, + ES8328_CHIPPOWER_ADCVREF_OFF, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Vref", ES8328_CHIPPOWER, + ES8328_CHIPPOWER_DACVREF_OFF, 1, NULL, 0), + + SND_SOC_DAPM_DAC("Right DAC", "Right Playback", ES8328_DACPOWER, + ES8328_DACPOWER_RDAC_OFF, 1), + SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ES8328_DACPOWER, + ES8328_DACPOWER_LDAC_OFF, 1), + + SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0, + &es8328_left_mixer_controls[0], + ARRAY_SIZE(es8328_left_mixer_controls)), + SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0, + &es8328_right_mixer_controls[0], + ARRAY_SIZE(es8328_right_mixer_controls)), + + SND_SOC_DAPM_PGA("Right Out 2", ES8328_DACPOWER, + ES8328_DACPOWER_ROUT2_ON, 0, NULL, 0), + SND_SOC_DAPM_PGA("Left Out 2", ES8328_DACPOWER, + ES8328_DACPOWER_LOUT2_ON, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Out 1", ES8328_DACPOWER, + ES8328_DACPOWER_ROUT1_ON, 0, NULL, 0), + SND_SOC_DAPM_PGA("Left Out 1", ES8328_DACPOWER, + ES8328_DACPOWER_LOUT1_ON, 0, NULL, 0), + + SND_SOC_DAPM_OUTPUT("LOUT1"), + SND_SOC_DAPM_OUTPUT("ROUT1"), + SND_SOC_DAPM_OUTPUT("LOUT2"), + SND_SOC_DAPM_OUTPUT("ROUT2"), + + SND_SOC_DAPM_INPUT("LINPUT1"), + SND_SOC_DAPM_INPUT("LINPUT2"), + SND_SOC_DAPM_INPUT("RINPUT1"), + SND_SOC_DAPM_INPUT("RINPUT2"), +}; + +static const struct snd_soc_dapm_route es8328_dapm_routes[] = { + + { "Left Line Mux", "Line 1", "LINPUT1" }, + { "Left Line Mux", "Line 2", "LINPUT2" }, + { "Left Line Mux", "PGA", "Left PGA Mux" }, + { "Left Line Mux", "Differential", "Differential Mux" }, + + { "Right Line Mux", "Line 1", "RINPUT1" }, + { "Right Line Mux", "Line 2", "RINPUT2" }, + { "Right Line Mux", "PGA", "Right PGA Mux" }, + { "Right Line Mux", "Differential", "Differential Mux" }, + + { "Left PGA Mux", "Line 1", "LINPUT1" }, + { "Left PGA Mux", "Line 2", "LINPUT2" }, + { "Left PGA Mux", "Differential", "Differential Mux" }, + + { "Right PGA Mux", "Line 1", "RINPUT1" }, + { "Right PGA Mux", "Line 2", "RINPUT2" }, + { "Right PGA Mux", "Differential", "Differential Mux" }, + + { "Differential Mux", "Line 1", "LINPUT1" }, + { "Differential Mux", "Line 1", "RINPUT1" }, + { "Differential Mux", "Line 2", "LINPUT2" }, + { "Differential Mux", "Line 2", "RINPUT2" }, + + { "Left ADC Mux", "Stereo", "Left PGA Mux" }, + { "Left ADC Mux", "Mono (Left)", "Left PGA Mux" }, + { "Left ADC Mux", "Digital Mono", "Left PGA Mux" }, + + { "Right ADC Mux", "Stereo", "Right PGA Mux" }, + { "Right ADC Mux", "Mono (Right)", "Right PGA Mux" }, + { "Right ADC Mux", "Digital Mono", "Right PGA Mux" }, + + { "Left ADC", NULL, "Left ADC Mux" }, + { "Right ADC", NULL, "Right ADC Mux" }, + + { "ADC DIG", NULL, "ADC STM" }, + { "ADC DIG", NULL, "ADC Vref" }, + { "ADC DIG", NULL, "ADC DLL" }, + + { "Left ADC", NULL, "ADC DIG" }, + { "Right ADC", NULL, "ADC DIG" }, + + { "Mic Bias", NULL, "Mic Bias Gen" }, + + { "Left Line Mux", "Line 1", "LINPUT1" }, + { "Left Line Mux", "Line 2", "LINPUT2" }, + { "Left Line Mux", "PGA", "Left PGA Mux" }, + { "Left Line Mux", "Differential", "Differential Mux" }, + + { "Right Line Mux", "Line 1", "RINPUT1" }, + { "Right Line Mux", "Line 2", "RINPUT2" }, + { "Right Line Mux", "PGA", "Right PGA Mux" }, + { "Right Line Mux", "Differential", "Differential Mux" }, + + { "Left Out 1", NULL, "Left DAC" }, + { "Right Out 1", NULL, "Right DAC" }, + { "Left Out 2", NULL, "Left DAC" }, + { "Right Out 2", NULL, "Right DAC" }, + + { "Left Mixer", "Playback Switch", "Left DAC" }, + { "Left Mixer", "Left Bypass Switch", "Left Line Mux" }, + { "Left Mixer", "Right Playback Switch", "Right DAC" }, + { "Left Mixer", "Right Bypass Switch", "Right Line Mux" }, + + { "Right Mixer", "Left Playback Switch", "Left DAC" }, + { "Right Mixer", "Left Bypass Switch", "Left Line Mux" }, + { "Right Mixer", "Playback Switch", "Right DAC" }, + { "Right Mixer", "Right Bypass Switch", "Right Line Mux" }, + + { "DAC DIG", NULL, "DAC STM" }, + { "DAC DIG", NULL, "DAC Vref" }, + { "DAC DIG", NULL, "DAC DLL" }, + + { "Left DAC", NULL, "DAC DIG" }, + { "Right DAC", NULL, "DAC DIG" }, + + { "Left Out 1", NULL, "Left Mixer" }, + { "LOUT1", NULL, "Left Out 1" }, + { "Right Out 1", NULL, "Right Mixer" }, + { "ROUT1", NULL, "Right Out 1" }, + + { "Left Out 2", NULL, "Left Mixer" }, + { "LOUT2", NULL, "Left Out 2" }, + { "Right Out 2", NULL, "Right Mixer" }, + { "ROUT2", NULL, "Right Out 2" }, +}; + +static int es8328_mute(struct snd_soc_dai *dai, int mute) +{ + return snd_soc_update_bits(dai->codec, ES8328_DACCONTROL3, + ES8328_DACCONTROL3_DACMUTE, + mute ? ES8328_DACCONTROL3_DACMUTE : 0); +} + +static int es8328_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); + int clk_rate; + int i; + int reg; + u8 ratio; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + reg = ES8328_DACCONTROL2; + else + reg = ES8328_ADCCONTROL5; + + clk_rate = clk_get_rate(es8328->clk); + + if ((clk_rate != ES8328_SYSCLK_RATE_1X) && + (clk_rate != ES8328_SYSCLK_RATE_2X)) { + dev_err(codec->dev, + "%s: clock is running at %d Hz, not %d or %d Hz\n", + __func__, clk_rate, + ES8328_SYSCLK_RATE_1X, ES8328_SYSCLK_RATE_2X); + return -EINVAL; + } + + /* find master mode MCLK to sampling frequency ratio */ + ratio = mclk_ratios[0].rate; + for (i = 1; i < ARRAY_SIZE(mclk_ratios); i++) + if (params_rate(params) <= mclk_ratios[i].rate) + ratio = mclk_ratios[i].ratio; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + es8328->playback_fs = params_rate(params); + es8328_set_deemph(codec); + } + + return snd_soc_update_bits(codec, reg, ES8328_RATEMASK, ratio); +} + +static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); + int clk_rate; + u8 mode = ES8328_DACCONTROL1_DACWL_16; + + /* set master/slave audio interface */ + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBM_CFM) + return -EINVAL; + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + mode |= ES8328_DACCONTROL1_DACFORMAT_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST; + break; + case SND_SOC_DAIFMT_LEFT_J: + mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) + return -EINVAL; + + snd_soc_write(codec, ES8328_DACCONTROL1, mode); + snd_soc_write(codec, ES8328_ADCCONTROL4, mode); + + /* Master serial port mode, with BCLK generated automatically */ + clk_rate = clk_get_rate(es8328->clk); + if (clk_rate == ES8328_SYSCLK_RATE_1X) + snd_soc_write(codec, ES8328_MASTERMODE, + ES8328_MASTERMODE_MSC); + else + snd_soc_write(codec, ES8328_MASTERMODE, + ES8328_MASTERMODE_MCLKDIV2 | + ES8328_MASTERMODE_MSC); + + return 0; +} + +static int es8328_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* VREF, VMID=2x50k, digital enabled */ + snd_soc_write(codec, ES8328_CHIPPOWER, 0); + snd_soc_update_bits(codec, ES8328_CONTROL1, + ES8328_CONTROL1_VMIDSEL_MASK | + ES8328_CONTROL1_ENREF, + ES8328_CONTROL1_VMIDSEL_50k | + ES8328_CONTROL1_ENREF); + break; + + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + snd_soc_update_bits(codec, ES8328_CONTROL1, + ES8328_CONTROL1_VMIDSEL_MASK | + ES8328_CONTROL1_ENREF, + ES8328_CONTROL1_VMIDSEL_5k | + ES8328_CONTROL1_ENREF); + + /* Charge caps */ + msleep(100); + } + + snd_soc_write(codec, ES8328_CONTROL2, + ES8328_CONTROL2_OVERCURRENT_ON | + ES8328_CONTROL2_THERMAL_SHUTDOWN_ON); + + /* VREF, VMID=2*500k, digital stopped */ + snd_soc_update_bits(codec, ES8328_CONTROL1, + ES8328_CONTROL1_VMIDSEL_MASK | + ES8328_CONTROL1_ENREF, + ES8328_CONTROL1_VMIDSEL_500k | + ES8328_CONTROL1_ENREF); + break; + + case SND_SOC_BIAS_OFF: + snd_soc_update_bits(codec, ES8328_CONTROL1, + ES8328_CONTROL1_VMIDSEL_MASK | + ES8328_CONTROL1_ENREF, + 0); + break; + } + codec->dapm.bias_level = level; + return 0; +} + +static const struct snd_soc_dai_ops es8328_dai_ops = { + .hw_params = es8328_hw_params, + .digital_mute = es8328_mute, + .set_fmt = es8328_set_dai_fmt, +}; + +static struct snd_soc_dai_driver es8328_dai = { + .name = "es8328-hifi-analog", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = ES8328_RATES, + .formats = ES8328_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = ES8328_RATES, + .formats = ES8328_FORMATS, + }, + .ops = &es8328_dai_ops, +}; + +static int es8328_suspend(struct snd_soc_codec *codec) +{ + struct es8328_priv *es8328; + int ret; + + es8328 = snd_soc_codec_get_drvdata(codec); + + clk_disable_unprepare(es8328->clk); + + ret = regulator_bulk_disable(ARRAY_SIZE(es8328->supplies), + es8328->supplies); + if (ret) { + dev_err(codec->dev, "unable to disable regulators\n"); + return ret; + } + return 0; +} + +static int es8328_resume(struct snd_soc_codec *codec) +{ + struct regmap *regmap = dev_get_regmap(codec->dev, NULL); + struct es8328_priv *es8328; + int ret; + + es8328 = snd_soc_codec_get_drvdata(codec); + + ret = clk_prepare_enable(es8328->clk); + if (ret) { + dev_err(codec->dev, "unable to enable clock\n"); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(es8328->supplies), + es8328->supplies); + if (ret) { + dev_err(codec->dev, "unable to enable regulators\n"); + return ret; + } + + regcache_mark_dirty(regmap); + ret = regcache_sync(regmap); + if (ret) { + dev_err(codec->dev, "unable to sync regcache\n"); + return ret; + } + + return 0; +} + +static int es8328_codec_probe(struct snd_soc_codec *codec) +{ + struct es8328_priv *es8328; + int ret; + + es8328 = snd_soc_codec_get_drvdata(codec); + + ret = regulator_bulk_enable(ARRAY_SIZE(es8328->supplies), + es8328->supplies); + if (ret) { + dev_err(codec->dev, "unable to enable regulators\n"); + return ret; + } + + /* Setup clocks */ + es8328->clk = devm_clk_get(codec->dev, NULL); + if (IS_ERR(es8328->clk)) { + dev_err(codec->dev, "codec clock missing or invalid\n"); + ret = PTR_ERR(es8328->clk); + goto clk_fail; + } + + ret = clk_prepare_enable(es8328->clk); + if (ret) { + dev_err(codec->dev, "unable to prepare codec clk\n"); + goto clk_fail; + } + + return 0; + +clk_fail: + regulator_bulk_disable(ARRAY_SIZE(es8328->supplies), + es8328->supplies); + return ret; +} + +static int es8328_remove(struct snd_soc_codec *codec) +{ + struct es8328_priv *es8328; + + es8328 = snd_soc_codec_get_drvdata(codec); + + if (es8328->clk) + clk_disable_unprepare(es8328->clk); + + regulator_bulk_disable(ARRAY_SIZE(es8328->supplies), + es8328->supplies); + + return 0; +} + +const struct regmap_config es8328_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = ES8328_REG_MAX, + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL_GPL(es8328_regmap_config); + +static struct snd_soc_codec_driver es8328_codec_driver = { + .probe = es8328_codec_probe, + .suspend = es8328_suspend, + .resume = es8328_resume, + .remove = es8328_remove, + .set_bias_level = es8328_set_bias_level, + .suspend_bias_off = true, + + .controls = es8328_snd_controls, + .num_controls = ARRAY_SIZE(es8328_snd_controls), + .dapm_widgets = es8328_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(es8328_dapm_widgets), + .dapm_routes = es8328_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(es8328_dapm_routes), +}; + +int es8328_probe(struct device *dev, struct regmap *regmap) +{ + struct es8328_priv *es8328; + int ret; + int i; + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + es8328 = devm_kzalloc(dev, sizeof(*es8328), GFP_KERNEL); + if (es8328 == NULL) + return -ENOMEM; + + es8328->regmap = regmap; + + for (i = 0; i < ARRAY_SIZE(es8328->supplies); i++) + es8328->supplies[i].supply = supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(es8328->supplies), + es8328->supplies); + if (ret) { + dev_err(dev, "unable to get regulators\n"); + return ret; + } + + dev_set_drvdata(dev, es8328); + + return snd_soc_register_codec(dev, + &es8328_codec_driver, &es8328_dai, 1); +} +EXPORT_SYMBOL_GPL(es8328_probe); + +MODULE_DESCRIPTION("ASoC ES8328 driver"); +MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/es8328.h b/sound/soc/codecs/es8328.h new file mode 100644 index 0000000..cb36afe --- /dev/null +++ b/sound/soc/codecs/es8328.h @@ -0,0 +1,314 @@ +/* + * es8328.h -- ES8328 ALSA SoC Audio driver + */ + +#ifndef _ES8328_H +#define _ES8328_H + +#include <linux/regmap.h> + +struct device; + +extern const struct regmap_config es8328_regmap_config; +int es8328_probe(struct device *dev, struct regmap *regmap); + +#define ES8328_DACLVOL 46 +#define ES8328_DACRVOL 47 +#define ES8328_DACCTL 28 +#define ES8328_RATEMASK (0x1f << 0) + +#define ES8328_CONTROL1 0x00 +#define ES8328_CONTROL1_VMIDSEL_OFF (0 << 0) +#define ES8328_CONTROL1_VMIDSEL_50k (1 << 0) +#define ES8328_CONTROL1_VMIDSEL_500k (2 << 0) +#define ES8328_CONTROL1_VMIDSEL_5k (3 << 0) +#define ES8328_CONTROL1_VMIDSEL_MASK (7 << 0) +#define ES8328_CONTROL1_ENREF (1 << 2) +#define ES8328_CONTROL1_SEQEN (1 << 3) +#define ES8328_CONTROL1_SAMEFS (1 << 4) +#define ES8328_CONTROL1_DACMCLK_ADC (0 << 5) +#define ES8328_CONTROL1_DACMCLK_DAC (1 << 5) +#define ES8328_CONTROL1_LRCM (1 << 6) +#define ES8328_CONTROL1_SCP_RESET (1 << 7) + +#define ES8328_CONTROL2 0x01 +#define ES8328_CONTROL2_VREF_BUF_OFF (1 << 0) +#define ES8328_CONTROL2_VREF_LOWPOWER (1 << 1) +#define ES8328_CONTROL2_IBIASGEN_OFF (1 << 2) +#define ES8328_CONTROL2_ANALOG_OFF (1 << 3) +#define ES8328_CONTROL2_VREF_BUF_LOWPOWER (1 << 4) +#define ES8328_CONTROL2_VCM_MOD_LOWPOWER (1 << 5) +#define ES8328_CONTROL2_OVERCURRENT_ON (1 << 6) +#define ES8328_CONTROL2_THERMAL_SHUTDOWN_ON (1 << 7) + +#define ES8328_CHIPPOWER 0x02 +#define ES8328_CHIPPOWER_DACVREF_OFF 0 +#define ES8328_CHIPPOWER_ADCVREF_OFF 1 +#define ES8328_CHIPPOWER_DACDLL_OFF 2 +#define ES8328_CHIPPOWER_ADCDLL_OFF 3 +#define ES8328_CHIPPOWER_DACSTM_RESET 4 +#define ES8328_CHIPPOWER_ADCSTM_RESET 5 +#define ES8328_CHIPPOWER_DACDIG_OFF 6 +#define ES8328_CHIPPOWER_ADCDIG_OFF 7 + +#define ES8328_ADCPOWER 0x03 +#define ES8328_ADCPOWER_INT1_LOWPOWER 0 +#define ES8328_ADCPOWER_FLASH_ADC_LOWPOWER 1 +#define ES8328_ADCPOWER_ADC_BIAS_GEN_OFF 2 +#define ES8328_ADCPOWER_MIC_BIAS_OFF 3 +#define ES8328_ADCPOWER_ADCR_OFF 4 +#define ES8328_ADCPOWER_ADCL_OFF 5 +#define ES8328_ADCPOWER_AINR_OFF 6 +#define ES8328_ADCPOWER_AINL_OFF 7 + +#define ES8328_DACPOWER 0x04 +#define ES8328_DACPOWER_OUT3_ON 0 +#define ES8328_DACPOWER_MONO_ON 1 +#define ES8328_DACPOWER_ROUT2_ON 2 +#define ES8328_DACPOWER_LOUT2_ON 3 +#define ES8328_DACPOWER_ROUT1_ON 4 +#define ES8328_DACPOWER_LOUT1_ON 5 +#define ES8328_DACPOWER_RDAC_OFF 6 +#define ES8328_DACPOWER_LDAC_OFF 7 + +#define ES8328_CHIPLOPOW1 0x05 +#define ES8328_CHIPLOPOW2 0x06 +#define ES8328_ANAVOLMANAG 0x07 + +#define ES8328_MASTERMODE 0x08 +#define ES8328_MASTERMODE_BCLKDIV (0 << 0) +#define ES8328_MASTERMODE_BCLK_INV (1 << 5) +#define ES8328_MASTERMODE_MCLKDIV2 (1 << 6) +#define ES8328_MASTERMODE_MSC (1 << 7) + +#define ES8328_ADCCONTROL1 0x09 +#define ES8328_ADCCONTROL2 0x0a +#define ES8328_ADCCONTROL3 0x0b +#define ES8328_ADCCONTROL4 0x0c +#define ES8328_ADCCONTROL5 0x0d +#define ES8328_ADCCONTROL5_RATEMASK (0x1f << 0) + +#define ES8328_ADCCONTROL6 0x0e + +#define ES8328_ADCCONTROL7 0x0f +#define ES8328_ADCCONTROL7_ADC_MUTE (1 << 2) +#define ES8328_ADCCONTROL7_ADC_LER (1 << 3) +#define ES8328_ADCCONTROL7_ADC_ZERO_CROSS (1 << 4) +#define ES8328_ADCCONTROL7_ADC_SOFT_RAMP (1 << 5) +#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_4 (0 << 6) +#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_8 (1 << 6) +#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_16 (2 << 6) +#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_32 (3 << 6) + +#define ES8328_ADCCONTROL8 0x10 +#define ES8328_ADCCONTROL9 0x11 +#define ES8328_ADCCONTROL10 0x12 +#define ES8328_ADCCONTROL11 0x13 +#define ES8328_ADCCONTROL12 0x14 +#define ES8328_ADCCONTROL13 0x15 +#define ES8328_ADCCONTROL14 0x16 + +#define ES8328_DACCONTROL1 0x17 +#define ES8328_DACCONTROL1_DACFORMAT_I2S (0 << 1) +#define ES8328_DACCONTROL1_DACFORMAT_LJUST (1 << 1) +#define ES8328_DACCONTROL1_DACFORMAT_RJUST (2 << 1) +#define ES8328_DACCONTROL1_DACFORMAT_PCM (3 << 1) +#define ES8328_DACCONTROL1_DACWL_24 (0 << 3) +#define ES8328_DACCONTROL1_DACWL_20 (1 << 3) +#define ES8328_DACCONTROL1_DACWL_18 (2 << 3) +#define ES8328_DACCONTROL1_DACWL_16 (3 << 3) +#define ES8328_DACCONTROL1_DACWL_32 (4 << 3) +#define ES8328_DACCONTROL1_DACLRP_I2S_POL_NORMAL (0 << 6) +#define ES8328_DACCONTROL1_DACLRP_I2S_POL_INV (1 << 6) +#define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK2 (0 << 6) +#define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK1 (1 << 6) +#define ES8328_DACCONTROL1_LRSWAP (1 << 7) + +#define ES8328_DACCONTROL2 0x18 +#define ES8328_DACCONTROL2_RATEMASK (0x1f << 0) +#define ES8328_DACCONTROL2_DOUBLESPEED (1 << 5) + +#define ES8328_DACCONTROL3 0x19 +#define ES8328_DACCONTROL3_AUTOMUTE (1 << 2) +#define ES8328_DACCONTROL3_DACMUTE (1 << 2) +#define ES8328_DACCONTROL3_LEFTGAINVOL (1 << 3) +#define ES8328_DACCONTROL3_DACZEROCROSS (1 << 4) +#define ES8328_DACCONTROL3_DACSOFTRAMP (1 << 5) +#define ES8328_DACCONTROL3_DACRAMPRATE (3 << 6) + +#define ES8328_LDACVOL 0x1a +#define ES8328_LDACVOL_MASK (0 << 0) +#define ES8328_LDACVOL_MAX (0xc0) + +#define ES8328_RDACVOL 0x1b +#define ES8328_RDACVOL_MASK (0 << 0) +#define ES8328_RDACVOL_MAX (0xc0) + +#define ES8328_DACVOL_MAX (0xc0) + +#define ES8328_DACCONTROL4 0x1a +#define ES8328_DACCONTROL5 0x1b + +#define ES8328_DACCONTROL6 0x1c +#define ES8328_DACCONTROL6_CLICKFREE (1 << 3) +#define ES8328_DACCONTROL6_DAC_INVR (1 << 4) +#define ES8328_DACCONTROL6_DAC_INVL (1 << 5) +#define ES8328_DACCONTROL6_DEEMPH_OFF (0 << 6) +#define ES8328_DACCONTROL6_DEEMPH_32k (1 << 6) +#define ES8328_DACCONTROL6_DEEMPH_44_1k (2 << 6) +#define ES8328_DACCONTROL6_DEEMPH_48k (3 << 6) + +#define ES8328_DACCONTROL7 0x1d +#define ES8328_DACCONTROL7_VPP_SCALE_3p5 (0 << 0) +#define ES8328_DACCONTROL7_VPP_SCALE_4p0 (1 << 0) +#define ES8328_DACCONTROL7_VPP_SCALE_3p0 (2 << 0) +#define ES8328_DACCONTROL7_VPP_SCALE_2p5 (3 << 0) +#define ES8328_DACCONTROL7_SHELVING_STRENGTH (1 << 2) /* In eights */ +#define ES8328_DACCONTROL7_MONO (1 << 5) +#define ES8328_DACCONTROL7_ZEROR (1 << 6) +#define ES8328_DACCONTROL7_ZEROL (1 << 7) + +/* Shelving filter */ +#define ES8328_DACCONTROL8 0x1e +#define ES8328_DACCONTROL9 0x1f +#define ES8328_DACCONTROL10 0x20 +#define ES8328_DACCONTROL11 0x21 +#define ES8328_DACCONTROL12 0x22 +#define ES8328_DACCONTROL13 0x23 +#define ES8328_DACCONTROL14 0x24 +#define ES8328_DACCONTROL15 0x25 + +#define ES8328_DACCONTROL16 0x26 +#define ES8328_DACCONTROL16_RMIXSEL_RIN1 (0 << 0) +#define ES8328_DACCONTROL16_RMIXSEL_RIN2 (1 << 0) +#define ES8328_DACCONTROL16_RMIXSEL_RIN3 (2 << 0) +#define ES8328_DACCONTROL16_RMIXSEL_RADC (3 << 0) +#define ES8328_DACCONTROL16_LMIXSEL_LIN1 (0 << 3) +#define ES8328_DACCONTROL16_LMIXSEL_LIN2 (1 << 3) +#define ES8328_DACCONTROL16_LMIXSEL_LIN3 (2 << 3) +#define ES8328_DACCONTROL16_LMIXSEL_LADC (3 << 3) + +#define ES8328_DACCONTROL17 0x27 +#define ES8328_DACCONTROL17_LI2LOVOL (7 << 3) +#define ES8328_DACCONTROL17_LI2LO (1 << 6) +#define ES8328_DACCONTROL17_LD2LO (1 << 7) + +#define ES8328_DACCONTROL18 0x28 +#define ES8328_DACCONTROL18_RI2LOVOL (7 << 3) +#define ES8328_DACCONTROL18_RI2LO (1 << 6) +#define ES8328_DACCONTROL18_RD2LO (1 << 7) + +#define ES8328_DACCONTROL19 0x29 +#define ES8328_DACCONTROL19_LI2ROVOL (7 << 3) +#define ES8328_DACCONTROL19_LI2RO (1 << 6) +#define ES8328_DACCONTROL19_LD2RO (1 << 7) + +#define ES8328_DACCONTROL20 0x2a +#define ES8328_DACCONTROL20_RI2ROVOL (7 << 3) +#define ES8328_DACCONTROL20_RI2RO (1 << 6) +#define ES8328_DACCONTROL20_RD2RO (1 << 7) + +#define ES8328_DACCONTROL21 0x2b +#define ES8328_DACCONTROL21_LI2MOVOL (7 << 3) +#define ES8328_DACCONTROL21_LI2MO (1 << 6) +#define ES8328_DACCONTROL21_LD2MO (1 << 7) + +#define ES8328_DACCONTROL22 0x2c +#define ES8328_DACCONTROL22_RI2MOVOL (7 << 3) +#define ES8328_DACCONTROL22_RI2MO (1 << 6) +#define ES8328_DACCONTROL22_RD2MO (1 << 7) + +#define ES8328_DACCONTROL23 0x2d +#define ES8328_DACCONTROL23_MOUTINV (1 << 1) +#define ES8328_DACCONTROL23_HPSWPOL (1 << 2) +#define ES8328_DACCONTROL23_HPSWEN (1 << 3) +#define ES8328_DACCONTROL23_VROI_1p5k (0 << 4) +#define ES8328_DACCONTROL23_VROI_40k (1 << 4) +#define ES8328_DACCONTROL23_OUT3_VREF (0 << 5) +#define ES8328_DACCONTROL23_OUT3_ROUT1 (1 << 5) +#define ES8328_DACCONTROL23_OUT3_MONOOUT (2 << 5) +#define ES8328_DACCONTROL23_OUT3_RIGHT_MIXER (3 << 5) +#define ES8328_DACCONTROL23_ROUT2INV (1 << 7) + +/* LOUT1 Amplifier */ +#define ES8328_LOUT1VOL 0x2e +#define ES8328_LOUT1VOL_MASK (0 << 5) +#define ES8328_LOUT1VOL_MAX (0x24) + +/* ROUT1 Amplifier */ +#define ES8328_ROUT1VOL 0x2f +#define ES8328_ROUT1VOL_MASK (0 << 5) +#define ES8328_ROUT1VOL_MAX (0x24) + +#define ES8328_OUT1VOL_MAX (0x24) + +/* LOUT2 Amplifier */ +#define ES8328_LOUT2VOL 0x30 +#define ES8328_LOUT2VOL_MASK (0 << 5) +#define ES8328_LOUT2VOL_MAX (0x24) + +/* ROUT2 Amplifier */ +#define ES8328_ROUT2VOL 0x31 +#define ES8328_ROUT2VOL_MASK (0 << 5) +#define ES8328_ROUT2VOL_MAX (0x24) + +#define ES8328_OUT2VOL_MAX (0x24) + +/* Mono Out Amplifier */ +#define ES8328_MONOOUTVOL 0x32 +#define ES8328_MONOOUTVOL_MASK (0 << 5) +#define ES8328_MONOOUTVOL_MAX (0x24) + +#define ES8328_DACCONTROL29 0x33 +#define ES8328_DACCONTROL30 0x34 + +#define ES8328_SYSCLK 0 + +#define ES8328_REG_MAX 0x35 + +#define ES8328_PLL1 0 +#define ES8328_PLL2 1 + +/* clock inputs */ +#define ES8328_MCLK 0 +#define ES8328_PCMCLK 1 + +/* clock divider id's */ +#define ES8328_PCMDIV 0 +#define ES8328_BCLKDIV 1 +#define ES8328_VXCLKDIV 2 + +/* PCM clock dividers */ +#define ES8328_PCM_DIV_1 (0 << 6) +#define ES8328_PCM_DIV_3 (2 << 6) +#define ES8328_PCM_DIV_5_5 (3 << 6) +#define ES8328_PCM_DIV_2 (4 << 6) +#define ES8328_PCM_DIV_4 (5 << 6) +#define ES8328_PCM_DIV_6 (6 << 6) +#define ES8328_PCM_DIV_8 (7 << 6) + +/* BCLK clock dividers */ +#define ES8328_BCLK_DIV_1 (0 << 7) +#define ES8328_BCLK_DIV_2 (1 << 7) +#define ES8328_BCLK_DIV_4 (2 << 7) +#define ES8328_BCLK_DIV_8 (3 << 7) + +/* VXCLK clock dividers */ +#define ES8328_VXCLK_DIV_1 (0 << 6) +#define ES8328_VXCLK_DIV_2 (1 << 6) +#define ES8328_VXCLK_DIV_4 (2 << 6) +#define ES8328_VXCLK_DIV_8 (3 << 6) +#define ES8328_VXCLK_DIV_16 (4 << 6) + +#define ES8328_DAI_HIFI 0 +#define ES8328_DAI_VOICE 1 + +#define ES8328_1536FS 1536 +#define ES8328_1024FS 1024 +#define ES8328_768FS 768 +#define ES8328_512FS 512 +#define ES8328_384FS 384 +#define ES8328_256FS 256 +#define ES8328_128FS 128 + +#endif diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c index bcebd1a..df7c01c 100644 --- a/sound/soc/codecs/jz4740.c +++ b/sound/soc/codecs/jz4740.c @@ -293,41 +293,13 @@ static int jz4740_codec_dev_probe(struct snd_soc_codec *codec) regmap_update_bits(jz4740_codec->regmap, JZ4740_REG_CODEC_1, JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE); - jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; } -static int jz4740_codec_dev_remove(struct snd_soc_codec *codec) -{ - jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP - -static int jz4740_codec_suspend(struct snd_soc_codec *codec) -{ - return jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_OFF); -} - -static int jz4740_codec_resume(struct snd_soc_codec *codec) -{ - return jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY); -} - -#else -#define jz4740_codec_suspend NULL -#define jz4740_codec_resume NULL -#endif - static struct snd_soc_codec_driver soc_codec_dev_jz4740_codec = { .probe = jz4740_codec_dev_probe, - .remove = jz4740_codec_dev_remove, - .suspend = jz4740_codec_suspend, - .resume = jz4740_codec_resume, .set_bias_level = jz4740_codec_set_bias_level, + .suspend_bias_off = true, .controls = jz4740_codec_controls, .num_controls = ARRAY_SIZE(jz4740_codec_controls), diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c index 275b3f7..c1ae576 100644 --- a/sound/soc/codecs/lm49453.c +++ b/sound/soc/codecs/lm49453.c @@ -1395,18 +1395,6 @@ static struct snd_soc_dai_driver lm49453_dai[] = { }, }; -static int lm49453_suspend(struct snd_soc_codec *codec) -{ - lm49453_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int lm49453_resume(struct snd_soc_codec *codec) -{ - lm49453_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} - /* power down chip */ static int lm49453_remove(struct snd_soc_codec *codec) { @@ -1416,8 +1404,6 @@ static int lm49453_remove(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_dev_lm49453 = { .remove = lm49453_remove, - .suspend = lm49453_suspend, - .resume = lm49453_resume, .set_bias_level = lm49453_set_bias_level, .controls = lm49453_snd_controls, .num_controls = ARRAY_SIZE(lm49453_snd_controls), diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index 4a063fa..d519294 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -1311,8 +1311,6 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = { {"MIC1 Input", NULL, "MIC1"}, {"MIC2 Input", NULL, "MIC2"}, - {"DMICL", NULL, "DMICL_ENA"}, - {"DMICR", NULL, "DMICR_ENA"}, {"DMICL", NULL, "AHPF"}, {"DMICR", NULL, "AHPF"}, @@ -1370,6 +1368,8 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = { {"DMIC Mux", "ADC", "ADCR"}, {"DMIC Mux", "DMIC", "DMICL"}, {"DMIC Mux", "DMIC", "DMICR"}, + {"DMIC Mux", "DMIC", "DMICL_ENA"}, + {"DMIC Mux", "DMIC", "DMICR_ENA"}, {"LBENL Mux", "Normal", "DMIC Mux"}, {"LBENL Mux", "Loopback", "LTENL Mux"}, @@ -1972,6 +1972,102 @@ static int max98090_dai_digital_mute(struct snd_soc_dai *codec_dai, int mute) return 0; } +static int max98090_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (!max98090->master && dai->active == 1) + queue_delayed_work(system_power_efficient_wq, + &max98090->pll_det_enable_work, + msecs_to_jiffies(10)); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (!max98090->master && dai->active == 1) + schedule_work(&max98090->pll_det_disable_work); + break; + default: + break; + } + + return 0; +} + +static void max98090_pll_det_enable_work(struct work_struct *work) +{ + struct max98090_priv *max98090 = + container_of(work, struct max98090_priv, + pll_det_enable_work.work); + struct snd_soc_codec *codec = max98090->codec; + unsigned int status, mask; + + /* + * Clear status register in order to clear possibly already occurred + * PLL unlock. If PLL hasn't still locked, the status will be set + * again and PLL unlock interrupt will occur. + * Note this will clear all status bits + */ + regmap_read(max98090->regmap, M98090_REG_DEVICE_STATUS, &status); + + /* + * Queue jack work in case jack state has just changed but handler + * hasn't run yet + */ + regmap_read(max98090->regmap, M98090_REG_INTERRUPT_S, &mask); + status &= mask; + if (status & M98090_JDET_MASK) + queue_delayed_work(system_power_efficient_wq, + &max98090->jack_work, + msecs_to_jiffies(100)); + + /* Enable PLL unlock interrupt */ + snd_soc_update_bits(codec, M98090_REG_INTERRUPT_S, + M98090_IULK_MASK, + 1 << M98090_IULK_SHIFT); +} + +static void max98090_pll_det_disable_work(struct work_struct *work) +{ + struct max98090_priv *max98090 = + container_of(work, struct max98090_priv, pll_det_disable_work); + struct snd_soc_codec *codec = max98090->codec; + + cancel_delayed_work_sync(&max98090->pll_det_enable_work); + + /* Disable PLL unlock interrupt */ + snd_soc_update_bits(codec, M98090_REG_INTERRUPT_S, + M98090_IULK_MASK, 0); +} + +static void max98090_pll_work(struct work_struct *work) +{ + struct max98090_priv *max98090 = + container_of(work, struct max98090_priv, pll_work); + struct snd_soc_codec *codec = max98090->codec; + + if (!snd_soc_codec_is_active(codec)) + return; + + dev_info(codec->dev, "PLL unlocked\n"); + + /* Toggle shutdown OFF then ON */ + snd_soc_update_bits(codec, M98090_REG_DEVICE_SHUTDOWN, + M98090_SHDNN_MASK, 0); + msleep(10); + snd_soc_update_bits(codec, M98090_REG_DEVICE_SHUTDOWN, + M98090_SHDNN_MASK, M98090_SHDNN_MASK); + + /* Give PLL time to lock */ + msleep(10); +} + static void max98090_jack_work(struct work_struct *work) { struct max98090_priv *max98090 = container_of(work, @@ -2063,12 +2159,16 @@ static void max98090_jack_work(struct work_struct *work) static irqreturn_t max98090_interrupt(int irq, void *data) { - struct snd_soc_codec *codec = data; - struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + struct max98090_priv *max98090 = data; + struct snd_soc_codec *codec = max98090->codec; int ret; unsigned int mask; unsigned int active; + /* Treat interrupt before codec is initialized as spurious */ + if (codec == NULL) + return IRQ_NONE; + dev_dbg(codec->dev, "***** max98090_interrupt *****\n"); ret = regmap_read(max98090->regmap, M98090_REG_INTERRUPT_S, &mask); @@ -2103,8 +2203,10 @@ static irqreturn_t max98090_interrupt(int irq, void *data) if (active & M98090_SLD_MASK) dev_dbg(codec->dev, "M98090_SLD_MASK\n"); - if (active & M98090_ULK_MASK) - dev_err(codec->dev, "M98090_ULK_MASK\n"); + if (active & M98090_ULK_MASK) { + dev_dbg(codec->dev, "M98090_ULK_MASK\n"); + schedule_work(&max98090->pll_work); + } if (active & M98090_JDET_MASK) { dev_dbg(codec->dev, "M98090_JDET_MASK\n"); @@ -2177,6 +2279,7 @@ static struct snd_soc_dai_ops max98090_dai_ops = { .set_tdm_slot = max98090_set_tdm_slot, .hw_params = max98090_dai_hw_params, .digital_mute = max98090_dai_digital_mute, + .trigger = max98090_dai_trigger, }; static struct snd_soc_dai_driver max98090_dai[] = { @@ -2230,7 +2333,6 @@ static int max98090_probe(struct snd_soc_codec *codec) max98090->lin_state = 0; max98090->pa1en = 0; max98090->pa2en = 0; - max98090->extmic_mux = 0; ret = snd_soc_read(codec, M98090_REG_REVISION_ID); if (ret < 0) { @@ -2258,22 +2360,16 @@ static int max98090_probe(struct snd_soc_codec *codec) max98090->jack_state = M98090_JACK_STATE_NO_HEADSET; INIT_DELAYED_WORK(&max98090->jack_work, max98090_jack_work); + INIT_DELAYED_WORK(&max98090->pll_det_enable_work, + max98090_pll_det_enable_work); + INIT_WORK(&max98090->pll_det_disable_work, + max98090_pll_det_disable_work); + INIT_WORK(&max98090->pll_work, max98090_pll_work); /* Enable jack detection */ snd_soc_write(codec, M98090_REG_JACK_DETECT, M98090_JDETEN_MASK | M98090_JDEB_25MS); - /* Register for interrupts */ - dev_dbg(codec->dev, "irq = %d\n", max98090->irq); - - ret = devm_request_threaded_irq(codec->dev, max98090->irq, NULL, - max98090_interrupt, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "max98090_interrupt", codec); - if (ret < 0) { - dev_err(codec->dev, "request_irq failed: %d\n", - ret); - } - /* * Clear any old interrupts. * An old interrupt ocurring prior to installing the ISR @@ -2310,6 +2406,10 @@ static int max98090_remove(struct snd_soc_codec *codec) struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); cancel_delayed_work_sync(&max98090->jack_work); + cancel_delayed_work_sync(&max98090->pll_det_enable_work); + cancel_work_sync(&max98090->pll_det_disable_work); + cancel_work_sync(&max98090->pll_work); + max98090->codec = NULL; return 0; } @@ -2362,7 +2462,6 @@ static int max98090_i2c_probe(struct i2c_client *i2c, max98090->devtype = driver_data; i2c_set_clientdata(i2c, max98090); max98090->pdata = i2c->dev.platform_data; - max98090->irq = i2c->irq; max98090->regmap = devm_regmap_init_i2c(i2c, &max98090_regmap); if (IS_ERR(max98090->regmap)) { @@ -2371,6 +2470,15 @@ static int max98090_i2c_probe(struct i2c_client *i2c, goto err_enable; } + ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, + max98090_interrupt, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "max98090_interrupt", max98090); + if (ret < 0) { + dev_err(&i2c->dev, "request_irq failed: %d\n", + ret); + return ret; + } + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98090, max98090_dai, ARRAY_SIZE(max98090_dai)); diff --git a/sound/soc/codecs/max98090.h b/sound/soc/codecs/max98090.h index cf1b606..a5f6bad 100644 --- a/sound/soc/codecs/max98090.h +++ b/sound/soc/codecs/max98090.h @@ -11,11 +11,6 @@ #ifndef _MAX98090_H #define _MAX98090_H -#include <linux/version.h> - -/* One can override the Linux version here with an explicit version number */ -#define M98090_LINUX_VERSION LINUX_VERSION_CODE - /* * MAX98090 Register Definitions */ @@ -1502,9 +1497,6 @@ #define M98090_REVID_WIDTH 8 #define M98090_REVID_NUM (1<<M98090_REVID_WIDTH) -#define M98090_BYTE1(w) ((w >> 8) & 0xff) -#define M98090_BYTE0(w) (w & 0xff) - /* Silicon revision number */ #define M98090_REVA 0x40 #define M98091_REVA 0x50 @@ -1529,9 +1521,11 @@ struct max98090_priv { unsigned int bclk; unsigned int lrclk; struct max98090_cdata dai[1]; - int irq; int jack_state; struct delayed_work jack_work; + struct delayed_work pll_det_enable_work; + struct work_struct pll_det_disable_work; + struct work_struct pll_work; struct snd_soc_jack *jack; unsigned int dai_fmt; int tdm_slots; @@ -1539,7 +1533,6 @@ struct max98090_priv { u8 lin_state; unsigned int pa1en; unsigned int pa2en; - unsigned int extmic_mux; unsigned int sidetone; bool master; }; diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c index 388f90a..71f775a 100644 --- a/sound/soc/codecs/mc13783.c +++ b/sound/soc/codecs/mc13783.c @@ -765,12 +765,18 @@ static int __init mc13783_codec_probe(struct platform_device *pdev) return -ENOSYS; ret = of_property_read_u32(np, "adc-port", &priv->adc_ssi_port); - if (ret) - goto out; + if (ret) { + of_node_put(np); + return ret; + } ret = of_property_read_u32(np, "dac-port", &priv->dac_ssi_port); - if (ret) - goto out; + if (ret) { + of_node_put(np); + return ret; + } + + of_node_put(np); } dev_set_drvdata(&pdev->dev, priv); @@ -783,8 +789,6 @@ static int __init mc13783_codec_probe(struct platform_device *pdev) ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783, mc13783_dai_async, ARRAY_SIZE(mc13783_dai_async)); -out: - of_node_put(np); return ret; } diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c index e661e84..711f550 100644 --- a/sound/soc/codecs/ml26124.c +++ b/sound/soc/codecs/ml26124.c @@ -565,41 +565,19 @@ static struct snd_soc_dai_driver ml26124_dai = { .symmetric_rates = 1, }; -#ifdef CONFIG_PM -static int ml26124_suspend(struct snd_soc_codec *codec) -{ - ml26124_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - -static int ml26124_resume(struct snd_soc_codec *codec) -{ - ml26124_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} -#else -#define ml26124_suspend NULL -#define ml26124_resume NULL -#endif - static int ml26124_probe(struct snd_soc_codec *codec) { /* Software Reset */ snd_soc_update_bits(codec, ML26124_SW_RST, 0x01, 1); snd_soc_update_bits(codec, ML26124_SW_RST, 0x01, 0); - ml26124_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; } static struct snd_soc_codec_driver soc_codec_dev_ml26124 = { .probe = ml26124_probe, - .suspend = ml26124_suspend, - .resume = ml26124_resume, .set_bias_level = ml26124_set_bias_level, + .suspend_bias_off = true, .dapm_widgets = ml26124_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(ml26124_dapm_widgets), .dapm_routes = ml26124_intercon, diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index b86b426..4aa555c 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -269,6 +269,7 @@ static int rt286_hw_read(void *context, unsigned int reg, unsigned int *value) return 0; } +#ifdef CONFIG_PM static void rt286_index_sync(struct snd_soc_codec *codec) { struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec); @@ -279,6 +280,7 @@ static void rt286_index_sync(struct snd_soc_codec *codec) rt286->index_cache[i].def); } } +#endif static int rt286_support_power_controls[] = { RT286_DAC_OUT1, diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index f1ec6e6..c3f2dec 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -1906,6 +1906,32 @@ static int rt5640_set_bias_level(struct snd_soc_codec *codec, return 0; } +int rt5640_dmic_enable(struct snd_soc_codec *codec, + bool dmic1_data_pin, bool dmic2_data_pin) +{ + struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); + + regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1, + RT5640_GP2_PIN_MASK, RT5640_GP2_PIN_DMIC1_SCL); + + if (dmic1_data_pin) { + regmap_update_bits(rt5640->regmap, RT5640_DMIC, + RT5640_DMIC_1_DP_MASK, RT5640_DMIC_1_DP_GPIO3); + regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1, + RT5640_GP3_PIN_MASK, RT5640_GP3_PIN_DMIC1_SDA); + } + + if (dmic2_data_pin) { + regmap_update_bits(rt5640->regmap, RT5640_DMIC, + RT5640_DMIC_2_DP_MASK, RT5640_DMIC_2_DP_GPIO4); + regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1, + RT5640_GP4_PIN_MASK, RT5640_GP4_PIN_DMIC2_SDA); + } + + return 0; +} +EXPORT_SYMBOL_GPL(rt5640_dmic_enable); + static int rt5640_probe(struct snd_soc_codec *codec) { struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); @@ -1945,6 +1971,10 @@ static int rt5640_probe(struct snd_soc_codec *codec) return -ENODEV; } + if (rt5640->pdata.dmic_en) + rt5640_dmic_enable(codec, rt5640->pdata.dmic1_data_pin, + rt5640->pdata.dmic2_data_pin); + return 0; } @@ -2195,25 +2225,6 @@ static int rt5640_i2c_probe(struct i2c_client *i2c, regmap_update_bits(rt5640->regmap, RT5640_IN3_IN4, RT5640_IN_DF2, RT5640_IN_DF2); - if (rt5640->pdata.dmic_en) { - regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1, - RT5640_GP2_PIN_MASK, RT5640_GP2_PIN_DMIC1_SCL); - - if (rt5640->pdata.dmic1_data_pin) { - regmap_update_bits(rt5640->regmap, RT5640_DMIC, - RT5640_DMIC_1_DP_MASK, RT5640_DMIC_1_DP_GPIO3); - regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1, - RT5640_GP3_PIN_MASK, RT5640_GP3_PIN_DMIC1_SDA); - } - - if (rt5640->pdata.dmic2_data_pin) { - regmap_update_bits(rt5640->regmap, RT5640_DMIC, - RT5640_DMIC_2_DP_MASK, RT5640_DMIC_2_DP_GPIO4); - regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1, - RT5640_GP4_PIN_MASK, RT5640_GP4_PIN_DMIC2_SDA); - } - } - rt5640->hp_mute = 1; return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5640, diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h index 58ebe96..3deb8ba 100644 --- a/sound/soc/codecs/rt5640.h +++ b/sound/soc/codecs/rt5640.h @@ -2097,4 +2097,7 @@ struct rt5640_priv { bool hp_mute; }; +int rt5640_dmic_enable(struct snd_soc_codec *codec, + bool dmic1_data_pin, bool dmic2_data_pin); + #endif diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index a7762d0..3fb83bf 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -17,6 +17,7 @@ #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/spi/spi.h> +#include <linux/gpio.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -2103,6 +2104,77 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec, return 0; } +static int rt5645_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack) +{ + struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); + int gpio_state, jack_type = 0; + unsigned int val; + + gpio_state = gpio_get_value(rt5645->pdata.hp_det_gpio); + + dev_dbg(codec->dev, "gpio = %d(%d)\n", rt5645->pdata.hp_det_gpio, + gpio_state); + + if ((rt5645->pdata.gpio_hp_det_active_high && gpio_state) || + (!rt5645->pdata.gpio_hp_det_active_high && !gpio_state)) { + snd_soc_dapm_force_enable_pin(&codec->dapm, "micbias1"); + snd_soc_dapm_force_enable_pin(&codec->dapm, "micbias2"); + snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO2"); + snd_soc_dapm_force_enable_pin(&codec->dapm, "Mic Det Power"); + snd_soc_dapm_sync(&codec->dapm); + + snd_soc_write(codec, RT5645_IN1_CTRL1, 0x0006); + snd_soc_write(codec, RT5645_JD_CTRL3, 0x00b0); + + snd_soc_update_bits(codec, RT5645_IN1_CTRL2, + RT5645_CBJ_MN_JD, 0); + snd_soc_update_bits(codec, RT5645_IN1_CTRL2, + RT5645_CBJ_MN_JD, RT5645_CBJ_MN_JD); + + msleep(400); + val = snd_soc_read(codec, RT5645_IN1_CTRL3) & 0x7; + dev_dbg(codec->dev, "val = %d\n", val); + + if (val == 1 || val == 2) + jack_type = SND_JACK_HEADSET; + else + jack_type = SND_JACK_HEADPHONE; + + snd_soc_dapm_disable_pin(&codec->dapm, "micbias1"); + snd_soc_dapm_disable_pin(&codec->dapm, "micbias2"); + snd_soc_dapm_disable_pin(&codec->dapm, "LDO2"); + snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power"); + snd_soc_dapm_sync(&codec->dapm); + } + + snd_soc_jack_report(rt5645->jack, jack_type, SND_JACK_HEADSET); + + return 0; +} + +int rt5645_set_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack) +{ + struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); + + rt5645->jack = jack; + + rt5645_jack_detect(codec, rt5645->jack); + + return 0; +} +EXPORT_SYMBOL_GPL(rt5645_set_jack_detect); + +static irqreturn_t rt5645_irq(int irq, void *data) +{ + struct rt5645_priv *rt5645 = data; + + rt5645_jack_detect(rt5645->codec, rt5645->jack); + + return IRQ_HANDLED; +} + static int rt5645_probe(struct snd_soc_codec *codec) { struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); @@ -2250,6 +2322,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, if (rt5645 == NULL) return -ENOMEM; + rt5645->i2c = i2c; i2c_set_clientdata(i2c, rt5645); if (pdata) @@ -2345,12 +2418,38 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, } + if (rt5645->i2c->irq) { + ret = request_threaded_irq(rt5645->i2c->irq, NULL, rt5645_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING + | IRQF_ONESHOT, "rt5645", rt5645); + if (ret) + dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + } + + if (gpio_is_valid(rt5645->pdata.hp_det_gpio)) { + ret = gpio_request(rt5645->pdata.hp_det_gpio, "rt5645"); + if (ret) + dev_err(&i2c->dev, "Fail gpio_request hp_det_gpio\n"); + + ret = gpio_direction_input(rt5645->pdata.hp_det_gpio); + if (ret) + dev_err(&i2c->dev, "Fail gpio_direction hp_det_gpio\n"); + } + return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5645, rt5645_dai, ARRAY_SIZE(rt5645_dai)); } static int rt5645_i2c_remove(struct i2c_client *i2c) { + struct rt5645_priv *rt5645 = i2c_get_clientdata(i2c); + + if (i2c->irq) + free_irq(i2c->irq, rt5645); + + if (gpio_is_valid(rt5645->pdata.hp_det_gpio)) + gpio_free(rt5645->pdata.hp_det_gpio); + snd_soc_unregister_codec(&i2c->dev); return 0; diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index 355b7e9..50c62c5 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -2166,6 +2166,8 @@ struct rt5645_priv { struct snd_soc_codec *codec; struct rt5645_platform_data pdata; struct regmap *regmap; + struct i2c_client *i2c; + struct snd_soc_jack *jack; int sysclk; int sysclk_src; @@ -2178,4 +2180,7 @@ struct rt5645_priv { int pll_out; }; +int rt5645_set_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack); + #endif /* __RT5645_H__ */ diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 5337c44..16aa4d9 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -15,10 +15,12 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> +#include <linux/of_gpio.h> #include <linux/regmap.h> #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/spi/spi.h> +#include <linux/gpio.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -540,6 +542,7 @@ static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0); static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0); static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0); static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); +static const DECLARE_TLV_DB_SCALE(st_vol_tlv, -4650, 150, 0); /* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ static unsigned int bst_tlv[] = { @@ -604,6 +607,10 @@ static const struct snd_kcontrol_new rt5677_snd_controls[] = { RT5677_MONO_ADC_L_VOL_SFT, RT5677_MONO_ADC_R_VOL_SFT, 127, 0, adc_vol_tlv), + /* Sidetone Control */ + SOC_SINGLE_TLV("Sidetone Volume", RT5677_SIDETONE_CTRL, + RT5677_ST_VOL_SFT, 31, 0, st_vol_tlv), + /* ADC Boost Volume Control */ SOC_DOUBLE_TLV("STO1 ADC Boost Volume", RT5677_STO1_2_ADC_BST, RT5677_STO1_ADC_L_BST_SFT, RT5677_STO1_ADC_R_BST_SFT, 3, 0, @@ -1700,14 +1707,19 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = { SND_SOC_DAPM_INPUT("Haptic Generator"), - SND_SOC_DAPM_PGA("DMIC1", RT5677_DMIC_CTRL1, RT5677_DMIC_1_EN_SFT, 0, - NULL, 0), - SND_SOC_DAPM_PGA("DMIC2", RT5677_DMIC_CTRL1, RT5677_DMIC_2_EN_SFT, 0, - NULL, 0), - SND_SOC_DAPM_PGA("DMIC3", RT5677_DMIC_CTRL1, RT5677_DMIC_3_EN_SFT, 0, - NULL, 0), - SND_SOC_DAPM_PGA("DMIC4", RT5677_DMIC_CTRL2, RT5677_DMIC_4_EN_SFT, 0, - NULL, 0), + SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC4", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DMIC1 power", RT5677_DMIC_CTRL1, + RT5677_DMIC_1_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC2 power", RT5677_DMIC_CTRL1, + RT5677_DMIC_2_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC3 power", RT5677_DMIC_CTRL1, + RT5677_DMIC_3_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC4 power", RT5677_DMIC_CTRL2, + RT5677_DMIC_4_EN_SFT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0, set_dmic_clk, SND_SOC_DAPM_PRE_PMU), @@ -1987,6 +1999,9 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = { /* Sidetone Mux */ SND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0, &rt5677_sidetone_mux), + SND_SOC_DAPM_SUPPLY("Sidetone Power", RT5677_SIDETONE_CTRL, + RT5677_ST_EN_SFT, 0, NULL, 0), + /* VAD Mux*/ SND_SOC_DAPM_MUX("VAD ADC Mux", SND_SOC_NOPM, 0, 0, &rt5677_vad_src_mux), @@ -2130,6 +2145,13 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = { { "DMIC L4", NULL, "DMIC CLK" }, { "DMIC R4", NULL, "DMIC CLK" }, + { "DMIC L1", NULL, "DMIC1 power" }, + { "DMIC R1", NULL, "DMIC1 power" }, + { "DMIC L3", NULL, "DMIC3 power" }, + { "DMIC R3", NULL, "DMIC3 power" }, + { "DMIC L4", NULL, "DMIC4 power" }, + { "DMIC R4", NULL, "DMIC4 power" }, + { "BST1", NULL, "IN1P" }, { "BST1", NULL, "IN1N" }, { "BST2", NULL, "IN2P" }, @@ -2691,6 +2713,7 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = { { "Sidetone Mux", "DMIC4 L", "DMIC L4" }, { "Sidetone Mux", "ADC1", "ADC 1" }, { "Sidetone Mux", "ADC2", "ADC 2" }, + { "Sidetone Mux", NULL, "Sidetone Power" }, { "Stereo DAC MIXL", "ST L Switch", "Sidetone Mux" }, { "Stereo DAC MIXL", "DAC1 L Switch", "DAC1 MIXL" }, @@ -2793,6 +2816,16 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = { { "PDM2R", NULL, "PDM2 R Mux" }, }; +static const struct snd_soc_dapm_route rt5677_dmic2_clk_1[] = { + { "DMIC L2", NULL, "DMIC1 power" }, + { "DMIC R2", NULL, "DMIC1 power" }, +}; + +static const struct snd_soc_dapm_route rt5677_dmic2_clk_2[] = { + { "DMIC L2", NULL, "DMIC2 power" }, + { "DMIC R2", NULL, "DMIC2 power" }, +}; + static int rt5677_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { @@ -3084,6 +3117,59 @@ static int rt5677_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, return 0; } +static int rt5677_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_codec *codec = dai->codec; + unsigned int val = 0; + + if (rx_mask || tx_mask) + val |= (1 << 12); + + switch (slots) { + case 4: + val |= (1 << 10); + break; + case 6: + val |= (2 << 10); + break; + case 8: + val |= (3 << 10); + break; + case 2: + default: + break; + } + + switch (slot_width) { + case 20: + val |= (1 << 8); + break; + case 24: + val |= (2 << 8); + break; + case 32: + val |= (3 << 8); + break; + case 16: + default: + break; + } + + switch (dai->id) { + case RT5677_AIF1: + snd_soc_update_bits(codec, RT5677_TDM1_CTRL1, 0x1f00, val); + break; + case RT5677_AIF2: + snd_soc_update_bits(codec, RT5677_TDM2_CTRL1, 0x1f00, val); + break; + default: + break; + } + + return 0; +} + static int rt5677_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { @@ -3138,12 +3224,148 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec, return 0; } +#ifdef CONFIG_GPIOLIB +static inline struct rt5677_priv *gpio_to_rt5677(struct gpio_chip *chip) +{ + return container_of(chip, struct rt5677_priv, gpio_chip); +} + +static void rt5677_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct rt5677_priv *rt5677 = gpio_to_rt5677(chip); + + switch (offset) { + case RT5677_GPIO1 ... RT5677_GPIO5: + regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2, + 0x1 << (offset * 3 + 1), !!value << (offset * 3 + 1)); + break; + + case RT5677_GPIO6: + regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL3, + RT5677_GPIO6_OUT_MASK, !!value << RT5677_GPIO6_OUT_SFT); + break; + + default: + break; + } +} + +static int rt5677_gpio_direction_out(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct rt5677_priv *rt5677 = gpio_to_rt5677(chip); + + switch (offset) { + case RT5677_GPIO1 ... RT5677_GPIO5: + regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2, + 0x3 << (offset * 3 + 1), + (0x2 | !!value) << (offset * 3 + 1)); + break; + + case RT5677_GPIO6: + regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL3, + RT5677_GPIO6_DIR_MASK | RT5677_GPIO6_OUT_MASK, + RT5677_GPIO6_DIR_OUT | !!value << RT5677_GPIO6_OUT_SFT); + break; + + default: + break; + } + + return 0; +} + +static int rt5677_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct rt5677_priv *rt5677 = gpio_to_rt5677(chip); + int value, ret; + + ret = regmap_read(rt5677->regmap, RT5677_GPIO_ST, &value); + if (ret < 0) + return ret; + + return (value & (0x1 << offset)) >> offset; +} + +static int rt5677_gpio_direction_in(struct gpio_chip *chip, unsigned offset) +{ + struct rt5677_priv *rt5677 = gpio_to_rt5677(chip); + + switch (offset) { + case RT5677_GPIO1 ... RT5677_GPIO5: + regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2, + 0x1 << (offset * 3 + 2), 0x0); + break; + + case RT5677_GPIO6: + regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL3, + RT5677_GPIO6_DIR_MASK, RT5677_GPIO6_DIR_IN); + break; + + default: + break; + } + + return 0; +} + +static struct gpio_chip rt5677_template_chip = { + .label = "rt5677", + .owner = THIS_MODULE, + .direction_output = rt5677_gpio_direction_out, + .set = rt5677_gpio_set, + .direction_input = rt5677_gpio_direction_in, + .get = rt5677_gpio_get, + .can_sleep = 1, +}; + +static void rt5677_init_gpio(struct i2c_client *i2c) +{ + struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c); + int ret; + + rt5677->gpio_chip = rt5677_template_chip; + rt5677->gpio_chip.ngpio = RT5677_GPIO_NUM; + rt5677->gpio_chip.dev = &i2c->dev; + rt5677->gpio_chip.base = -1; + + ret = gpiochip_add(&rt5677->gpio_chip); + if (ret != 0) + dev_err(&i2c->dev, "Failed to add GPIOs: %d\n", ret); +} + +static void rt5677_free_gpio(struct i2c_client *i2c) +{ + struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c); + + gpiochip_remove(&rt5677->gpio_chip); +} +#else +static void rt5677_init_gpio(struct i2c_client *i2c) +{ +} + +static void rt5677_free_gpio(struct i2c_client *i2c) +{ +} +#endif + static int rt5677_probe(struct snd_soc_codec *codec) { struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); rt5677->codec = codec; + if (rt5677->pdata.dmic2_clk_pin == RT5677_DMIC_CLK2) { + snd_soc_dapm_add_routes(&codec->dapm, + rt5677_dmic2_clk_2, + ARRAY_SIZE(rt5677_dmic2_clk_2)); + } else { /*use dmic1 clock by default*/ + snd_soc_dapm_add_routes(&codec->dapm, + rt5677_dmic2_clk_1, + ARRAY_SIZE(rt5677_dmic2_clk_1)); + } + rt5677_set_bias_level(codec, SND_SOC_BIAS_OFF); regmap_write(rt5677->regmap, RT5677_DIG_MISC, 0x0020); @@ -3157,6 +3379,8 @@ static int rt5677_remove(struct snd_soc_codec *codec) struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec); + if (gpio_is_valid(rt5677->pow_ldo2)) + gpio_set_value_cansleep(rt5677->pow_ldo2, 0); return 0; } @@ -3168,6 +3392,8 @@ static int rt5677_suspend(struct snd_soc_codec *codec) regcache_cache_only(rt5677->regmap, true); regcache_mark_dirty(rt5677->regmap); + if (gpio_is_valid(rt5677->pow_ldo2)) + gpio_set_value_cansleep(rt5677->pow_ldo2, 0); return 0; } @@ -3176,6 +3402,10 @@ static int rt5677_resume(struct snd_soc_codec *codec) { struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); + if (gpio_is_valid(rt5677->pow_ldo2)) { + gpio_set_value_cansleep(rt5677->pow_ldo2, 1); + msleep(10); + } regcache_cache_only(rt5677->regmap, false); regcache_sync(rt5677->regmap); @@ -3195,6 +3425,7 @@ static struct snd_soc_dai_ops rt5677_aif_dai_ops = { .set_fmt = rt5677_set_dai_fmt, .set_sysclk = rt5677_set_dai_sysclk, .set_pll = rt5677_set_dai_pll, + .set_tdm_slot = rt5677_set_tdm_slot, }; static struct snd_soc_dai_driver rt5677_dai[] = { @@ -3333,6 +3564,35 @@ static const struct i2c_device_id rt5677_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, rt5677_i2c_id); +static int rt5677_parse_dt(struct rt5677_priv *rt5677, struct device_node *np) +{ + rt5677->pdata.in1_diff = of_property_read_bool(np, + "realtek,in1-differential"); + rt5677->pdata.in2_diff = of_property_read_bool(np, + "realtek,in2-differential"); + rt5677->pdata.lout1_diff = of_property_read_bool(np, + "realtek,lout1-differential"); + rt5677->pdata.lout2_diff = of_property_read_bool(np, + "realtek,lout2-differential"); + rt5677->pdata.lout3_diff = of_property_read_bool(np, + "realtek,lout3-differential"); + + rt5677->pow_ldo2 = of_get_named_gpio(np, + "realtek,pow-ldo2-gpio", 0); + + /* + * POW_LDO2 is optional (it may be statically tied on the board). + * -ENOENT means that the property doesn't exist, i.e. there is no + * GPIO, so is not an error. Any other error code means the property + * exists, but could not be parsed. + */ + if (!gpio_is_valid(rt5677->pow_ldo2) && + (rt5677->pow_ldo2 != -ENOENT)) + return rt5677->pow_ldo2; + + return 0; +} + static int rt5677_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -3351,6 +3611,33 @@ static int rt5677_i2c_probe(struct i2c_client *i2c, if (pdata) rt5677->pdata = *pdata; + if (i2c->dev.of_node) { + ret = rt5677_parse_dt(rt5677, i2c->dev.of_node); + if (ret) { + dev_err(&i2c->dev, "Failed to parse device tree: %d\n", + ret); + return ret; + } + } else { + rt5677->pow_ldo2 = -EINVAL; + } + + if (gpio_is_valid(rt5677->pow_ldo2)) { + ret = devm_gpio_request_one(&i2c->dev, rt5677->pow_ldo2, + GPIOF_OUT_INIT_HIGH, + "RT5677 POW_LDO2"); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to request POW_LDO2 %d: %d\n", + rt5677->pow_ldo2, ret); + return ret; + } + /* Wait a while until I2C bus becomes available. The datasheet + * does not specify the exact we should wait but startup + * sequence mentiones at least a few milliseconds. + */ + msleep(10); + } + rt5677->regmap = devm_regmap_init_i2c(i2c, &rt5677_regmap); if (IS_ERR(rt5677->regmap)) { ret = PTR_ERR(rt5677->regmap); @@ -3381,6 +3668,29 @@ static int rt5677_i2c_probe(struct i2c_client *i2c, regmap_update_bits(rt5677->regmap, RT5677_IN1, RT5677_IN_DF2, RT5677_IN_DF2); + if (rt5677->pdata.lout1_diff) + regmap_update_bits(rt5677->regmap, RT5677_LOUT1, + RT5677_LOUT1_L_DF, RT5677_LOUT1_L_DF); + + if (rt5677->pdata.lout2_diff) + regmap_update_bits(rt5677->regmap, RT5677_LOUT1, + RT5677_LOUT2_L_DF, RT5677_LOUT2_L_DF); + + if (rt5677->pdata.lout3_diff) + regmap_update_bits(rt5677->regmap, RT5677_LOUT1, + RT5677_LOUT3_L_DF, RT5677_LOUT3_L_DF); + + if (rt5677->pdata.dmic2_clk_pin == RT5677_DMIC_CLK2) { + regmap_update_bits(rt5677->regmap, RT5677_GEN_CTRL2, + RT5677_GPIO5_FUNC_MASK, + RT5677_GPIO5_FUNC_DMIC); + regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2, + RT5677_GPIO5_DIR_MASK, + RT5677_GPIO5_DIR_OUT); + } + + rt5677_init_gpio(i2c); + return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5677, rt5677_dai, ARRAY_SIZE(rt5677_dai)); } @@ -3388,6 +3698,7 @@ static int rt5677_i2c_probe(struct i2c_client *i2c, static int rt5677_i2c_remove(struct i2c_client *i2c) { snd_soc_unregister_codec(&i2c->dev); + rt5677_free_gpio(i2c); return 0; } diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h index 863393e..d4eb6d5 100644 --- a/sound/soc/codecs/rt5677.h +++ b/sound/soc/codecs/rt5677.h @@ -382,6 +382,10 @@ #define RT5677_ST_SEL_SFT 9 #define RT5677_ST_EN (0x1 << 6) #define RT5677_ST_EN_SFT 6 +#define RT5677_ST_GAIN (0x1 << 5) +#define RT5677_ST_GAIN_SFT 5 +#define RT5677_ST_VOL_MASK (0x1f << 0) +#define RT5677_ST_VOL_SFT 0 /* Analog DAC1/2/3 Source Control (0x15) */ #define RT5677_ANA_DAC3_SRC_SEL_MASK (0x3 << 4) @@ -1287,16 +1291,16 @@ #define RT5677_PLL1_PD_SFT 8 #define RT5677_PLL1_PD_1 (0x0 << 8) #define RT5677_PLL1_PD_2 (0x1 << 8) -#define RT5671_DAC_OSR_MASK (0x3 << 6) -#define RT5671_DAC_OSR_SFT 6 -#define RT5671_DAC_OSR_128 (0x0 << 6) -#define RT5671_DAC_OSR_64 (0x1 << 6) -#define RT5671_DAC_OSR_32 (0x2 << 6) -#define RT5671_ADC_OSR_MASK (0x3 << 4) -#define RT5671_ADC_OSR_SFT 4 -#define RT5671_ADC_OSR_128 (0x0 << 4) -#define RT5671_ADC_OSR_64 (0x1 << 4) -#define RT5671_ADC_OSR_32 (0x2 << 4) +#define RT5677_DAC_OSR_MASK (0x3 << 6) +#define RT5677_DAC_OSR_SFT 6 +#define RT5677_DAC_OSR_128 (0x0 << 6) +#define RT5677_DAC_OSR_64 (0x1 << 6) +#define RT5677_DAC_OSR_32 (0x2 << 6) +#define RT5677_ADC_OSR_MASK (0x3 << 4) +#define RT5677_ADC_OSR_SFT 4 +#define RT5677_ADC_OSR_128 (0x0 << 4) +#define RT5677_ADC_OSR_64 (0x1 << 4) +#define RT5677_ADC_OSR_32 (0x2 << 4) /* Global Clock Control 2 (0x81) */ #define RT5677_PLL2_PR_SRC_MASK (0x1 << 15) @@ -1312,18 +1316,18 @@ #define RT5677_PLL2_SRC_BCLK4 (0x4 << 12) #define RT5677_PLL2_SRC_RCCLK (0x5 << 12) #define RT5677_PLL2_SRC_SLIM (0x6 << 12) -#define RT5671_DSP_ASRC_O_SRC (0x3 << 10) -#define RT5671_DSP_ASRC_O_SRC_SFT 10 -#define RT5671_DSP_ASRC_O_MCLK (0x0 << 10) -#define RT5671_DSP_ASRC_O_PLL1 (0x1 << 10) -#define RT5671_DSP_ASRC_O_SLIM (0x2 << 10) -#define RT5671_DSP_ASRC_O_RCCLK (0x3 << 10) -#define RT5671_DSP_ASRC_I_SRC (0x3 << 8) -#define RT5671_DSP_ASRC_I_SRC_SFT 8 -#define RT5671_DSP_ASRC_I_MCLK (0x0 << 8) -#define RT5671_DSP_ASRC_I_PLL1 (0x1 << 8) -#define RT5671_DSP_ASRC_I_SLIM (0x2 << 8) -#define RT5671_DSP_ASRC_I_RCCLK (0x3 << 8) +#define RT5677_DSP_ASRC_O_SRC (0x3 << 10) +#define RT5677_DSP_ASRC_O_SRC_SFT 10 +#define RT5677_DSP_ASRC_O_MCLK (0x0 << 10) +#define RT5677_DSP_ASRC_O_PLL1 (0x1 << 10) +#define RT5677_DSP_ASRC_O_SLIM (0x2 << 10) +#define RT5677_DSP_ASRC_O_RCCLK (0x3 << 10) +#define RT5677_DSP_ASRC_I_SRC (0x3 << 8) +#define RT5677_DSP_ASRC_I_SRC_SFT 8 +#define RT5677_DSP_ASRC_I_MCLK (0x0 << 8) +#define RT5677_DSP_ASRC_I_PLL1 (0x1 << 8) +#define RT5677_DSP_ASRC_I_SLIM (0x2 << 8) +#define RT5677_DSP_ASRC_I_RCCLK (0x3 << 8) #define RT5677_DSP_CLK_SRC_MASK (0x1 << 7) #define RT5677_DSP_CLK_SRC_SFT 7 #define RT5677_DSP_CLK_SRC_PLL2 (0x0 << 7) @@ -1363,6 +1367,110 @@ #define RT5677_SEL_SRC_IB01 (0x1 << 0) #define RT5677_SEL_SRC_IB01_SFT 0 +/* GPIO status (0xbf) */ +#define RT5677_GPIO6_STATUS_MASK (0x1 << 5) +#define RT5677_GPIO6_STATUS_SFT 5 +#define RT5677_GPIO5_STATUS_MASK (0x1 << 4) +#define RT5677_GPIO5_STATUS_SFT 4 +#define RT5677_GPIO4_STATUS_MASK (0x1 << 3) +#define RT5677_GPIO4_STATUS_SFT 3 +#define RT5677_GPIO3_STATUS_MASK (0x1 << 2) +#define RT5677_GPIO3_STATUS_SFT 2 +#define RT5677_GPIO2_STATUS_MASK (0x1 << 1) +#define RT5677_GPIO2_STATUS_SFT 1 +#define RT5677_GPIO1_STATUS_MASK (0x1 << 0) +#define RT5677_GPIO1_STATUS_SFT 0 + +/* GPIO Control 1 (0xc0) */ +#define RT5677_GPIO1_PIN_MASK (0x1 << 15) +#define RT5677_GPIO1_PIN_SFT 15 +#define RT5677_GPIO1_PIN_GPIO1 (0x0 << 15) +#define RT5677_GPIO1_PIN_IRQ (0x1 << 15) +#define RT5677_IPTV_MODE_MASK (0x1 << 14) +#define RT5677_IPTV_MODE_SFT 14 +#define RT5677_IPTV_MODE_GPIO (0x0 << 14) +#define RT5677_IPTV_MODE_IPTV (0x1 << 14) +#define RT5677_FUNC_MODE_MASK (0x1 << 13) +#define RT5677_FUNC_MODE_SFT 13 +#define RT5677_FUNC_MODE_DMIC_GPIO (0x0 << 13) +#define RT5677_FUNC_MODE_JTAG (0x1 << 13) + +/* GPIO Control 2 (0xc1) */ +#define RT5677_GPIO5_DIR_MASK (0x1 << 14) +#define RT5677_GPIO5_DIR_SFT 14 +#define RT5677_GPIO5_DIR_IN (0x0 << 14) +#define RT5677_GPIO5_DIR_OUT (0x1 << 14) +#define RT5677_GPIO5_OUT_MASK (0x1 << 13) +#define RT5677_GPIO5_OUT_SFT 13 +#define RT5677_GPIO5_OUT_LO (0x0 << 13) +#define RT5677_GPIO5_OUT_HI (0x1 << 13) +#define RT5677_GPIO5_P_MASK (0x1 << 12) +#define RT5677_GPIO5_P_SFT 12 +#define RT5677_GPIO5_P_NOR (0x0 << 12) +#define RT5677_GPIO5_P_INV (0x1 << 12) +#define RT5677_GPIO4_DIR_MASK (0x1 << 11) +#define RT5677_GPIO4_DIR_SFT 11 +#define RT5677_GPIO4_DIR_IN (0x0 << 11) +#define RT5677_GPIO4_DIR_OUT (0x1 << 11) +#define RT5677_GPIO4_OUT_MASK (0x1 << 10) +#define RT5677_GPIO4_OUT_SFT 10 +#define RT5677_GPIO4_OUT_LO (0x0 << 10) +#define RT5677_GPIO4_OUT_HI (0x1 << 10) +#define RT5677_GPIO4_P_MASK (0x1 << 9) +#define RT5677_GPIO4_P_SFT 9 +#define RT5677_GPIO4_P_NOR (0x0 << 9) +#define RT5677_GPIO4_P_INV (0x1 << 9) +#define RT5677_GPIO3_DIR_MASK (0x1 << 8) +#define RT5677_GPIO3_DIR_SFT 8 +#define RT5677_GPIO3_DIR_IN (0x0 << 8) +#define RT5677_GPIO3_DIR_OUT (0x1 << 8) +#define RT5677_GPIO3_OUT_MASK (0x1 << 7) +#define RT5677_GPIO3_OUT_SFT 7 +#define RT5677_GPIO3_OUT_LO (0x0 << 7) +#define RT5677_GPIO3_OUT_HI (0x1 << 7) +#define RT5677_GPIO3_P_MASK (0x1 << 6) +#define RT5677_GPIO3_P_SFT 6 +#define RT5677_GPIO3_P_NOR (0x0 << 6) +#define RT5677_GPIO3_P_INV (0x1 << 6) +#define RT5677_GPIO2_DIR_MASK (0x1 << 5) +#define RT5677_GPIO2_DIR_SFT 5 +#define RT5677_GPIO2_DIR_IN (0x0 << 5) +#define RT5677_GPIO2_DIR_OUT (0x1 << 5) +#define RT5677_GPIO2_OUT_MASK (0x1 << 4) +#define RT5677_GPIO2_OUT_SFT 4 +#define RT5677_GPIO2_OUT_LO (0x0 << 4) +#define RT5677_GPIO2_OUT_HI (0x1 << 4) +#define RT5677_GPIO2_P_MASK (0x1 << 3) +#define RT5677_GPIO2_P_SFT 3 +#define RT5677_GPIO2_P_NOR (0x0 << 3) +#define RT5677_GPIO2_P_INV (0x1 << 3) +#define RT5677_GPIO1_DIR_MASK (0x1 << 2) +#define RT5677_GPIO1_DIR_SFT 2 +#define RT5677_GPIO1_DIR_IN (0x0 << 2) +#define RT5677_GPIO1_DIR_OUT (0x1 << 2) +#define RT5677_GPIO1_OUT_MASK (0x1 << 1) +#define RT5677_GPIO1_OUT_SFT 1 +#define RT5677_GPIO1_OUT_LO (0x0 << 1) +#define RT5677_GPIO1_OUT_HI (0x1 << 1) +#define RT5677_GPIO1_P_MASK (0x1 << 0) +#define RT5677_GPIO1_P_SFT 0 +#define RT5677_GPIO1_P_NOR (0x0 << 0) +#define RT5677_GPIO1_P_INV (0x1 << 0) + +/* GPIO Control 3 (0xc2) */ +#define RT5677_GPIO6_DIR_MASK (0x1 << 2) +#define RT5677_GPIO6_DIR_SFT 2 +#define RT5677_GPIO6_DIR_IN (0x0 << 2) +#define RT5677_GPIO6_DIR_OUT (0x1 << 2) +#define RT5677_GPIO6_OUT_MASK (0x1 << 1) +#define RT5677_GPIO6_OUT_SFT 1 +#define RT5677_GPIO6_OUT_LO (0x0 << 1) +#define RT5677_GPIO6_OUT_HI (0x1 << 1) +#define RT5677_GPIO6_P_MASK (0x1 << 0) +#define RT5677_GPIO6_P_SFT 0 +#define RT5677_GPIO6_P_NOR (0x0 << 0) +#define RT5677_GPIO6_P_INV (0x1 << 0) + /* Virtual DSP Mixer Control (0xf7 0xf8 0xf9) */ #define RT5677_DSP_IB_01_H (0x1 << 15) #define RT5677_DSP_IB_01_H_SFT 15 @@ -1393,6 +1501,11 @@ #define RT5677_DSP_IB_9_L (0x1 << 1) #define RT5677_DSP_IB_9_L_SFT 1 +/* General Control2 (0xfc)*/ +#define RT5677_GPIO5_FUNC_MASK (0x1 << 9) +#define RT5677_GPIO5_FUNC_GPIO (0x0 << 9) +#define RT5677_GPIO5_FUNC_DMIC (0x1 << 9) + /* System Clock Source */ enum { RT5677_SCLK_S_MCLK, @@ -1418,6 +1531,16 @@ enum { RT5677_AIFS, }; +enum { + RT5677_GPIO1, + RT5677_GPIO2, + RT5677_GPIO3, + RT5677_GPIO4, + RT5677_GPIO5, + RT5677_GPIO6, + RT5677_GPIO_NUM, +}; + struct rt5677_priv { struct snd_soc_codec *codec; struct rt5677_platform_data pdata; @@ -1431,6 +1554,10 @@ struct rt5677_priv { int pll_src; int pll_in; int pll_out; + int pow_ldo2; /* POW_LDO2 pin */ +#ifdef CONFIG_GPIOLIB + struct gpio_chip gpio_chip; +#endif }; #endif /* __RT5677_H__ */ diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index e997d27..6bb77d7 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -626,6 +626,9 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate) } else { dev_err(codec->dev, "PLL not supported in slave mode\n"); + dev_err(codec->dev, "%d ratio is not supported. " + "SYS_MCLK needs to be 256, 384 or 512 * fs\n", + sgtl5000->sysclk / sys_fs); return -EINVAL; } } @@ -1073,26 +1076,6 @@ static bool sgtl5000_readable(struct device *dev, unsigned int reg) } } -#ifdef CONFIG_SUSPEND -static int sgtl5000_suspend(struct snd_soc_codec *codec) -{ - sgtl5000_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - -static int sgtl5000_resume(struct snd_soc_codec *codec) -{ - /* Bring the codec back up to standby to enable regulators */ - sgtl5000_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} -#else -#define sgtl5000_suspend NULL -#define sgtl5000_resume NULL -#endif /* CONFIG_SUSPEND */ - /* * sgtl5000 has 3 internal power supplies: * 1. VAG, normally set to vdda/2 @@ -1352,11 +1335,6 @@ static int sgtl5000_probe(struct snd_soc_codec *codec) */ snd_soc_write(codec, SGTL5000_DAP_CTRL, 0); - /* leading to standby state */ - ret = sgtl5000_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - if (ret) - goto err; - return 0; err: @@ -1373,8 +1351,6 @@ static int sgtl5000_remove(struct snd_soc_codec *codec) { struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); - sgtl5000_set_bias_level(codec, SND_SOC_BIAS_OFF); - regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), sgtl5000->supplies); regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies), @@ -1387,9 +1363,8 @@ static int sgtl5000_remove(struct snd_soc_codec *codec) static struct snd_soc_codec_driver sgtl5000_driver = { .probe = sgtl5000_probe, .remove = sgtl5000_remove, - .suspend = sgtl5000_suspend, - .resume = sgtl5000_resume, .set_bias_level = sgtl5000_set_bias_level, + .suspend_bias_off = true, .controls = sgtl5000_snd_controls, .num_controls = ARRAY_SIZE(sgtl5000_snd_controls), .dapm_widgets = sgtl5000_dapm_widgets, @@ -1442,6 +1417,7 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, { struct sgtl5000_priv *sgtl5000; int ret, reg, rev; + unsigned int mclk; sgtl5000 = devm_kzalloc(&client->dev, sizeof(struct sgtl5000_priv), GFP_KERNEL); @@ -1465,6 +1441,14 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, return ret; } + /* SGTL5000 SYS_MCLK should be between 8 and 27 MHz */ + mclk = clk_get_rate(sgtl5000->mclk); + if (mclk < 8000000 || mclk > 27000000) { + dev_err(&client->dev, "Invalid SYS_CLK frequency: %u.%03uMHz\n", + mclk / 1000000, mclk / 1000 % 1000); + return -EINVAL; + } + ret = clk_prepare_enable(sgtl5000->mclk); if (ret) return ret; diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c index e8680be..67ea55a 100644 --- a/sound/soc/codecs/ssm2518.c +++ b/sound/soc/codecs/ssm2518.c @@ -646,17 +646,6 @@ static struct snd_soc_dai_driver ssm2518_dai = { .ops = &ssm2518_dai_ops, }; -static int ssm2518_probe(struct snd_soc_codec *codec) -{ - return ssm2518_set_bias_level(codec, SND_SOC_BIAS_OFF); -} - -static int ssm2518_remove(struct snd_soc_codec *codec) -{ - ssm2518_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static int ssm2518_set_sysclk(struct snd_soc_codec *codec, int clk_id, int source, unsigned int freq, int dir) { @@ -727,8 +716,6 @@ static int ssm2518_set_sysclk(struct snd_soc_codec *codec, int clk_id, } static struct snd_soc_codec_driver ssm2518_codec_driver = { - .probe = ssm2518_probe, - .remove = ssm2518_remove, .set_bias_level = ssm2518_set_bias_level, .set_sysclk = ssm2518_set_sysclk, .idle_bias_off = true, diff --git a/sound/soc/codecs/ssm2602-i2c.c b/sound/soc/codecs/ssm2602-i2c.c index abd63d5..0d9779d 100644 --- a/sound/soc/codecs/ssm2602-i2c.c +++ b/sound/soc/codecs/ssm2602-i2c.c @@ -41,10 +41,19 @@ static const struct i2c_device_id ssm2602_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id); +static const struct of_device_id ssm2602_of_match[] = { + { .compatible = "adi,ssm2602", }, + { .compatible = "adi,ssm2603", }, + { .compatible = "adi,ssm2604", }, + { } +}; +MODULE_DEVICE_TABLE(of, ssm2602_of_match); + static struct i2c_driver ssm2602_i2c_driver = { .driver = { .name = "ssm2602", .owner = THIS_MODULE, + .of_match_table = ssm2602_of_match, }, .probe = ssm2602_i2c_probe, .remove = ssm2602_i2c_remove, diff --git a/sound/soc/codecs/ssm2602-spi.c b/sound/soc/codecs/ssm2602-spi.c index 2bf55e2..b5df14f 100644 --- a/sound/soc/codecs/ssm2602-spi.c +++ b/sound/soc/codecs/ssm2602-spi.c @@ -26,10 +26,17 @@ static int ssm2602_spi_remove(struct spi_device *spi) return 0; } +static const struct of_device_id ssm2602_of_match[] = { + { .compatible = "adi,ssm2602", }, + { } +}; +MODULE_DEVICE_TABLE(of, ssm2602_of_match); + static struct spi_driver ssm2602_spi_driver = { .driver = { .name = "ssm2602", .owner = THIS_MODULE, + .of_match_table = ssm2602_of_match, }, .probe = ssm2602_spi_probe, .remove = ssm2602_spi_remove, diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index 4021cd4..314eaec 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -192,7 +192,7 @@ static const struct snd_pcm_hw_constraint_list ssm2602_constraints_12288000 = { }; static const unsigned int ssm2602_rates_11289600[] = { - 8000, 44100, 88200, + 8000, 11025, 22050, 44100, 88200, }; static const struct snd_pcm_hw_constraint_list ssm2602_constraints_11289600 = { @@ -237,6 +237,16 @@ static const struct ssm2602_coeff ssm2602_coeff_table[] = { {18432000, 96000, SSM2602_COEFF_SRATE(0x7, 0x1, 0x0)}, {12000000, 96000, SSM2602_COEFF_SRATE(0x7, 0x0, 0x1)}, + /* 11.025k */ + {11289600, 11025, SSM2602_COEFF_SRATE(0xc, 0x0, 0x0)}, + {16934400, 11025, SSM2602_COEFF_SRATE(0xc, 0x1, 0x0)}, + {12000000, 11025, SSM2602_COEFF_SRATE(0xc, 0x1, 0x1)}, + + /* 22.05k */ + {11289600, 22050, SSM2602_COEFF_SRATE(0xd, 0x0, 0x0)}, + {16934400, 22050, SSM2602_COEFF_SRATE(0xd, 0x1, 0x0)}, + {12000000, 22050, SSM2602_COEFF_SRATE(0xd, 0x1, 0x1)}, + /* 44.1k */ {11289600, 44100, SSM2602_COEFF_SRATE(0x8, 0x0, 0x0)}, {16934400, 44100, SSM2602_COEFF_SRATE(0x8, 0x1, 0x0)}, @@ -467,7 +477,8 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec, return 0; } -#define SSM2602_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ +#define SSM2602_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ SNDRV_PCM_RATE_96000) @@ -502,18 +513,11 @@ static struct snd_soc_dai_driver ssm2602_dai = { .symmetric_samplebits = 1, }; -static int ssm2602_suspend(struct snd_soc_codec *codec) -{ - ssm2602_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static int ssm2602_resume(struct snd_soc_codec *codec) { struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec); regcache_sync(ssm2602->regmap); - ssm2602_set_bias_level(codec, SND_SOC_BIAS_STANDBY); return 0; } @@ -586,27 +590,14 @@ static int ssm260x_codec_probe(struct snd_soc_codec *codec) break; } - if (ret) - return ret; - - ssm2602_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} - -/* remove everything here */ -static int ssm2602_remove(struct snd_soc_codec *codec) -{ - ssm2602_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; + return ret; } static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = { .probe = ssm260x_codec_probe, - .remove = ssm2602_remove, - .suspend = ssm2602_suspend, .resume = ssm2602_resume, .set_bias_level = ssm2602_set_bias_level, + .suspend_bias_off = true, .controls = ssm260x_snd_controls, .num_controls = ARRAY_SIZE(ssm260x_snd_controls), diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c new file mode 100644 index 0000000..4b5c17f --- /dev/null +++ b/sound/soc/codecs/ssm4567.c @@ -0,0 +1,343 @@ +/* + * SSM4567 amplifier audio driver + * + * Copyright 2014 Google Chromium project. + * Author: Anatol Pomozov <anatol@chromium.org> + * + * Based on code copyright/by: + * Copyright 2013 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#define SSM4567_REG_POWER_CTRL 0x00 +#define SSM4567_REG_AMP_SNS_CTRL 0x01 +#define SSM4567_REG_DAC_CTRL 0x02 +#define SSM4567_REG_DAC_VOLUME 0x03 +#define SSM4567_REG_SAI_CTRL_1 0x04 +#define SSM4567_REG_SAI_CTRL_2 0x05 +#define SSM4567_REG_SAI_PLACEMENT_1 0x06 +#define SSM4567_REG_SAI_PLACEMENT_2 0x07 +#define SSM4567_REG_SAI_PLACEMENT_3 0x08 +#define SSM4567_REG_SAI_PLACEMENT_4 0x09 +#define SSM4567_REG_SAI_PLACEMENT_5 0x0a +#define SSM4567_REG_SAI_PLACEMENT_6 0x0b +#define SSM4567_REG_BATTERY_V_OUT 0x0c +#define SSM4567_REG_LIMITER_CTRL_1 0x0d +#define SSM4567_REG_LIMITER_CTRL_2 0x0e +#define SSM4567_REG_LIMITER_CTRL_3 0x0f +#define SSM4567_REG_STATUS_1 0x10 +#define SSM4567_REG_STATUS_2 0x11 +#define SSM4567_REG_FAULT_CTRL 0x12 +#define SSM4567_REG_PDM_CTRL 0x13 +#define SSM4567_REG_MCLK_RATIO 0x14 +#define SSM4567_REG_BOOST_CTRL_1 0x15 +#define SSM4567_REG_BOOST_CTRL_2 0x16 +#define SSM4567_REG_SOFT_RESET 0xff + +/* POWER_CTRL */ +#define SSM4567_POWER_APWDN_EN BIT(7) +#define SSM4567_POWER_BSNS_PWDN BIT(6) +#define SSM4567_POWER_VSNS_PWDN BIT(5) +#define SSM4567_POWER_ISNS_PWDN BIT(4) +#define SSM4567_POWER_BOOST_PWDN BIT(3) +#define SSM4567_POWER_AMP_PWDN BIT(2) +#define SSM4567_POWER_VBAT_ONLY BIT(1) +#define SSM4567_POWER_SPWDN BIT(0) + +/* DAC_CTRL */ +#define SSM4567_DAC_HV BIT(7) +#define SSM4567_DAC_MUTE BIT(6) +#define SSM4567_DAC_HPF BIT(5) +#define SSM4567_DAC_LPM BIT(4) +#define SSM4567_DAC_FS_MASK 0x7 +#define SSM4567_DAC_FS_8000_12000 0x0 +#define SSM4567_DAC_FS_16000_24000 0x1 +#define SSM4567_DAC_FS_32000_48000 0x2 +#define SSM4567_DAC_FS_64000_96000 0x3 +#define SSM4567_DAC_FS_128000_192000 0x4 + +struct ssm4567 { + struct regmap *regmap; +}; + +static const struct reg_default ssm4567_reg_defaults[] = { + { SSM4567_REG_POWER_CTRL, 0x81 }, + { SSM4567_REG_AMP_SNS_CTRL, 0x09 }, + { SSM4567_REG_DAC_CTRL, 0x32 }, + { SSM4567_REG_DAC_VOLUME, 0x40 }, + { SSM4567_REG_SAI_CTRL_1, 0x00 }, + { SSM4567_REG_SAI_CTRL_2, 0x08 }, + { SSM4567_REG_SAI_PLACEMENT_1, 0x01 }, + { SSM4567_REG_SAI_PLACEMENT_2, 0x20 }, + { SSM4567_REG_SAI_PLACEMENT_3, 0x32 }, + { SSM4567_REG_SAI_PLACEMENT_4, 0x07 }, + { SSM4567_REG_SAI_PLACEMENT_5, 0x07 }, + { SSM4567_REG_SAI_PLACEMENT_6, 0x07 }, + { SSM4567_REG_BATTERY_V_OUT, 0x00 }, + { SSM4567_REG_LIMITER_CTRL_1, 0xa4 }, + { SSM4567_REG_LIMITER_CTRL_2, 0x73 }, + { SSM4567_REG_LIMITER_CTRL_3, 0x00 }, + { SSM4567_REG_STATUS_1, 0x00 }, + { SSM4567_REG_STATUS_2, 0x00 }, + { SSM4567_REG_FAULT_CTRL, 0x30 }, + { SSM4567_REG_PDM_CTRL, 0x40 }, + { SSM4567_REG_MCLK_RATIO, 0x11 }, + { SSM4567_REG_BOOST_CTRL_1, 0x03 }, + { SSM4567_REG_BOOST_CTRL_2, 0x00 }, + { SSM4567_REG_SOFT_RESET, 0x00 }, +}; + + +static bool ssm4567_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SSM4567_REG_POWER_CTRL ... SSM4567_REG_BOOST_CTRL_2: + return true; + default: + return false; + } + +} + +static bool ssm4567_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SSM4567_REG_POWER_CTRL ... SSM4567_REG_SAI_PLACEMENT_6: + case SSM4567_REG_LIMITER_CTRL_1 ... SSM4567_REG_LIMITER_CTRL_3: + case SSM4567_REG_FAULT_CTRL ... SSM4567_REG_BOOST_CTRL_2: + /* The datasheet states that soft reset register is read-only, + * but logically it is write-only. */ + case SSM4567_REG_SOFT_RESET: + return true; + default: + return false; + } +} + +static bool ssm4567_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SSM4567_REG_BATTERY_V_OUT: + case SSM4567_REG_STATUS_1 ... SSM4567_REG_STATUS_2: + case SSM4567_REG_SOFT_RESET: + return true; + default: + return false; + } +} + +static const DECLARE_TLV_DB_MINMAX_MUTE(ssm4567_vol_tlv, -7125, 2400); + +static const struct snd_kcontrol_new ssm4567_snd_controls[] = { + SOC_SINGLE_TLV("Master Playback Volume", SSM4567_REG_DAC_VOLUME, 0, + 0xff, 1, ssm4567_vol_tlv), + SOC_SINGLE("DAC Low Power Mode Switch", SSM4567_REG_DAC_CTRL, 4, 1, 0), +}; + +static const struct snd_soc_dapm_widget ssm4567_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM4567_REG_POWER_CTRL, 2, 1), + + SND_SOC_DAPM_OUTPUT("OUT"), +}; + +static const struct snd_soc_dapm_route ssm4567_routes[] = { + { "OUT", NULL, "DAC" }, +}; + +static int ssm4567_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct ssm4567 *ssm4567 = snd_soc_codec_get_drvdata(codec); + unsigned int rate = params_rate(params); + unsigned int dacfs; + + if (rate >= 8000 && rate <= 12000) + dacfs = SSM4567_DAC_FS_8000_12000; + else if (rate >= 16000 && rate <= 24000) + dacfs = SSM4567_DAC_FS_16000_24000; + else if (rate >= 32000 && rate <= 48000) + dacfs = SSM4567_DAC_FS_32000_48000; + else if (rate >= 64000 && rate <= 96000) + dacfs = SSM4567_DAC_FS_64000_96000; + else if (rate >= 128000 && rate <= 192000) + dacfs = SSM4567_DAC_FS_128000_192000; + else + return -EINVAL; + + return regmap_update_bits(ssm4567->regmap, SSM4567_REG_DAC_CTRL, + SSM4567_DAC_FS_MASK, dacfs); +} + +static int ssm4567_mute(struct snd_soc_dai *dai, int mute) +{ + struct ssm4567 *ssm4567 = snd_soc_codec_get_drvdata(dai->codec); + unsigned int val; + + val = mute ? SSM4567_DAC_MUTE : 0; + return regmap_update_bits(ssm4567->regmap, SSM4567_REG_DAC_CTRL, + SSM4567_DAC_MUTE, val); +} + +static int ssm4567_set_power(struct ssm4567 *ssm4567, bool enable) +{ + int ret = 0; + + if (!enable) { + ret = regmap_update_bits(ssm4567->regmap, + SSM4567_REG_POWER_CTRL, + SSM4567_POWER_SPWDN, SSM4567_POWER_SPWDN); + regcache_mark_dirty(ssm4567->regmap); + } + + regcache_cache_only(ssm4567->regmap, !enable); + + if (enable) { + ret = regmap_update_bits(ssm4567->regmap, + SSM4567_REG_POWER_CTRL, + SSM4567_POWER_SPWDN, 0x00); + regcache_sync(ssm4567->regmap); + } + + return ret; +} + +static int ssm4567_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct ssm4567 *ssm4567 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + ret = ssm4567_set_power(ssm4567, true); + break; + case SND_SOC_BIAS_OFF: + ret = ssm4567_set_power(ssm4567, false); + break; + } + + if (ret) + return ret; + + codec->dapm.bias_level = level; + + return 0; +} + +static const struct snd_soc_dai_ops ssm4567_dai_ops = { + .hw_params = ssm4567_hw_params, + .digital_mute = ssm4567_mute, +}; + +static struct snd_soc_dai_driver ssm4567_dai = { + .name = "ssm4567-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32, + }, + .ops = &ssm4567_dai_ops, +}; + +static struct snd_soc_codec_driver ssm4567_codec_driver = { + .set_bias_level = ssm4567_set_bias_level, + .idle_bias_off = true, + + .controls = ssm4567_snd_controls, + .num_controls = ARRAY_SIZE(ssm4567_snd_controls), + .dapm_widgets = ssm4567_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ssm4567_dapm_widgets), + .dapm_routes = ssm4567_routes, + .num_dapm_routes = ARRAY_SIZE(ssm4567_routes), +}; + +static const struct regmap_config ssm4567_regmap_config = { + .val_bits = 8, + .reg_bits = 8, + + .max_register = SSM4567_REG_SOFT_RESET, + .readable_reg = ssm4567_readable_reg, + .writeable_reg = ssm4567_writeable_reg, + .volatile_reg = ssm4567_volatile_reg, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = ssm4567_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ssm4567_reg_defaults), +}; + +static int ssm4567_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct ssm4567 *ssm4567; + int ret; + + ssm4567 = devm_kzalloc(&i2c->dev, sizeof(*ssm4567), GFP_KERNEL); + if (ssm4567 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, ssm4567); + + ssm4567->regmap = devm_regmap_init_i2c(i2c, &ssm4567_regmap_config); + if (IS_ERR(ssm4567->regmap)) + return PTR_ERR(ssm4567->regmap); + + ret = regmap_write(ssm4567->regmap, SSM4567_REG_SOFT_RESET, 0x00); + if (ret) + return ret; + + ret = ssm4567_set_power(ssm4567, false); + if (ret) + return ret; + + return snd_soc_register_codec(&i2c->dev, &ssm4567_codec_driver, + &ssm4567_dai, 1); +} + +static int ssm4567_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id ssm4567_i2c_ids[] = { + { "ssm4567", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ssm4567_i2c_ids); + +static struct i2c_driver ssm4567_driver = { + .driver = { + .name = "ssm4567", + .owner = THIS_MODULE, + }, + .probe = ssm4567_i2c_probe, + .remove = ssm4567_i2c_remove, + .id_table = ssm4567_i2c_ids, +}; +module_i2c_driver(ssm4567_driver); + +MODULE_DESCRIPTION("ASoC SSM4567 driver"); +MODULE_AUTHOR("Anatol Pomozov <anatol@chromium.org>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index 23b3296..f039dc8 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c @@ -78,6 +78,44 @@ struct tas2552_data { unsigned int mclk; }; +/* Input mux controls */ +static const char *tas2552_input_texts[] = { + "Digital", "Analog" +}; + +static SOC_ENUM_SINGLE_DECL(tas2552_input_mux_enum, TAS2552_CFG_3, 7, + tas2552_input_texts); + +static const struct snd_kcontrol_new tas2552_input_mux_control[] = { + SOC_DAPM_ENUM("Input selection", tas2552_input_mux_enum) +}; + +static const struct snd_soc_dapm_widget tas2552_dapm_widgets[] = +{ + SND_SOC_DAPM_INPUT("IN"), + + /* MUX Controls */ + SND_SOC_DAPM_MUX("Input selection", SND_SOC_NOPM, 0, 0, + tas2552_input_mux_control), + + SND_SOC_DAPM_AIF_IN("DAC IN", "DAC Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUT_DRV("ClassD", TAS2552_CFG_2, 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL", TAS2552_CFG_2, 3, 0, NULL, 0), + + SND_SOC_DAPM_OUTPUT("OUT") +}; + +static const struct snd_soc_dapm_route tas2552_audio_map[] = { + {"DAC", NULL, "DAC IN"}, + {"Input selection", "Digital", "DAC"}, + {"Input selection", "Analog", "IN"}, + {"ClassD", NULL, "Input selection"}, + {"OUT", NULL, "ClassD"}, + {"ClassD", NULL, "PLL"}, +}; + +#ifdef CONFIG_PM_RUNTIME static void tas2552_sw_shutdown(struct tas2552_data *tas_data, int sw_shutdown) { u8 cfg1_reg; @@ -90,6 +128,7 @@ static void tas2552_sw_shutdown(struct tas2552_data *tas_data, int sw_shutdown) snd_soc_update_bits(tas_data->codec, TAS2552_CFG_1, TAS2552_SWS_MASK, cfg1_reg); } +#endif static int tas2552_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, @@ -101,10 +140,6 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream, int d; u8 p, j; - /* Turn on Class D amplifier */ - snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_CLASSD_EN_MASK, - TAS2552_CLASSD_EN); - if (!tas2552->mclk) return -EINVAL; @@ -147,9 +182,6 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream, } - snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, - TAS2552_PLL_ENABLE); - return 0; } @@ -269,19 +301,10 @@ static const struct dev_pm_ops tas2552_pm = { NULL) }; -static void tas2552_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_codec *codec = dai->codec; - - snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0); -} - static struct snd_soc_dai_ops tas2552_speaker_dai_ops = { .hw_params = tas2552_hw_params, .set_sysclk = tas2552_set_dai_sysclk, .set_fmt = tas2552_set_dai_fmt, - .shutdown = tas2552_shutdown, .digital_mute = tas2552_mute, }; @@ -294,7 +317,7 @@ static struct snd_soc_dai_driver tas2552_dai[] = { { .name = "tas2552-amplifier", .playback = { - .stream_name = "Speaker", + .stream_name = "Playback", .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_192000, @@ -312,6 +335,7 @@ static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 24); static const struct snd_kcontrol_new tas2552_snd_controls[] = { SOC_SINGLE_TLV("Speaker Driver Playback Volume", TAS2552_PGA_GAIN, 0, 0x1f, 1, dac_tlv), + SOC_DAPM_SINGLE("Playback AMP", SND_SOC_NOPM, 0, 1, 0), }; static const struct reg_default tas2552_init_regs[] = { @@ -321,6 +345,7 @@ static const struct reg_default tas2552_init_regs[] = { static int tas2552_codec_probe(struct snd_soc_codec *codec) { struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; tas2552->codec = codec; @@ -362,9 +387,14 @@ static int tas2552_codec_probe(struct snd_soc_codec *codec) goto patch_fail; } - snd_soc_write(codec, TAS2552_CFG_2, TAS2552_CLASSD_EN | - TAS2552_BOOST_EN | TAS2552_APT_EN | - TAS2552_LIM_EN); + snd_soc_write(codec, TAS2552_CFG_2, TAS2552_BOOST_EN | + TAS2552_APT_EN | TAS2552_LIM_EN); + + snd_soc_dapm_new_controls(dapm, tas2552_dapm_widgets, + ARRAY_SIZE(tas2552_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, tas2552_audio_map, + ARRAY_SIZE(tas2552_audio_map)); + return 0; patch_fail: diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index aea9e1f..145fe5b 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -167,13 +167,13 @@ struct aic31xx_priv { struct regulator_bulk_data supplies[AIC31XX_NUM_SUPPLIES]; struct aic31xx_disable_nb disable_nb[AIC31XX_NUM_SUPPLIES]; unsigned int sysclk; + u8 p_div; int rate_div_line; }; struct aic31xx_rate_divs { - u32 mclk; + u32 mclk_p; u32 rate; - u8 p_val; u8 pll_j; u16 pll_d; u16 dosr; @@ -186,62 +186,51 @@ struct aic31xx_rate_divs { /* ADC dividers can be disabled by cofiguring them to 0 */ static const struct aic31xx_rate_divs aic31xx_divs[] = { - /* mclk rate pll: p j d dosr ndac mdac aors nadc madc */ + /* mclk/p rate pll: j d dosr ndac mdac aors nadc madc */ /* 8k rate */ - {12000000, 8000, 1, 8, 1920, 128, 48, 2, 128, 48, 2}, - {12000000, 8000, 1, 8, 1920, 128, 32, 3, 128, 32, 3}, - {24000000, 8000, 2, 8, 1920, 128, 48, 2, 128, 48, 2}, - {25000000, 8000, 2, 7, 8643, 128, 48, 2, 128, 48, 2}, + {12000000, 8000, 8, 1920, 128, 48, 2, 128, 48, 2}, + {12000000, 8000, 8, 1920, 128, 32, 3, 128, 32, 3}, + {12500000, 8000, 7, 8643, 128, 48, 2, 128, 48, 2}, /* 11.025k rate */ - {12000000, 11025, 1, 7, 5264, 128, 32, 2, 128, 32, 2}, - {12000000, 11025, 1, 8, 4672, 128, 24, 3, 128, 24, 3}, - {24000000, 11025, 2, 7, 5264, 128, 32, 2, 128, 32, 2}, - {25000000, 11025, 2, 7, 2253, 128, 32, 2, 128, 32, 2}, + {12000000, 11025, 7, 5264, 128, 32, 2, 128, 32, 2}, + {12000000, 11025, 8, 4672, 128, 24, 3, 128, 24, 3}, + {12500000, 11025, 7, 2253, 128, 32, 2, 128, 32, 2}, /* 16k rate */ - {12000000, 16000, 1, 8, 1920, 128, 24, 2, 128, 24, 2}, - {12000000, 16000, 1, 8, 1920, 128, 16, 3, 128, 16, 3}, - {24000000, 16000, 2, 8, 1920, 128, 24, 2, 128, 24, 2}, - {25000000, 16000, 2, 7, 8643, 128, 24, 2, 128, 24, 2}, + {12000000, 16000, 8, 1920, 128, 24, 2, 128, 24, 2}, + {12000000, 16000, 8, 1920, 128, 16, 3, 128, 16, 3}, + {12500000, 16000, 7, 8643, 128, 24, 2, 128, 24, 2}, /* 22.05k rate */ - {12000000, 22050, 1, 7, 5264, 128, 16, 2, 128, 16, 2}, - {12000000, 22050, 1, 8, 4672, 128, 12, 3, 128, 12, 3}, - {24000000, 22050, 2, 7, 5264, 128, 16, 2, 128, 16, 2}, - {25000000, 22050, 2, 7, 2253, 128, 16, 2, 128, 16, 2}, + {12000000, 22050, 7, 5264, 128, 16, 2, 128, 16, 2}, + {12000000, 22050, 8, 4672, 128, 12, 3, 128, 12, 3}, + {12500000, 22050, 7, 2253, 128, 16, 2, 128, 16, 2}, /* 32k rate */ - {12000000, 32000, 1, 8, 1920, 128, 12, 2, 128, 12, 2}, - {12000000, 32000, 1, 8, 1920, 128, 8, 3, 128, 8, 3}, - {24000000, 32000, 2, 8, 1920, 128, 12, 2, 128, 12, 2}, - {25000000, 32000, 2, 7, 8643, 128, 12, 2, 128, 12, 2}, + {12000000, 32000, 8, 1920, 128, 12, 2, 128, 12, 2}, + {12000000, 32000, 8, 1920, 128, 8, 3, 128, 8, 3}, + {12500000, 32000, 7, 8643, 128, 12, 2, 128, 12, 2}, /* 44.1k rate */ - {12000000, 44100, 1, 7, 5264, 128, 8, 2, 128, 8, 2}, - {12000000, 44100, 1, 8, 4672, 128, 6, 3, 128, 6, 3}, - {24000000, 44100, 2, 7, 5264, 128, 8, 2, 128, 8, 2}, - {25000000, 44100, 2, 7, 2253, 128, 8, 2, 128, 8, 2}, + {12000000, 44100, 7, 5264, 128, 8, 2, 128, 8, 2}, + {12000000, 44100, 8, 4672, 128, 6, 3, 128, 6, 3}, + {12500000, 44100, 7, 2253, 128, 8, 2, 128, 8, 2}, /* 48k rate */ - {12000000, 48000, 1, 8, 1920, 128, 8, 2, 128, 8, 2}, - {12000000, 48000, 1, 7, 6800, 96, 5, 4, 96, 5, 4}, - {24000000, 48000, 2, 8, 1920, 128, 8, 2, 128, 8, 2}, - {25000000, 48000, 2, 7, 8643, 128, 8, 2, 128, 8, 2}, + {12000000, 48000, 8, 1920, 128, 8, 2, 128, 8, 2}, + {12000000, 48000, 7, 6800, 96, 5, 4, 96, 5, 4}, + {12500000, 48000, 7, 8643, 128, 8, 2, 128, 8, 2}, /* 88.2k rate */ - {12000000, 88200, 1, 7, 5264, 64, 8, 2, 64, 8, 2}, - {12000000, 88200, 1, 8, 4672, 64, 6, 3, 64, 6, 3}, - {24000000, 88200, 2, 7, 5264, 64, 8, 2, 64, 8, 2}, - {25000000, 88200, 2, 7, 2253, 64, 8, 2, 64, 8, 2}, + {12000000, 88200, 7, 5264, 64, 8, 2, 64, 8, 2}, + {12000000, 88200, 8, 4672, 64, 6, 3, 64, 6, 3}, + {12500000, 88200, 7, 2253, 64, 8, 2, 64, 8, 2}, /* 96k rate */ - {12000000, 96000, 1, 8, 1920, 64, 8, 2, 64, 8, 2}, - {12000000, 96000, 1, 7, 6800, 48, 5, 4, 48, 5, 4}, - {24000000, 96000, 2, 8, 1920, 64, 8, 2, 64, 8, 2}, - {25000000, 96000, 2, 7, 8643, 64, 8, 2, 64, 8, 2}, + {12000000, 96000, 8, 1920, 64, 8, 2, 64, 8, 2}, + {12000000, 96000, 7, 6800, 48, 5, 4, 48, 5, 4}, + {12500000, 96000, 7, 8643, 64, 8, 2, 64, 8, 2}, /* 176.4k rate */ - {12000000, 176400, 1, 7, 5264, 32, 8, 2, 32, 8, 2}, - {12000000, 176400, 1, 8, 4672, 32, 6, 3, 32, 6, 3}, - {24000000, 176400, 2, 7, 5264, 32, 8, 2, 32, 8, 2}, - {25000000, 176400, 2, 7, 2253, 32, 8, 2, 32, 8, 2}, + {12000000, 176400, 7, 5264, 32, 8, 2, 32, 8, 2}, + {12000000, 176400, 8, 4672, 32, 6, 3, 32, 6, 3}, + {12500000, 176400, 7, 2253, 32, 8, 2, 32, 8, 2}, /* 192k rate */ - {12000000, 192000, 1, 8, 1920, 32, 8, 2, 32, 8, 2}, - {12000000, 192000, 1, 7, 6800, 24, 5, 4, 24, 5, 4}, - {24000000, 192000, 2, 8, 1920, 32, 8, 2, 32, 8, 2}, - {25000000, 192000, 2, 7, 8643, 32, 8, 2, 32, 8, 2}, + {12000000, 192000, 8, 1920, 32, 8, 2, 32, 8, 2}, + {12000000, 192000, 7, 6800, 24, 5, 4, 24, 5, 4}, + {12500000, 192000, 7, 8643, 32, 8, 2, 32, 8, 2}, }; static const char * const ldac_in_text[] = { @@ -692,6 +681,7 @@ static int aic31xx_setup_pll(struct snd_soc_codec *codec, { struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec); int bclk_score = snd_soc_params_to_frame_size(params); + int mclk_p = aic31xx->sysclk / aic31xx->p_div; int bclk_n = 0; int match = -1; int i; @@ -704,7 +694,7 @@ static int aic31xx_setup_pll(struct snd_soc_codec *codec, for (i = 0; i < ARRAY_SIZE(aic31xx_divs); i++) { if (aic31xx_divs[i].rate == params_rate(params) && - aic31xx_divs[i].mclk == aic31xx->sysclk) { + aic31xx_divs[i].mclk_p == mclk_p) { int s = (aic31xx_divs[i].dosr * aic31xx_divs[i].mdac) % snd_soc_params_to_frame_size(params); int bn = (aic31xx_divs[i].dosr * aic31xx_divs[i].mdac) / @@ -738,7 +728,7 @@ static int aic31xx_setup_pll(struct snd_soc_codec *codec, /* PLL configuration */ snd_soc_update_bits(codec, AIC31XX_PLLPR, AIC31XX_PLL_MASK, - (aic31xx_divs[i].p_val << 4) | 0x01); + (aic31xx->p_div << 4) | 0x01); snd_soc_write(codec, AIC31XX_PLLJ, aic31xx_divs[i].pll_j); snd_soc_write(codec, AIC31XX_PLLDMSB, @@ -772,7 +762,7 @@ static int aic31xx_setup_pll(struct snd_soc_codec *codec, dev_dbg(codec->dev, "pll %d.%04d/%d dosr %d n %d m %d aosr %d n %d m %d bclk_n %d\n", aic31xx_divs[i].pll_j, aic31xx_divs[i].pll_d, - aic31xx_divs[i].p_val, aic31xx_divs[i].dosr, + aic31xx->p_div, aic31xx_divs[i].dosr, aic31xx_divs[i].ndac, aic31xx_divs[i].mdac, aic31xx_divs[i].aosr, aic31xx_divs[i].nadc, aic31xx_divs[i].madc, bclk_n); @@ -840,7 +830,7 @@ static int aic31xx_set_dai_fmt(struct snd_soc_dai *codec_dai, { struct snd_soc_codec *codec = codec_dai->codec; u8 iface_reg1 = 0; - u8 iface_reg3 = 0; + u8 iface_reg2 = 0; u8 dsp_a_val = 0; dev_dbg(codec->dev, "## %s: fmt = 0x%x\n", __func__, fmt); @@ -865,7 +855,7 @@ static int aic31xx_set_dai_fmt(struct snd_soc_dai *codec_dai, /* NOTE: BCLKINV bit value 1 equas NB and 0 equals IB */ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: - iface_reg3 |= AIC31XX_BCLKINV_MASK; + iface_reg2 |= AIC31XX_BCLKINV_MASK; break; case SND_SOC_DAIFMT_IB_NF: break; @@ -897,7 +887,7 @@ static int aic31xx_set_dai_fmt(struct snd_soc_dai *codec_dai, dsp_a_val); snd_soc_update_bits(codec, AIC31XX_IFACE2, AIC31XX_BCLKINV_MASK, - iface_reg3); + iface_reg2); return 0; } @@ -912,7 +902,16 @@ static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai, dev_dbg(codec->dev, "## %s: clk_id = %d, freq = %d, dir = %d\n", __func__, clk_id, freq, dir); - for (i = 0; aic31xx_divs[i].mclk != freq; i++) { + for (i = 1; freq/i > 20000000 && i < 8; i++) + ; + if (freq/i > 20000000) { + dev_err(aic31xx->dev, "%s: Too high mclk frequency %u\n", + __func__, freq); + return -EINVAL; + } + aic31xx->p_div = i; + + for (i = 0; aic31xx_divs[i].mclk_p != freq/aic31xx->p_div; i++) { if (i == ARRAY_SIZE(aic31xx_divs)) { dev_err(aic31xx->dev, "%s: Unsupported frequency %d\n", __func__, freq); diff --git a/sound/soc/codecs/tlv320aic31xx.h b/sound/soc/codecs/tlv320aic31xx.h index 52ed57c..fe16c34 100644 --- a/sound/soc/codecs/tlv320aic31xx.h +++ b/sound/soc/codecs/tlv320aic31xx.h @@ -18,7 +18,8 @@ #define AIC31XX_RATES SNDRV_PCM_RATE_8000_192000 #define AIC31XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ - | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE \ + | SNDRV_PCM_FMTBIT_S32_LE) #define AIC31XX_STEREO_CLASS_D_BIT 0x1 diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 64f179e..f7c2a57 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1121,6 +1121,7 @@ static int aic3x_regulator_event(struct notifier_block *nb, static int aic3x_set_power(struct snd_soc_codec *codec, int power) { struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); + unsigned int pll_c, pll_d; int ret; if (power) { @@ -1138,6 +1139,18 @@ static int aic3x_set_power(struct snd_soc_codec *codec, int power) /* Sync reg_cache with the hardware */ regcache_cache_only(aic3x->regmap, false); regcache_sync(aic3x->regmap); + + /* Rewrite paired PLL D registers in case cached sync skipped + * writing one of them and thus caused other one also not + * being written + */ + pll_c = snd_soc_read(codec, AIC3X_PLL_PROGC_REG); + pll_d = snd_soc_read(codec, AIC3X_PLL_PROGD_REG); + if (pll_c == aic3x_reg[AIC3X_PLL_PROGC_REG].def || + pll_d == aic3x_reg[AIC3X_PLL_PROGD_REG].def) { + snd_soc_write(codec, AIC3X_PLL_PROGC_REG, pll_c); + snd_soc_write(codec, AIC3X_PLL_PROGD_REG, pll_d); + } } else { /* * Do soft reset to this codec instance in order to clear @@ -1222,20 +1235,6 @@ static struct snd_soc_dai_driver aic3x_dai = { .symmetric_rates = 1, }; -static int aic3x_suspend(struct snd_soc_codec *codec) -{ - aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - -static int aic3x_resume(struct snd_soc_codec *codec) -{ - aic3x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} - static void aic3x_mono_init(struct snd_soc_codec *codec) { /* DAC to Mono Line Out default volume and route to Output mixer */ @@ -1429,8 +1428,6 @@ static struct snd_soc_codec_driver soc_codec_dev_aic3x = { .idle_bias_off = true, .probe = aic3x_probe, .remove = aic3x_remove, - .suspend = aic3x_suspend, - .resume = aic3x_resume, .controls = aic3x_snd_controls, .num_controls = ARRAY_SIZE(aic3x_snd_controls), .dapm_widgets = aic3x_dapm_widgets, diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index 3dfdcc4..628ec77 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -212,7 +212,7 @@ static void wm8350_pga_work(struct work_struct *work) { struct snd_soc_dapm_context *dapm = container_of(work, struct snd_soc_dapm_context, delayed_work.work); - struct snd_soc_codec *codec = dapm->codec; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec); struct wm8350_output *out1 = &wm8350_data->out1, *out2 = &wm8350_data->out2; diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index a237f16..31bb480 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -413,7 +413,6 @@ static int wm8741_resume(struct snd_soc_codec *codec) return 0; } #else -#define wm8741_suspend NULL #define wm8741_resume NULL #endif diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index e54e097..21ca3a9 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -1433,7 +1433,7 @@ static void wm8753_work(struct work_struct *work) struct snd_soc_dapm_context *dapm = container_of(work, struct snd_soc_dapm_context, delayed_work.work); - struct snd_soc_codec *codec = dapm->codec; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); wm8753_set_bias_level(codec, dapm->bias_level); } diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c index 0ea01df..3addc5f 100644 --- a/sound/soc/codecs/wm8804.c +++ b/sound/soc/codecs/wm8804.c @@ -518,23 +518,6 @@ static int wm8804_set_bias_level(struct snd_soc_codec *codec, return 0; } -#ifdef CONFIG_PM -static int wm8804_suspend(struct snd_soc_codec *codec) -{ - wm8804_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int wm8804_resume(struct snd_soc_codec *codec) -{ - wm8804_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} -#else -#define wm8804_suspend NULL -#define wm8804_resume NULL -#endif - static int wm8804_remove(struct snd_soc_codec *codec) { struct wm8804_priv *wm8804; @@ -671,8 +654,6 @@ static struct snd_soc_dai_driver wm8804_dai = { static struct snd_soc_codec_driver soc_codec_dev_wm8804 = { .probe = wm8804_probe, .remove = wm8804_remove, - .suspend = wm8804_suspend, - .resume = wm8804_resume, .set_bias_level = wm8804_set_bias_level, .idle_bias_off = true, diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c index 0499cd4..39ddb9b 100644 --- a/sound/soc/codecs/wm8971.c +++ b/sound/soc/codecs/wm8971.c @@ -615,7 +615,7 @@ static void wm8971_work(struct work_struct *work) struct snd_soc_dapm_context *dapm = container_of(work, struct snd_soc_dapm_context, delayed_work.work); - struct snd_soc_codec *codec = dapm->codec; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); wm8971_set_bias_level(codec, codec->dapm.bias_level); } diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 6cc0566..1fcb9f3 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -4082,17 +4082,23 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) switch (control->type) { case WM8994: - if (wm8994->micdet_irq) { + if (wm8994->micdet_irq) ret = request_threaded_irq(wm8994->micdet_irq, NULL, wm8994_mic_irq, IRQF_TRIGGER_RISING, "Mic1 detect", wm8994); - if (ret != 0) - dev_warn(codec->dev, - "Failed to request Mic1 detect IRQ: %d\n", - ret); - } + else + ret = wm8994_request_irq(wm8994->wm8994, + WM8994_IRQ_MIC1_DET, + wm8994_mic_irq, "Mic 1 detect", + wm8994); + + if (ret != 0) + dev_warn(codec->dev, + "Failed to request Mic1 detect IRQ: %d\n", + ret); + ret = wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_MIC1_SHRT, diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index cae4ac5..1288ede 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -1998,23 +1998,6 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec, return 0; } -#ifdef CONFIG_PM -static int wm8995_suspend(struct snd_soc_codec *codec) -{ - wm8995_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int wm8995_resume(struct snd_soc_codec *codec) -{ - wm8995_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} -#else -#define wm8995_suspend NULL -#define wm8995_resume NULL -#endif - static int wm8995_remove(struct snd_soc_codec *codec) { struct wm8995_priv *wm8995; @@ -2220,8 +2203,6 @@ static struct snd_soc_dai_driver wm8995_dai[] = { static struct snd_soc_codec_driver soc_codec_dev_wm8995 = { .probe = wm8995_probe, .remove = wm8995_remove, - .suspend = wm8995_suspend, - .resume = wm8995_resume, .set_bias_level = wm8995_set_bias_level, .idle_bias_off = true, }; diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig index d69510c..8e948c6 100644 --- a/sound/soc/davinci/Kconfig +++ b/sound/soc/davinci/Kconfig @@ -63,7 +63,8 @@ config SND_DM365_AIC3X_CODEC Say Y if you want to add support for AIC3101 audio codec config SND_DM365_VOICE_CODEC - bool "Voice Codec - CQ93VC" + tristate "Voice Codec - CQ93VC" + depends on SND_DAVINCI_SOC select MFD_DAVINCI_VOICECODEC select SND_DAVINCI_SOC_VCIF select SND_SOC_CQ0093VC diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 68347b5..0eed9b1 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -42,14 +42,26 @@ #define MCASP_MAX_AFIFO_DEPTH 64 +static u32 context_regs[] = { + DAVINCI_MCASP_TXFMCTL_REG, + DAVINCI_MCASP_RXFMCTL_REG, + DAVINCI_MCASP_TXFMT_REG, + DAVINCI_MCASP_RXFMT_REG, + DAVINCI_MCASP_ACLKXCTL_REG, + DAVINCI_MCASP_ACLKRCTL_REG, + DAVINCI_MCASP_AHCLKXCTL_REG, + DAVINCI_MCASP_AHCLKRCTL_REG, + DAVINCI_MCASP_PDIR_REG, + DAVINCI_MCASP_RXMASK_REG, + DAVINCI_MCASP_TXMASK_REG, + DAVINCI_MCASP_RXTDM_REG, + DAVINCI_MCASP_TXTDM_REG, +}; + struct davinci_mcasp_context { - u32 txfmtctl; - u32 rxfmtctl; - u32 txfmt; - u32 rxfmt; - u32 aclkxctl; - u32 aclkrctl; - u32 pdir; + u32 config_regs[ARRAY_SIZE(context_regs)]; + u32 afifo_regs[2]; /* for read/write fifo control registers */ + u32 *xrsr_regs; /* for serializer configuration */ }; struct davinci_mcasp { @@ -874,14 +886,24 @@ static int davinci_mcasp_suspend(struct snd_soc_dai *dai) { struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); struct davinci_mcasp_context *context = &mcasp->context; + u32 reg; + int i; + + for (i = 0; i < ARRAY_SIZE(context_regs); i++) + context->config_regs[i] = mcasp_get_reg(mcasp, context_regs[i]); + + if (mcasp->txnumevt) { + reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET; + context->afifo_regs[0] = mcasp_get_reg(mcasp, reg); + } + if (mcasp->rxnumevt) { + reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET; + context->afifo_regs[1] = mcasp_get_reg(mcasp, reg); + } - context->txfmtctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG); - context->rxfmtctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG); - context->txfmt = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXFMT_REG); - context->rxfmt = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMT_REG); - context->aclkxctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG); - context->aclkrctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKRCTL_REG); - context->pdir = mcasp_get_reg(mcasp, DAVINCI_MCASP_PDIR_REG); + for (i = 0; i < mcasp->num_serializer; i++) + context->xrsr_regs[i] = mcasp_get_reg(mcasp, + DAVINCI_MCASP_XRSRCTL_REG(i)); return 0; } @@ -890,14 +912,24 @@ static int davinci_mcasp_resume(struct snd_soc_dai *dai) { struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); struct davinci_mcasp_context *context = &mcasp->context; + u32 reg; + int i; + + for (i = 0; i < ARRAY_SIZE(context_regs); i++) + mcasp_set_reg(mcasp, context_regs[i], context->config_regs[i]); + + if (mcasp->txnumevt) { + reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET; + mcasp_set_reg(mcasp, reg, context->afifo_regs[0]); + } + if (mcasp->rxnumevt) { + reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET; + mcasp_set_reg(mcasp, reg, context->afifo_regs[1]); + } - mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG, context->txfmtctl); - mcasp_set_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG, context->rxfmtctl); - mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMT_REG, context->txfmt); - mcasp_set_reg(mcasp, DAVINCI_MCASP_RXFMT_REG, context->rxfmt); - mcasp_set_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, context->aclkxctl); - mcasp_set_reg(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, context->aclkrctl); - mcasp_set_reg(mcasp, DAVINCI_MCASP_PDIR_REG, context->pdir); + for (i = 0; i < mcasp->num_serializer; i++) + mcasp_set_reg(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i), + context->xrsr_regs[i]); return 0; } @@ -1216,6 +1248,11 @@ static int davinci_mcasp_probe(struct platform_device *pdev) mcasp->op_mode = pdata->op_mode; mcasp->tdm_slots = pdata->tdm_slots; mcasp->num_serializer = pdata->num_serializer; +#ifdef CONFIG_PM_SLEEP + mcasp->context.xrsr_regs = devm_kzalloc(&pdev->dev, + sizeof(u32) * mcasp->num_serializer, + GFP_KERNEL); +#endif mcasp->serial_dir = pdata->serial_dir; mcasp->version = pdata->version; mcasp->txnumevt = pdata->txnumevt; diff --git a/sound/soc/davinci/edma-pcm.c b/sound/soc/davinci/edma-pcm.c index 605e643..59e588a 100644 --- a/sound/soc/davinci/edma-pcm.c +++ b/sound/soc/davinci/edma-pcm.c @@ -25,6 +25,8 @@ #include <sound/dmaengine_pcm.h> #include <linux/edma.h> +#include "edma-pcm.h" + static const struct snd_pcm_hardware edma_pcm_hardware = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index f3012b6..081e406 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -240,6 +240,18 @@ config SND_SOC_IMX_WM8962 Say Y if you want to add support for SoC audio on an i.MX board with a wm8962 codec. +config SND_SOC_IMX_ES8328 + tristate "SoC Audio support for i.MX boards with the ES8328 codec" + depends on OF && (I2C || SPI) + select SND_SOC_ES8328_I2C if I2C + select SND_SOC_ES8328_SPI if SPI_MASTER + select SND_SOC_IMX_PCM_DMA + select SND_SOC_IMX_AUDMUX + select SND_SOC_FSL_SSI + help + Say Y if you want to add support for the ES8328 audio codec connected + via SSI/I2S over either SPI or I2C. + config SND_SOC_IMX_SGTL5000 tristate "SoC Audio support for i.MX boards with sgtl5000" depends on OF && I2C @@ -268,6 +280,20 @@ config SND_SOC_IMX_MC13783 select SND_SOC_MC13783 select SND_SOC_IMX_PCM_DMA +config SND_SOC_FSL_ASOC_CARD + tristate "Generic ASoC Sound Card with ASRC support" + depends on OF && I2C + select SND_SOC_IMX_AUDMUX + select SND_SOC_IMX_PCM_DMA + select SND_SOC_FSL_ESAI + select SND_SOC_FSL_SAI + select SND_SOC_FSL_SSI + help + ALSA SoC Audio support with ASRC feature for Freescale SoCs that have + ESAI/SAI/SSI and connect with external CODECs such as WM8962, CS42888 + and SGTL5000. + Say Y if you want to add support for Freescale Generic ASoC Sound Card. + endif # SND_IMX_SOC endmenu diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 9ff5926..d28dc25 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -11,6 +11,7 @@ snd-soc-p1022-rdk-objs := p1022_rdk.o obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o # Freescale SSI/DMA/SAI/SPDIF Support +snd-soc-fsl-asoc-card-objs := fsl-asoc-card.o snd-soc-fsl-asrc-objs := fsl_asrc.o fsl_asrc_dma.o snd-soc-fsl-sai-objs := fsl_sai.o snd-soc-fsl-ssi-y := fsl_ssi.o @@ -19,6 +20,7 @@ snd-soc-fsl-spdif-objs := fsl_spdif.o snd-soc-fsl-esai-objs := fsl_esai.o snd-soc-fsl-utils-objs := fsl_utils.o snd-soc-fsl-dma-objs := fsl_dma.o +obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o obj-$(CONFIG_SND_SOC_FSL_ASRC) += snd-soc-fsl-asrc.o obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o @@ -50,6 +52,7 @@ snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o snd-soc-phycore-ac97-objs := phycore-ac97.o snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o snd-soc-wm1133-ev1-objs := wm1133-ev1.o +snd-soc-imx-es8328-objs := imx-es8328.o snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o snd-soc-imx-wm8962-objs := imx-wm8962.o snd-soc-imx-spdif-objs := imx-spdif.o @@ -59,6 +62,7 @@ obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o +obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c new file mode 100644 index 0000000..007c772 --- /dev/null +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -0,0 +1,574 @@ +/* + * Freescale Generic ASoC Sound Card driver with ASRC + * + * Copyright (C) 2014 Freescale Semiconductor, Inc. + * + * Author: Nicolin Chen <nicoleotsuka@gmail.com> + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include <linux/clk.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "fsl_esai.h" +#include "fsl_sai.h" +#include "imx-audmux.h" + +#include "../codecs/sgtl5000.h" +#include "../codecs/wm8962.h" + +#define RX 0 +#define TX 1 + +/* Default DAI format without Master and Slave flag */ +#define DAI_FMT_BASE (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF) + +/** + * CODEC private data + * + * @mclk_freq: Clock rate of MCLK + * @mclk_id: MCLK (or main clock) id for set_sysclk() + * @fll_id: FLL (or secordary clock) id for set_sysclk() + * @pll_id: PLL id for set_pll() + */ +struct codec_priv { + unsigned long mclk_freq; + u32 mclk_id; + u32 fll_id; + u32 pll_id; +}; + +/** + * CPU private data + * + * @sysclk_freq[2]: SYSCLK rates for set_sysclk() + * @sysclk_dir[2]: SYSCLK directions for set_sysclk() + * @sysclk_id[2]: SYSCLK ids for set_sysclk() + * + * Note: [1] for tx and [0] for rx + */ +struct cpu_priv { + unsigned long sysclk_freq[2]; + u32 sysclk_dir[2]; + u32 sysclk_id[2]; +}; + +/** + * Freescale Generic ASOC card private data + * + * @dai_link[3]: DAI link structure including normal one and DPCM link + * @pdev: platform device pointer + * @codec_priv: CODEC private data + * @cpu_priv: CPU private data + * @card: ASoC card structure + * @sample_rate: Current sample rate + * @sample_format: Current sample format + * @asrc_rate: ASRC sample rate used by Back-Ends + * @asrc_format: ASRC sample format used by Back-Ends + * @dai_fmt: DAI format between CPU and CODEC + * @name: Card name + */ + +struct fsl_asoc_card_priv { + struct snd_soc_dai_link dai_link[3]; + struct platform_device *pdev; + struct codec_priv codec_priv; + struct cpu_priv cpu_priv; + struct snd_soc_card card; + u32 sample_rate; + u32 sample_format; + u32 asrc_rate; + u32 asrc_format; + u32 dai_fmt; + char name[32]; +}; + +/** + * This dapm route map exsits for DPCM link only. + * The other routes shall go through Device Tree. + */ +static const struct snd_soc_dapm_route audio_map[] = { + {"CPU-Playback", NULL, "ASRC-Playback"}, + {"Playback", NULL, "CPU-Playback"}, + {"ASRC-Capture", NULL, "CPU-Capture"}, + {"CPU-Capture", NULL, "Capture"}, +}; + +/* Add all possible widgets into here without being redundant */ +static const struct snd_soc_dapm_widget fsl_asoc_card_dapm_widgets[] = { + SND_SOC_DAPM_LINE("Line Out Jack", NULL), + SND_SOC_DAPM_LINE("Line In Jack", NULL), + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_SPK("Ext Spk", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_MIC("AMIC", NULL), + SND_SOC_DAPM_MIC("DMIC", NULL), +}; + +static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + struct cpu_priv *cpu_priv = &priv->cpu_priv; + struct device *dev = rtd->card->dev; + int ret; + + priv->sample_rate = params_rate(params); + priv->sample_format = params_format(params); + + if (priv->card.set_bias_level) + return 0; + + /* Specific configurations of DAIs starts from here */ + ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, cpu_priv->sysclk_id[tx], + cpu_priv->sysclk_freq[tx], + cpu_priv->sysclk_dir[tx]); + if (ret) { + dev_err(dev, "failed to set sysclk for cpu dai\n"); + return ret; + } + + return 0; +} + +static struct snd_soc_ops fsl_asoc_card_ops = { + .hw_params = fsl_asoc_card_hw_params, +}; + +static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct snd_interval *rate; + struct snd_mask *mask; + + rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + rate->max = rate->min = priv->asrc_rate; + + mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + snd_mask_none(mask); + snd_mask_set(mask, priv->asrc_format); + + return 0; +} + +static struct snd_soc_dai_link fsl_asoc_card_dai[] = { + /* Default ASoC DAI Link*/ + { + .name = "HiFi", + .stream_name = "HiFi", + .ops = &fsl_asoc_card_ops, + }, + /* DPCM Link between Front-End and Back-End (Optional) */ + { + .name = "HiFi-ASRC-FE", + .stream_name = "HiFi-ASRC-FE", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .dpcm_playback = 1, + .dpcm_capture = 1, + .dynamic = 1, + }, + { + .name = "HiFi-ASRC-BE", + .stream_name = "HiFi-ASRC-BE", + .platform_name = "snd-soc-dummy", + .be_hw_params_fixup = be_hw_params_fixup, + .ops = &fsl_asoc_card_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + .no_pcm = 1, + }, +}; + +static int fsl_asoc_card_set_bias_level(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm, + enum snd_soc_bias_level level) +{ + struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; + struct codec_priv *codec_priv = &priv->codec_priv; + struct device *dev = card->dev; + unsigned int pll_out; + int ret; + + if (dapm->dev != codec_dai->dev) + return 0; + + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (dapm->bias_level != SND_SOC_BIAS_STANDBY) + break; + + if (priv->sample_format == SNDRV_PCM_FORMAT_S24_LE) + pll_out = priv->sample_rate * 384; + else + pll_out = priv->sample_rate * 256; + + ret = snd_soc_dai_set_pll(codec_dai, codec_priv->pll_id, + codec_priv->mclk_id, + codec_priv->mclk_freq, pll_out); + if (ret) { + dev_err(dev, "failed to start FLL: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->fll_id, + pll_out, SND_SOC_CLOCK_IN); + if (ret) { + dev_err(dev, "failed to set SYSCLK: %d\n", ret); + return ret; + } + break; + + case SND_SOC_BIAS_STANDBY: + if (dapm->bias_level != SND_SOC_BIAS_PREPARE) + break; + + ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, + codec_priv->mclk_freq, + SND_SOC_CLOCK_IN); + if (ret) { + dev_err(dev, "failed to switch away from FLL: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_pll(codec_dai, codec_priv->pll_id, 0, 0, 0); + if (ret) { + dev_err(dev, "failed to stop FLL: %d\n", ret); + return ret; + } + break; + + default: + break; + } + + return 0; +} + +static int fsl_asoc_card_audmux_init(struct device_node *np, + struct fsl_asoc_card_priv *priv) +{ + struct device *dev = &priv->pdev->dev; + u32 int_ptcr = 0, ext_ptcr = 0; + int int_port, ext_port; + int ret; + + ret = of_property_read_u32(np, "mux-int-port", &int_port); + if (ret) { + dev_err(dev, "mux-int-port missing or invalid\n"); + return ret; + } + ret = of_property_read_u32(np, "mux-ext-port", &ext_port); + if (ret) { + dev_err(dev, "mux-ext-port missing or invalid\n"); + return ret; + } + + /* + * The port numbering in the hardware manual starts at 1, while + * the AUDMUX API expects it starts at 0. + */ + int_port--; + ext_port--; + + /* + * Use asynchronous mode (6 wires) for all cases. + * If only 4 wires are needed, just set SSI into + * synchronous mode and enable 4 PADs in IOMUX. + */ + switch (priv->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + int_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | ext_port) | + IMX_AUDMUX_V2_PTCR_RCSEL(8 | ext_port) | + IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) | + IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) | + IMX_AUDMUX_V2_PTCR_RFSDIR | + IMX_AUDMUX_V2_PTCR_RCLKDIR | + IMX_AUDMUX_V2_PTCR_TFSDIR | + IMX_AUDMUX_V2_PTCR_TCLKDIR; + break; + case SND_SOC_DAIFMT_CBM_CFS: + int_ptcr = IMX_AUDMUX_V2_PTCR_RCSEL(8 | ext_port) | + IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) | + IMX_AUDMUX_V2_PTCR_RCLKDIR | + IMX_AUDMUX_V2_PTCR_TCLKDIR; + ext_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | int_port) | + IMX_AUDMUX_V2_PTCR_TFSEL(int_port) | + IMX_AUDMUX_V2_PTCR_RFSDIR | + IMX_AUDMUX_V2_PTCR_TFSDIR; + break; + case SND_SOC_DAIFMT_CBS_CFM: + int_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | ext_port) | + IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) | + IMX_AUDMUX_V2_PTCR_RFSDIR | + IMX_AUDMUX_V2_PTCR_TFSDIR; + ext_ptcr = IMX_AUDMUX_V2_PTCR_RCSEL(8 | int_port) | + IMX_AUDMUX_V2_PTCR_TCSEL(int_port) | + IMX_AUDMUX_V2_PTCR_RCLKDIR | + IMX_AUDMUX_V2_PTCR_TCLKDIR; + break; + case SND_SOC_DAIFMT_CBS_CFS: + ext_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | int_port) | + IMX_AUDMUX_V2_PTCR_RCSEL(8 | int_port) | + IMX_AUDMUX_V2_PTCR_TFSEL(int_port) | + IMX_AUDMUX_V2_PTCR_TCSEL(int_port) | + IMX_AUDMUX_V2_PTCR_RFSDIR | + IMX_AUDMUX_V2_PTCR_RCLKDIR | + IMX_AUDMUX_V2_PTCR_TFSDIR | + IMX_AUDMUX_V2_PTCR_TCLKDIR; + break; + default: + return -EINVAL; + } + + /* Asynchronous mode can not be set along with RCLKDIR */ + ret = imx_audmux_v2_configure_port(int_port, 0, + IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port)); + if (ret) { + dev_err(dev, "audmux internal port setup failed\n"); + return ret; + } + + ret = imx_audmux_v2_configure_port(int_port, int_ptcr, + IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port)); + if (ret) { + dev_err(dev, "audmux internal port setup failed\n"); + return ret; + } + + ret = imx_audmux_v2_configure_port(ext_port, 0, + IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)); + if (ret) { + dev_err(dev, "audmux external port setup failed\n"); + return ret; + } + + ret = imx_audmux_v2_configure_port(ext_port, ext_ptcr, + IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)); + if (ret) { + dev_err(dev, "audmux external port setup failed\n"); + return ret; + } + + return 0; +} + +static int fsl_asoc_card_late_probe(struct snd_soc_card *card) +{ + struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; + struct codec_priv *codec_priv = &priv->codec_priv; + struct device *dev = card->dev; + int ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, + codec_priv->mclk_freq, SND_SOC_CLOCK_IN); + if (ret) { + dev_err(dev, "failed to set sysclk in %s\n", __func__); + return ret; + } + + return 0; +} + +static int fsl_asoc_card_probe(struct platform_device *pdev) +{ + struct device_node *cpu_np, *codec_np, *asrc_np; + struct device_node *np = pdev->dev.of_node; + struct platform_device *asrc_pdev = NULL; + struct platform_device *cpu_pdev; + struct fsl_asoc_card_priv *priv; + struct i2c_client *codec_dev; + struct clk *codec_clk; + u32 width; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + cpu_np = of_parse_phandle(np, "audio-cpu", 0); + /* Give a chance to old DT binding */ + if (!cpu_np) + cpu_np = of_parse_phandle(np, "ssi-controller", 0); + codec_np = of_parse_phandle(np, "audio-codec", 0); + if (!cpu_np || !codec_np) { + dev_err(&pdev->dev, "phandle missing or invalid\n"); + ret = -EINVAL; + goto fail; + } + + cpu_pdev = of_find_device_by_node(cpu_np); + if (!cpu_pdev) { + dev_err(&pdev->dev, "failed to find CPU DAI device\n"); + ret = -EINVAL; + goto fail; + } + + codec_dev = of_find_i2c_device_by_node(codec_np); + if (!codec_dev) { + dev_err(&pdev->dev, "failed to find codec platform device\n"); + ret = -EINVAL; + goto fail; + } + + asrc_np = of_parse_phandle(np, "audio-asrc", 0); + if (asrc_np) + asrc_pdev = of_find_device_by_node(asrc_np); + + /* Get the MCLK rate only, and leave it controlled by CODEC drivers */ + codec_clk = clk_get(&codec_dev->dev, NULL); + if (!IS_ERR(codec_clk)) { + priv->codec_priv.mclk_freq = clk_get_rate(codec_clk); + clk_put(codec_clk); + } + + /* Default sample rate and format, will be updated in hw_params() */ + priv->sample_rate = 44100; + priv->sample_format = SNDRV_PCM_FORMAT_S16_LE; + + /* Assign a default DAI format, and allow each card to overwrite it */ + priv->dai_fmt = DAI_FMT_BASE; + + /* Diversify the card configurations */ + if (of_device_is_compatible(np, "fsl,imx-audio-cs42888")) { + priv->card.set_bias_level = NULL; + priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv.mclk_freq; + priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv.mclk_freq; + priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; + priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT; + priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; + } else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) { + priv->codec_priv.mclk_id = SGTL5000_SYSCLK; + priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; + } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) { + priv->card.set_bias_level = fsl_asoc_card_set_bias_level; + priv->codec_priv.mclk_id = WM8962_SYSCLK_MCLK; + priv->codec_priv.fll_id = WM8962_SYSCLK_FLL; + priv->codec_priv.pll_id = WM8962_FLL; + priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; + } else { + dev_err(&pdev->dev, "unknown Device Tree compatible\n"); + return -EINVAL; + } + + /* Common settings for corresponding Freescale CPU DAI driver */ + if (strstr(cpu_np->name, "ssi")) { + /* Only SSI needs to configure AUDMUX */ + ret = fsl_asoc_card_audmux_init(np, priv); + if (ret) { + dev_err(&pdev->dev, "failed to init audmux\n"); + goto asrc_fail; + } + } else if (strstr(cpu_np->name, "esai")) { + priv->cpu_priv.sysclk_id[1] = ESAI_HCKT_EXTAL; + priv->cpu_priv.sysclk_id[0] = ESAI_HCKR_EXTAL; + } else if (strstr(cpu_np->name, "sai")) { + priv->cpu_priv.sysclk_id[1] = FSL_SAI_CLK_MAST1; + priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1; + } + + sprintf(priv->name, "%s-audio", codec_dev->name); + + /* Initialize sound card */ + priv->pdev = pdev; + priv->card.dev = &pdev->dev; + priv->card.name = priv->name; + priv->card.dai_link = priv->dai_link; + priv->card.dapm_routes = audio_map; + priv->card.late_probe = fsl_asoc_card_late_probe; + priv->card.num_dapm_routes = ARRAY_SIZE(audio_map); + priv->card.dapm_widgets = fsl_asoc_card_dapm_widgets; + priv->card.num_dapm_widgets = ARRAY_SIZE(fsl_asoc_card_dapm_widgets); + + memcpy(priv->dai_link, fsl_asoc_card_dai, + sizeof(struct snd_soc_dai_link) * ARRAY_SIZE(priv->dai_link)); + + /* Normal DAI Link */ + priv->dai_link[0].cpu_of_node = cpu_np; + priv->dai_link[0].codec_of_node = codec_np; + priv->dai_link[0].codec_dai_name = codec_dev->name; + priv->dai_link[0].platform_of_node = cpu_np; + priv->dai_link[0].dai_fmt = priv->dai_fmt; + priv->card.num_links = 1; + + if (asrc_pdev) { + /* DPCM DAI Links only if ASRC exsits */ + priv->dai_link[1].cpu_of_node = asrc_np; + priv->dai_link[1].platform_of_node = asrc_np; + priv->dai_link[2].codec_dai_name = codec_dev->name; + priv->dai_link[2].codec_of_node = codec_np; + priv->dai_link[2].cpu_of_node = cpu_np; + priv->dai_link[2].dai_fmt = priv->dai_fmt; + priv->card.num_links = 3; + + ret = of_property_read_u32(asrc_np, "fsl,asrc-rate", + &priv->asrc_rate); + if (ret) { + dev_err(&pdev->dev, "failed to get output rate\n"); + ret = -EINVAL; + goto asrc_fail; + } + + ret = of_property_read_u32(asrc_np, "fsl,asrc-width", &width); + if (ret) { + dev_err(&pdev->dev, "failed to get output rate\n"); + ret = -EINVAL; + goto asrc_fail; + } + + if (width == 24) + priv->asrc_format = SNDRV_PCM_FORMAT_S24_LE; + else + priv->asrc_format = SNDRV_PCM_FORMAT_S16_LE; + } + + /* Finish card registering */ + platform_set_drvdata(pdev, priv); + snd_soc_card_set_drvdata(&priv->card, priv); + + ret = devm_snd_soc_register_card(&pdev->dev, &priv->card); + if (ret) + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); + +asrc_fail: + of_node_put(asrc_np); +fail: + of_node_put(codec_np); + of_node_put(cpu_np); + + return ret; +} + +static const struct of_device_id fsl_asoc_card_dt_ids[] = { + { .compatible = "fsl,imx-audio-cs42888", }, + { .compatible = "fsl,imx-audio-sgtl5000", }, + { .compatible = "fsl,imx-audio-wm8962", }, + {} +}; + +static struct platform_driver fsl_asoc_card_driver = { + .probe = fsl_asoc_card_probe, + .driver = { + .name = "fsl-asoc-card", + .pm = &snd_soc_pm_ops, + .of_match_table = fsl_asoc_card_dt_ids, + }, +}; +module_platform_driver(fsl_asoc_card_driver); + +MODULE_DESCRIPTION("Freescale Generic ASoC Sound Card driver with ASRC"); +MODULE_AUTHOR("Nicolin Chen <nicoleotsuka@gmail.com>"); +MODULE_ALIAS("platform:fsl-asoc-card"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 8221104..3b14531 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -684,7 +684,7 @@ static bool fsl_asrc_writeable_reg(struct device *dev, unsigned int reg) } } -static struct regmap_config fsl_asrc_regmap_config = { +static const struct regmap_config fsl_asrc_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, @@ -802,10 +802,6 @@ static int fsl_asrc_probe(struct platform_device *pdev) asrc_priv->paddr = res->start; - /* Register regmap and let it prepare core clock */ - if (of_property_read_bool(np, "big-endian")) - fsl_asrc_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG; - asrc_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "mem", regs, &fsl_asrc_regmap_config); if (IS_ERR(asrc_priv->regmap)) { diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index a3b29ed..8bcdfda 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -37,6 +37,7 @@ * @fsysclk: system clock source to derive HCK, SCK and FS * @fifo_depth: depth of tx/rx FIFO * @slot_width: width of each DAI slot + * @slots: number of slots * @hck_rate: clock rate of desired HCKx clock * @sck_rate: clock rate of desired SCKx clock * @hck_dir: the direction of HCKx pads @@ -55,6 +56,7 @@ struct fsl_esai { struct clk *fsysclk; u32 fifo_depth; u32 slot_width; + u32 slots; u32 hck_rate[2]; u32 sck_rate[2]; bool hck_dir[2]; @@ -362,6 +364,7 @@ static int fsl_esai_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, ESAI_xSMB_xS_MASK, ESAI_xSMB_xS(rx_mask)); esai_priv->slot_width = slot_width; + esai_priv->slots = slots; return 0; } @@ -509,10 +512,11 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream, bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; u32 width = snd_pcm_format_width(params_format(params)); u32 channels = params_channels(params); + u32 pins = DIV_ROUND_UP(channels, esai_priv->slots); u32 bclk, mask, val; int ret; - bclk = params_rate(params) * esai_priv->slot_width * 2; + bclk = params_rate(params) * esai_priv->slot_width * esai_priv->slots; ret = fsl_esai_set_bclk(dai, tx, bclk); if (ret) @@ -529,7 +533,7 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream, mask = ESAI_xFCR_xFR_MASK | ESAI_xFCR_xWA_MASK | ESAI_xFCR_xFWM_MASK | (tx ? ESAI_xFCR_TE_MASK | ESAI_xFCR_TIEN : ESAI_xFCR_RE_MASK); val = ESAI_xFCR_xWA(width) | ESAI_xFCR_xFWM(esai_priv->fifo_depth) | - (tx ? ESAI_xFCR_TE(channels) | ESAI_xFCR_TIEN : ESAI_xFCR_RE(channels)); + (tx ? ESAI_xFCR_TE(pins) | ESAI_xFCR_TIEN : ESAI_xFCR_RE(pins)); regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), mask, val); @@ -564,6 +568,7 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; u8 i, channels = substream->runtime->channels; + u32 pins = DIV_ROUND_UP(channels, esai_priv->slots); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -578,7 +583,7 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, - tx ? ESAI_xCR_TE(channels) : ESAI_xCR_RE(channels)); + tx ? ESAI_xCR_TE(pins) : ESAI_xCR_RE(pins)); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: @@ -705,7 +710,7 @@ static bool fsl_esai_writeable_reg(struct device *dev, unsigned int reg) } } -static struct regmap_config fsl_esai_regmap_config = { +static const struct regmap_config fsl_esai_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, @@ -731,9 +736,6 @@ static int fsl_esai_probe(struct platform_device *pdev) esai_priv->pdev = pdev; strcpy(esai_priv->name, np->name); - if (of_property_read_bool(np, "big-endian")) - fsl_esai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG; - /* Get the addresses and IRQ */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = devm_ioremap_resource(&pdev->dev, res); @@ -781,6 +783,9 @@ static int fsl_esai_probe(struct platform_device *pdev) /* Set a default slot size */ esai_priv->slot_width = 32; + /* Set a default slot number */ + esai_priv->slots = 2; + /* Set a default master/slave state */ esai_priv->slave_mode = true; diff --git a/sound/soc/fsl/fsl_esai.h b/sound/soc/fsl/fsl_esai.h index 75e1403..91a550f 100644 --- a/sound/soc/fsl/fsl_esai.h +++ b/sound/soc/fsl/fsl_esai.h @@ -130,8 +130,8 @@ #define ESAI_xFCR_RE_WIDTH 4 #define ESAI_xFCR_TE_MASK (((1 << ESAI_xFCR_TE_WIDTH) - 1) << ESAI_xFCR_xE_SHIFT) #define ESAI_xFCR_RE_MASK (((1 << ESAI_xFCR_RE_WIDTH) - 1) << ESAI_xFCR_xE_SHIFT) -#define ESAI_xFCR_TE(x) ((ESAI_xFCR_TE_MASK >> (ESAI_xFCR_TE_WIDTH - ((x + 1) >> 1))) & ESAI_xFCR_TE_MASK) -#define ESAI_xFCR_RE(x) ((ESAI_xFCR_RE_MASK >> (ESAI_xFCR_RE_WIDTH - ((x + 1) >> 1))) & ESAI_xFCR_RE_MASK) +#define ESAI_xFCR_TE(x) ((ESAI_xFCR_TE_MASK >> (ESAI_xFCR_TE_WIDTH - x)) & ESAI_xFCR_TE_MASK) +#define ESAI_xFCR_RE(x) ((ESAI_xFCR_RE_MASK >> (ESAI_xFCR_RE_WIDTH - x)) & ESAI_xFCR_RE_MASK) #define ESAI_xFCR_xFR_SHIFT 1 #define ESAI_xFCR_xFR_MASK (1 << ESAI_xFCR_xFR_SHIFT) #define ESAI_xFCR_xFR (1 << ESAI_xFCR_xFR_SHIFT) @@ -272,8 +272,8 @@ #define ESAI_xCR_RE_WIDTH 4 #define ESAI_xCR_TE_MASK (((1 << ESAI_xCR_TE_WIDTH) - 1) << ESAI_xCR_xE_SHIFT) #define ESAI_xCR_RE_MASK (((1 << ESAI_xCR_RE_WIDTH) - 1) << ESAI_xCR_xE_SHIFT) -#define ESAI_xCR_TE(x) ((ESAI_xCR_TE_MASK >> (ESAI_xCR_TE_WIDTH - ((x + 1) >> 1))) & ESAI_xCR_TE_MASK) -#define ESAI_xCR_RE(x) ((ESAI_xCR_RE_MASK >> (ESAI_xCR_RE_WIDTH - ((x + 1) >> 1))) & ESAI_xCR_RE_MASK) +#define ESAI_xCR_TE(x) ((ESAI_xCR_TE_MASK >> (ESAI_xCR_TE_WIDTH - x)) & ESAI_xCR_TE_MASK) +#define ESAI_xCR_RE(x) ((ESAI_xCR_RE_MASK >> (ESAI_xCR_RE_WIDTH - x)) & ESAI_xCR_RE_MASK) /* * Transmit Clock Control Register -- REG_ESAI_TCCR 0xD8 diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index faa0497..7eeb1dd 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -175,7 +175,7 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, bool tx = fsl_dir == FSL_FMT_TRANSMITTER; u32 val_cr2 = 0, val_cr4 = 0; - if (!sai->big_endian_data) + if (!sai->is_lsb_first) val_cr4 |= FSL_SAI_CR4_MF; /* DAI mode */ @@ -304,7 +304,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, val_cr5 |= FSL_SAI_CR5_WNW(word_width); val_cr5 |= FSL_SAI_CR5_W0W(word_width); - if (sai->big_endian_data) + if (sai->is_lsb_first) val_cr5 |= FSL_SAI_CR5_FBT(0); else val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1); @@ -330,13 +330,13 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, u32 xcsr, count = 100; /* - * The transmitter bit clock and frame sync are to be - * used by both the transmitter and receiver. + * Asynchronous mode: Clear SYNC for both Tx and Rx. + * Rx sync with Tx clocks: Clear SYNC for Tx, set it for Rx. + * Tx sync with Rx clocks: Clear SYNC for Rx, set it for Tx. */ - regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC, - ~FSL_SAI_CR2_SYNC); + regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC, 0); regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_SYNC, - FSL_SAI_CR2_SYNC); + sai->synchronous[RX] ? FSL_SAI_CR2_SYNC : 0); /* * It is recommended that the transmitter is the last enabled @@ -437,8 +437,13 @@ static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai) { struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev); - regmap_update_bits(sai->regmap, FSL_SAI_TCSR, 0xffffffff, 0x0); - regmap_update_bits(sai->regmap, FSL_SAI_RCSR, 0xffffffff, 0x0); + /* Software Reset for both Tx and Rx */ + regmap_write(sai->regmap, FSL_SAI_TCSR, FSL_SAI_CSR_SR); + regmap_write(sai->regmap, FSL_SAI_RCSR, FSL_SAI_CSR_SR); + /* Clear SR bit to finish the reset */ + regmap_write(sai->regmap, FSL_SAI_TCSR, 0); + regmap_write(sai->regmap, FSL_SAI_RCSR, 0); + regmap_update_bits(sai->regmap, FSL_SAI_TCR1, FSL_SAI_CR1_RFW_MASK, FSL_SAI_MAXBURST_TX * 2); regmap_update_bits(sai->regmap, FSL_SAI_RCR1, FSL_SAI_CR1_RFW_MASK, @@ -539,7 +544,7 @@ static bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg) } } -static struct regmap_config fsl_sai_regmap_config = { +static const struct regmap_config fsl_sai_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, @@ -568,11 +573,7 @@ static int fsl_sai_probe(struct platform_device *pdev) if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx6sx-sai")) sai->sai_on_imx = true; - sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs"); - if (sai->big_endian_regs) - fsl_sai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG; - - sai->big_endian_data = of_property_read_bool(np, "big-endian-data"); + sai->is_lsb_first = of_property_read_bool(np, "lsb-first"); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); @@ -621,6 +622,33 @@ static int fsl_sai_probe(struct platform_device *pdev) return ret; } + /* Sync Tx with Rx as default by following old DT binding */ + sai->synchronous[RX] = true; + sai->synchronous[TX] = false; + fsl_sai_dai.symmetric_rates = 1; + fsl_sai_dai.symmetric_channels = 1; + fsl_sai_dai.symmetric_samplebits = 1; + + if (of_find_property(np, "fsl,sai-synchronous-rx", NULL) && + of_find_property(np, "fsl,sai-asynchronous", NULL)) { + /* error out if both synchronous and asynchronous are present */ + dev_err(&pdev->dev, "invalid binding for synchronous mode\n"); + return -EINVAL; + } + + if (of_find_property(np, "fsl,sai-synchronous-rx", NULL)) { + /* Sync Rx with Tx */ + sai->synchronous[RX] = false; + sai->synchronous[TX] = true; + } else if (of_find_property(np, "fsl,sai-asynchronous", NULL)) { + /* Discard all settings for asynchronous mode */ + sai->synchronous[RX] = false; + sai->synchronous[TX] = false; + fsl_sai_dai.symmetric_rates = 0; + fsl_sai_dai.symmetric_channels = 0; + fsl_sai_dai.symmetric_samplebits = 0; + } + sai->dma_params_rx.addr = res->start + FSL_SAI_RDR; sai->dma_params_tx.addr = res->start + FSL_SAI_TDR; sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX; diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index 0e6c9f5..3466720 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -48,6 +48,7 @@ /* SAI Transmit/Recieve Control Register */ #define FSL_SAI_CSR_TERE BIT(31) #define FSL_SAI_CSR_FR BIT(25) +#define FSL_SAI_CSR_SR BIT(24) #define FSL_SAI_CSR_xF_SHIFT 16 #define FSL_SAI_CSR_xF_W_SHIFT 18 #define FSL_SAI_CSR_xF_MASK (0x1f << FSL_SAI_CSR_xF_SHIFT) @@ -131,13 +132,16 @@ struct fsl_sai { struct clk *bus_clk; struct clk *mclk_clk[FSL_SAI_MCLK_MAX]; - bool big_endian_regs; - bool big_endian_data; + bool is_lsb_first; bool is_dsp_mode; bool sai_on_imx; + bool synchronous[2]; struct snd_dmaengine_dai_dma_data dma_params_rx; struct snd_dmaengine_dai_dma_data dma_params_tx; }; +#define TX 1 +#define RX 0 + #endif /* __FSL_SAI_H */ diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 70acfe4..9b79162 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -15,7 +15,6 @@ #include <linux/bitrev.h> #include <linux/clk.h> -#include <linux/clk-private.h> #include <linux/module.h> #include <linux/of_address.h> #include <linux/of_device.h> @@ -1040,7 +1039,7 @@ static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg) } } -static struct regmap_config fsl_spdif_regmap_config = { +static const struct regmap_config fsl_spdif_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, @@ -1184,9 +1183,6 @@ static int fsl_spdif_probe(struct platform_device *pdev) memcpy(&spdif_priv->cpu_dai_drv, &fsl_spdif_dai, sizeof(fsl_spdif_dai)); spdif_priv->cpu_dai_drv.name = spdif_priv->name; - if (of_property_read_bool(np, "big-endian")) - fsl_spdif_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG; - /* Get the addresses and IRQ */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = devm_ioremap_resource(&pdev->dev, res); diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index de6ab06..e695517 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -169,6 +169,7 @@ struct fsl_ssi_private { u8 i2s_mode; bool use_dma; bool use_dual_fifo; + bool has_ipg_clk_name; unsigned int fifo_depth; struct fsl_ssi_rxtx_reg_val rxtx_reg_val; @@ -259,6 +260,11 @@ static bool fsl_ssi_is_i2s_master(struct fsl_ssi_private *ssi_private) SND_SOC_DAIFMT_CBS_CFS; } +static bool fsl_ssi_is_i2s_cbm_cfs(struct fsl_ssi_private *ssi_private) +{ + return (ssi_private->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) == + SND_SOC_DAIFMT_CBM_CFS; +} /** * fsl_ssi_isr: SSI interrupt handler * @@ -525,6 +531,11 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai); + int ret; + + ret = clk_prepare_enable(ssi_private->clk); + if (ret) + return ret; /* When using dual fifo mode, it is safer to ensure an even period * size. If appearing to an odd number while DMA always starts its @@ -539,6 +550,21 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, } /** + * fsl_ssi_shutdown: shutdown the SSI + * + */ +static void fsl_ssi_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct fsl_ssi_private *ssi_private = + snd_soc_dai_get_drvdata(rtd->cpu_dai); + + clk_disable_unprepare(ssi_private->clk); + +} + +/** * fsl_ssi_set_bclk - configure Digital Audio Interface bit clock * * Note: This function can be only called when using SSI as DAI master @@ -705,6 +731,23 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, } } + if (!fsl_ssi_is_ac97(ssi_private)) { + u8 i2smode; + /* + * Switch to normal net mode in order to have a frame sync + * signal every 32 bits instead of 16 bits + */ + if (fsl_ssi_is_i2s_cbm_cfs(ssi_private) && sample_size == 16) + i2smode = CCSR_SSI_SCR_I2S_MODE_NORMAL | + CCSR_SSI_SCR_NET; + else + i2smode = ssi_private->i2s_mode; + + regmap_update_bits(regs, CCSR_SSI_SCR, + CCSR_SSI_SCR_NET | CCSR_SSI_SCR_I2S_MODE_MASK, + channels == 1 ? 0 : i2smode); + } + /* * FIXME: The documentation says that SxCCR[WL] should not be * modified while the SSI is enabled. The only time this can @@ -724,11 +767,6 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, regmap_update_bits(regs, CCSR_SSI_SRCCR, CCSR_SSI_SxCCR_WL_MASK, wl); - if (!fsl_ssi_is_ac97(ssi_private)) - regmap_update_bits(regs, CCSR_SSI_SCR, - CCSR_SSI_SCR_NET | CCSR_SSI_SCR_I2S_MODE_MASK, - channels == 1 ? 0 : ssi_private->i2s_mode); - return 0; } @@ -781,6 +819,7 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev, switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFS: case SND_SOC_DAIFMT_CBS_CFS: ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_MASTER; regmap_update_bits(regs, CCSR_SSI_STCCR, @@ -854,6 +893,11 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev, case SND_SOC_DAIFMT_CBM_CFM: scr &= ~CCSR_SSI_SCR_SYS_CLK_EN; break; + case SND_SOC_DAIFMT_CBM_CFS: + strcr &= ~CCSR_SSI_STCR_TXDIR; + strcr |= CCSR_SSI_STCR_TFDIR; + scr &= ~CCSR_SSI_SCR_SYS_CLK_EN; + break; default: return -EINVAL; } @@ -1021,6 +1065,7 @@ static int fsl_ssi_dai_probe(struct snd_soc_dai *dai) static const struct snd_soc_dai_ops fsl_ssi_dai_ops = { .startup = fsl_ssi_startup, + .shutdown = fsl_ssi_shutdown, .hw_params = fsl_ssi_hw_params, .hw_free = fsl_ssi_hw_free, .set_fmt = fsl_ssi_set_dai_fmt, @@ -1146,17 +1191,22 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev, u32 dmas[4]; int ret; - ssi_private->clk = devm_clk_get(&pdev->dev, NULL); + if (ssi_private->has_ipg_clk_name) + ssi_private->clk = devm_clk_get(&pdev->dev, "ipg"); + else + ssi_private->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(ssi_private->clk)) { ret = PTR_ERR(ssi_private->clk); dev_err(&pdev->dev, "could not get clock: %d\n", ret); return ret; } - ret = clk_prepare_enable(ssi_private->clk); - if (ret) { - dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret); - return ret; + if (!ssi_private->has_ipg_clk_name) { + ret = clk_prepare_enable(ssi_private->clk); + if (ret) { + dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret); + return ret; + } } /* For those SLAVE implementations, we ingore non-baudclk cases @@ -1214,8 +1264,9 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev, return 0; error_pcm: - clk_disable_unprepare(ssi_private->clk); + if (!ssi_private->has_ipg_clk_name) + clk_disable_unprepare(ssi_private->clk); return ret; } @@ -1224,7 +1275,8 @@ static void fsl_ssi_imx_clean(struct platform_device *pdev, { if (!ssi_private->use_dma) imx_pcm_fiq_exit(pdev); - clk_disable_unprepare(ssi_private->clk); + if (!ssi_private->has_ipg_clk_name) + clk_disable_unprepare(ssi_private->clk); } static int fsl_ssi_probe(struct platform_device *pdev) @@ -1263,9 +1315,6 @@ static int fsl_ssi_probe(struct platform_device *pdev) if (sprop) { if (!strcmp(sprop, "ac97-slave")) ssi_private->dai_fmt = SND_SOC_DAIFMT_AC97; - else if (!strcmp(sprop, "i2s-slave")) - ssi_private->dai_fmt = SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_CBM_CFM; } ssi_private->use_dma = !of_property_read_bool(np, @@ -1299,8 +1348,16 @@ static int fsl_ssi_probe(struct platform_device *pdev) return -ENOMEM; } - ssi_private->regs = devm_regmap_init_mmio(&pdev->dev, iomem, + ret = of_property_match_string(np, "clock-names", "ipg"); + if (ret < 0) { + ssi_private->has_ipg_clk_name = false; + ssi_private->regs = devm_regmap_init_mmio(&pdev->dev, iomem, &fsl_ssi_regconfig); + } else { + ssi_private->has_ipg_clk_name = true; + ssi_private->regs = devm_regmap_init_mmio_clk(&pdev->dev, + "ipg", iomem, &fsl_ssi_regconfig); + } if (IS_ERR(ssi_private->regs)) { dev_err(&pdev->dev, "Failed to init register map\n"); return PTR_ERR(ssi_private->regs); diff --git a/sound/soc/fsl/imx-es8328.c b/sound/soc/fsl/imx-es8328.c new file mode 100644 index 0000000..f8cf10e --- /dev/null +++ b/sound/soc/fsl/imx-es8328.c @@ -0,0 +1,233 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2012 Linaro Ltd. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/gpio.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/i2c.h> +#include <linux/of_gpio.h> +#include <sound/soc.h> +#include <sound/jack.h> + +#include "imx-audmux.h" + +#define DAI_NAME_SIZE 32 +#define MUX_PORT_MAX 7 + +struct imx_es8328_data { + struct device *dev; + struct snd_soc_dai_link dai; + struct snd_soc_card card; + char codec_dai_name[DAI_NAME_SIZE]; + char platform_name[DAI_NAME_SIZE]; + int jack_gpio; +}; + +static struct snd_soc_jack_gpio headset_jack_gpios[] = { + { + .gpio = -1, + .name = "headset-gpio", + .report = SND_JACK_HEADSET, + .invert = 0, + .debounce_time = 200, + }, +}; + +static struct snd_soc_jack headset_jack; + +static int imx_es8328_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct imx_es8328_data *data = container_of(rtd->card, + struct imx_es8328_data, card); + int ret = 0; + + /* Headphone jack detection */ + if (gpio_is_valid(data->jack_gpio)) { + ret = snd_soc_jack_new(rtd->codec, "Headphone", + SND_JACK_HEADPHONE | SND_JACK_BTN_0, + &headset_jack); + if (ret) + return ret; + + headset_jack_gpios[0].gpio = data->jack_gpio; + ret = snd_soc_jack_add_gpios(&headset_jack, + ARRAY_SIZE(headset_jack_gpios), + headset_jack_gpios); + } + + return ret; +} + +static const struct snd_soc_dapm_widget imx_es8328_dapm_widgets[] = { + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_REGULATOR_SUPPLY("audio-amp", 1, 0), +}; + +static int imx_es8328_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *ssi_np = NULL, *codec_np = NULL; + struct platform_device *ssi_pdev; + struct imx_es8328_data *data; + u32 int_port, ext_port; + int ret; + struct device *dev = &pdev->dev; + + ret = of_property_read_u32(np, "mux-int-port", &int_port); + if (ret) { + dev_err(dev, "mux-int-port missing or invalid\n"); + goto fail; + } + if (int_port > MUX_PORT_MAX || int_port == 0) { + dev_err(dev, "mux-int-port: hardware only has %d mux ports\n", + MUX_PORT_MAX); + goto fail; + } + + ret = of_property_read_u32(np, "mux-ext-port", &ext_port); + if (ret) { + dev_err(dev, "mux-ext-port missing or invalid\n"); + goto fail; + } + if (ext_port > MUX_PORT_MAX || ext_port == 0) { + dev_err(dev, "mux-ext-port: hardware only has %d mux ports\n", + MUX_PORT_MAX); + ret = -EINVAL; + goto fail; + } + + /* + * The port numbering in the hardware manual starts at 1, while + * the audmux API expects it starts at 0. + */ + int_port--; + ext_port--; + ret = imx_audmux_v2_configure_port(int_port, + IMX_AUDMUX_V2_PTCR_SYN | + IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) | + IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) | + IMX_AUDMUX_V2_PTCR_TFSDIR | + IMX_AUDMUX_V2_PTCR_TCLKDIR, + IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port)); + if (ret) { + dev_err(dev, "audmux internal port setup failed\n"); + return ret; + } + ret = imx_audmux_v2_configure_port(ext_port, + IMX_AUDMUX_V2_PTCR_SYN, + IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)); + if (ret) { + dev_err(dev, "audmux external port setup failed\n"); + return ret; + } + + ssi_np = of_parse_phandle(pdev->dev.of_node, "ssi-controller", 0); + codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0); + if (!ssi_np || !codec_np) { + dev_err(dev, "phandle missing or invalid\n"); + ret = -EINVAL; + goto fail; + } + + ssi_pdev = of_find_device_by_node(ssi_np); + if (!ssi_pdev) { + dev_err(dev, "failed to find SSI platform device\n"); + ret = -EINVAL; + goto fail; + } + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto fail; + } + + data->dev = dev; + + data->jack_gpio = of_get_named_gpio(pdev->dev.of_node, "jack-gpio", 0); + + data->dai.name = "hifi"; + data->dai.stream_name = "hifi"; + data->dai.codec_dai_name = "es8328-hifi-analog"; + data->dai.codec_of_node = codec_np; + data->dai.cpu_of_node = ssi_np; + data->dai.platform_of_node = ssi_np; + data->dai.init = &imx_es8328_dai_init; + data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM; + + data->card.dev = dev; + data->card.dapm_widgets = imx_es8328_dapm_widgets; + data->card.num_dapm_widgets = ARRAY_SIZE(imx_es8328_dapm_widgets); + ret = snd_soc_of_parse_card_name(&data->card, "model"); + if (ret) { + dev_err(dev, "Unable to parse card name\n"); + goto fail; + } + ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing"); + if (ret) { + dev_err(dev, "Unable to parse routing: %d\n", ret); + goto fail; + } + data->card.num_links = 1; + data->card.owner = THIS_MODULE; + data->card.dai_link = &data->dai; + + ret = snd_soc_register_card(&data->card); + if (ret) { + dev_err(dev, "Unable to register: %d\n", ret); + goto fail; + } + + platform_set_drvdata(pdev, data); +fail: + of_node_put(ssi_np); + of_node_put(codec_np); + + return ret; +} + +static int imx_es8328_remove(struct platform_device *pdev) +{ + struct imx_es8328_data *data = platform_get_drvdata(pdev); + + snd_soc_jack_free_gpios(&headset_jack, ARRAY_SIZE(headset_jack_gpios), + headset_jack_gpios); + + snd_soc_unregister_card(&data->card); + + return 0; +} + +static const struct of_device_id imx_es8328_dt_ids[] = { + { .compatible = "fsl,imx-audio-es8328", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx_es8328_dt_ids); + +static struct platform_driver imx_es8328_driver = { + .driver = { + .name = "imx-es8328", + .of_match_table = imx_es8328_dt_ids, + }, + .probe = imx_es8328_probe, + .remove = imx_es8328_remove, +}; +module_platform_driver(imx_es8328_driver); + +MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>"); +MODULE_DESCRIPTION("Kosagi i.MX6 ES8328 ASoC machine driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:imx-audio-es8328"); diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index cef7776..d1b7293 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -10,10 +10,13 @@ */ #include <linux/clk.h> #include <linux/device.h> +#include <linux/gpio.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_gpio.h> #include <linux/platform_device.h> #include <linux/string.h> +#include <sound/jack.h> #include <sound/simple_card.h> #include <sound/soc-dai.h> #include <sound/soc.h> @@ -25,9 +28,15 @@ struct simple_card_data { struct asoc_simple_dai codec_dai; } *dai_props; unsigned int mclk_fs; + int gpio_hp_det; + int gpio_mic_det; struct snd_soc_dai_link dai_link[]; /* dynamically allocated */ }; +#define simple_priv_to_dev(priv) ((priv)->snd_card.dev) +#define simple_priv_to_link(priv, i) ((priv)->snd_card.dai_link + i) +#define simple_priv_to_props(priv, i) ((priv)->dai_props + i) + static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -50,6 +59,32 @@ static struct snd_soc_ops asoc_simple_card_ops = { .hw_params = asoc_simple_card_hw_params, }; +static struct snd_soc_jack simple_card_hp_jack; +static struct snd_soc_jack_pin simple_card_hp_jack_pins[] = { + { + .pin = "Headphones", + .mask = SND_JACK_HEADPHONE, + }, +}; +static struct snd_soc_jack_gpio simple_card_hp_jack_gpio = { + .name = "Headphone detection", + .report = SND_JACK_HEADPHONE, + .debounce_time = 150, +}; + +static struct snd_soc_jack simple_card_mic_jack; +static struct snd_soc_jack_pin simple_card_mic_jack_pins[] = { + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, +}; +static struct snd_soc_jack_gpio simple_card_mic_jack_gpio = { + .name = "Mic detection", + .report = SND_JACK_MICROPHONE, + .debounce_time = 150, +}; + static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai, struct asoc_simple_dai *set) { @@ -105,42 +140,70 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) if (ret < 0) return ret; + if (gpio_is_valid(priv->gpio_hp_det)) { + snd_soc_jack_new(codec->codec, "Headphones", SND_JACK_HEADPHONE, + &simple_card_hp_jack); + snd_soc_jack_add_pins(&simple_card_hp_jack, + ARRAY_SIZE(simple_card_hp_jack_pins), + simple_card_hp_jack_pins); + + simple_card_hp_jack_gpio.gpio = priv->gpio_hp_det; + snd_soc_jack_add_gpios(&simple_card_hp_jack, 1, + &simple_card_hp_jack_gpio); + } + + if (gpio_is_valid(priv->gpio_mic_det)) { + snd_soc_jack_new(codec->codec, "Mic Jack", SND_JACK_MICROPHONE, + &simple_card_mic_jack); + snd_soc_jack_add_pins(&simple_card_mic_jack, + ARRAY_SIZE(simple_card_mic_jack_pins), + simple_card_mic_jack_pins); + simple_card_mic_jack_gpio.gpio = priv->gpio_mic_det; + snd_soc_jack_add_gpios(&simple_card_mic_jack, 1, + &simple_card_mic_jack_gpio); + } return 0; } static int asoc_simple_card_sub_parse_of(struct device_node *np, struct asoc_simple_dai *dai, - const struct device_node **p_node, - const char **name) + struct device_node **p_node, + const char **name, + int *args_count) { - struct device_node *node; + struct of_phandle_args args; struct clk *clk; u32 val; int ret; /* - * get node via "sound-dai = <&phandle port>" + * Get node via "sound-dai = <&phandle port>" * it will be used as xxx_of_node on soc_bind_dai_link() */ - node = of_parse_phandle(np, "sound-dai", 0); - if (!node) - return -ENODEV; - *p_node = node; + ret = of_parse_phandle_with_args(np, "sound-dai", + "#sound-dai-cells", 0, &args); + if (ret) + return ret; + + *p_node = args.np; + + if (args_count) + *args_count = args.args_count; - /* get dai->name */ + /* Get dai->name */ ret = snd_soc_of_get_dai_name(np, name); if (ret < 0) return ret; - /* parse TDM slot */ + /* Parse TDM slot */ ret = snd_soc_of_parse_tdm_slot(np, &dai->slots, &dai->slot_width); if (ret) return ret; /* - * dai->sysclk come from - * "clocks = <&xxx>" (if system has common clock) + * Parse dai->sysclk come from "clocks = <&xxx>" + * (if system has common clock) * or "system-clock-frequency = <xxx>" * or device's module clock. */ @@ -155,7 +218,7 @@ asoc_simple_card_sub_parse_of(struct device_node *np, } else if (!of_property_read_u32(np, "system-clock-frequency", &val)) { dai->sysclk = val; } else { - clk = of_clk_get(node, 0); + clk = of_clk_get(args.np, 0); if (!IS_ERR(clk)) dai->sysclk = clk_get_rate(clk); } @@ -163,12 +226,14 @@ asoc_simple_card_sub_parse_of(struct device_node *np, return 0; } -static int simple_card_dai_link_of(struct device_node *node, - struct device *dev, - struct snd_soc_dai_link *dai_link, - struct simple_dai_props *dai_props, - bool is_top_level_node) +static int asoc_simple_card_dai_link_of(struct device_node *node, + struct simple_card_data *priv, + int idx, + bool is_top_level_node) { + struct device *dev = simple_priv_to_dev(priv); + struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx); struct device_node *np = NULL; struct device_node *bitclkmaster = NULL; struct device_node *framemaster = NULL; @@ -176,8 +241,9 @@ static int simple_card_dai_link_of(struct device_node *node, char *name; char prop[128]; char *prefix = ""; - int ret; + int ret, cpu_args; + /* For single DAI link & old style of DT node */ if (is_top_level_node) prefix = "simple-audio-card,"; @@ -195,7 +261,8 @@ static int simple_card_dai_link_of(struct device_node *node, ret = asoc_simple_card_sub_parse_of(np, &dai_props->cpu_dai, &dai_link->cpu_of_node, - &dai_link->cpu_dai_name); + &dai_link->cpu_dai_name, + &cpu_args); if (ret < 0) goto dai_link_of_err; @@ -226,14 +293,16 @@ static int simple_card_dai_link_of(struct device_node *node, ret = asoc_simple_card_sub_parse_of(np, &dai_props->codec_dai, &dai_link->codec_of_node, - &dai_link->codec_dai_name); + &dai_link->codec_dai_name, NULL); if (ret < 0) goto dai_link_of_err; if (strlen(prefix) && !bitclkmaster && !framemaster) { - /* No dai-link level and master setting was not found from - sound node level, revert back to legacy DT parsing and - take the settings from codec node. */ + /* + * No DAI link level and master setting was found + * from sound node level, revert back to legacy DT + * parsing and take the settings from codec node. + */ dev_dbg(dev, "%s: Revert to legacy daifmt parsing\n", __func__); dai_props->cpu_dai.fmt = dai_props->codec_dai.fmt = @@ -262,10 +331,10 @@ static int simple_card_dai_link_of(struct device_node *node, goto dai_link_of_err; } - /* simple-card assumes platform == cpu */ + /* Simple Card assumes platform == cpu */ dai_link->platform_of_node = dai_link->cpu_of_node; - /* Link name is created from CPU/CODEC dai name */ + /* DAI link name is created from CPU/CODEC dai name */ name = devm_kzalloc(dev, strlen(dai_link->cpu_dai_name) + strlen(dai_link->codec_dai_name) + 2, @@ -274,6 +343,7 @@ static int simple_card_dai_link_of(struct device_node *node, dai_link->codec_dai_name); dai_link->name = dai_link->stream_name = name; dai_link->ops = &asoc_simple_card_ops; + dai_link->init = asoc_simple_card_dai_init; dev_dbg(dev, "\tname : %s\n", dai_link->stream_name); dev_dbg(dev, "\tcpu : %s / %04x / %d\n", @@ -285,6 +355,18 @@ static int simple_card_dai_link_of(struct device_node *node, dai_props->codec_dai.fmt, dai_props->codec_dai.sysclk); + /* + * In soc_bind_dai_link() will check cpu name after + * of_node matching if dai_link has cpu_dai_name. + * but, it will never match if name was created by + * fmt_single_name() remove cpu_dai_name if cpu_args + * was 0. See: + * fmt_single_name() + * fmt_multiple_name() + */ + if (!cpu_args) + dai_link->cpu_dai_name = NULL; + dai_link_of_err: if (np) of_node_put(np); @@ -296,19 +378,19 @@ dai_link_of_err: } static int asoc_simple_card_parse_of(struct device_node *node, - struct simple_card_data *priv, - struct device *dev, - int multi) + struct simple_card_data *priv) { - struct snd_soc_dai_link *dai_link = priv->snd_card.dai_link; - struct simple_dai_props *dai_props = priv->dai_props; + struct device *dev = simple_priv_to_dev(priv); u32 val; int ret; - /* parsing the card name from DT */ + if (!node) + return -EINVAL; + + /* Parse the card name from DT */ snd_soc_of_parse_card_name(&priv->snd_card, "simple-audio-card,name"); - /* off-codec widgets */ + /* The off-codec widgets */ if (of_property_read_bool(node, "simple-audio-card,widgets")) { ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card, "simple-audio-card,widgets"); @@ -332,32 +414,45 @@ static int asoc_simple_card_parse_of(struct device_node *node, dev_dbg(dev, "New simple-card: %s\n", priv->snd_card.name ? priv->snd_card.name : ""); - if (multi) { + /* Single/Muti DAI link(s) & New style of DT node */ + if (of_get_child_by_name(node, "simple-audio-card,dai-link")) { struct device_node *np = NULL; - int i; - for (i = 0; (np = of_get_next_child(node, np)); i++) { + int i = 0; + + for_each_child_of_node(node, np) { dev_dbg(dev, "\tlink %d:\n", i); - ret = simple_card_dai_link_of(np, dev, dai_link + i, - dai_props + i, false); + ret = asoc_simple_card_dai_link_of(np, priv, + i, false); if (ret < 0) { of_node_put(np); return ret; } + i++; } } else { - ret = simple_card_dai_link_of(node, dev, dai_link, dai_props, - true); + /* For single DAI link & old style of DT node */ + ret = asoc_simple_card_dai_link_of(node, priv, 0, true); if (ret < 0) return ret; } + priv->gpio_hp_det = of_get_named_gpio(node, + "simple-audio-card,hp-det-gpio", 0); + if (priv->gpio_hp_det == -EPROBE_DEFER) + return -EPROBE_DEFER; + + priv->gpio_mic_det = of_get_named_gpio(node, + "simple-audio-card,mic-det-gpio", 0); + if (priv->gpio_mic_det == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (!priv->snd_card.name) priv->snd_card.name = priv->snd_card.dai_link->name; return 0; } -/* update the reference count of the devices nodes at end of probe */ +/* Decrease the reference count of the device nodes */ static int asoc_simple_card_unref(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); @@ -384,34 +479,32 @@ static int asoc_simple_card_probe(struct platform_device *pdev) struct snd_soc_dai_link *dai_link; struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; - int num_links, multi, ret; + int num_links, ret; - /* get the number of DAI links */ - if (np && of_get_child_by_name(np, "simple-audio-card,dai-link")) { + /* Get the number of DAI links */ + if (np && of_get_child_by_name(np, "simple-audio-card,dai-link")) num_links = of_get_child_count(np); - multi = 1; - } else { + else num_links = 1; - multi = 0; - } - /* allocate the private data and the DAI link array */ + /* Allocate the private data and the DAI link array */ priv = devm_kzalloc(dev, sizeof(*priv) + sizeof(*dai_link) * num_links, GFP_KERNEL); if (!priv) return -ENOMEM; - /* - * init snd_soc_card - */ + /* Init snd_soc_card */ priv->snd_card.owner = THIS_MODULE; priv->snd_card.dev = dev; dai_link = priv->dai_link; priv->snd_card.dai_link = dai_link; priv->snd_card.num_links = num_links; - /* get room for the other properties */ + priv->gpio_hp_det = -ENOENT; + priv->gpio_mic_det = -ENOENT; + + /* Get room for the other properties */ priv->dai_props = devm_kzalloc(dev, sizeof(*priv->dai_props) * num_links, GFP_KERNEL); @@ -420,25 +513,13 @@ static int asoc_simple_card_probe(struct platform_device *pdev) if (np && of_device_is_available(np)) { - ret = asoc_simple_card_parse_of(np, priv, dev, multi); + ret = asoc_simple_card_parse_of(np, priv); if (ret < 0) { if (ret != -EPROBE_DEFER) dev_err(dev, "parse error %d\n", ret); goto err; } - /* - * soc_bind_dai_link() will check cpu name - * after of_node matching if dai_link has cpu_dai_name. - * but, it will never match if name was created by fmt_single_name() - * remove cpu_dai_name to escape name matching. - * see - * fmt_single_name() - * fmt_multiple_name() - */ - if (num_links == 1) - dai_link->cpu_dai_name = NULL; - } else { struct asoc_simple_card_info *cinfo; @@ -464,6 +545,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) dai_link->codec_name = cinfo->codec; dai_link->cpu_dai_name = cinfo->cpu_dai.name; dai_link->codec_dai_name = cinfo->codec_dai.name; + dai_link->init = asoc_simple_card_dai_init; memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai, sizeof(priv->dai_props->cpu_dai)); memcpy(&priv->dai_props->codec_dai, &cinfo->codec_dai, @@ -473,11 +555,6 @@ static int asoc_simple_card_probe(struct platform_device *pdev) priv->dai_props->codec_dai.fmt |= cinfo->daifmt; } - /* - * init snd_soc_dai_link - */ - dai_link->init = asoc_simple_card_dai_init; - snd_soc_card_set_drvdata(&priv->snd_card, priv); ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card); @@ -491,6 +568,16 @@ err: static int asoc_simple_card_remove(struct platform_device *pdev) { + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct simple_card_data *priv = snd_soc_card_get_drvdata(card); + + if (gpio_is_valid(priv->gpio_hp_det)) + snd_soc_jack_free_gpios(&simple_card_hp_jack, 1, + &simple_card_hp_jack_gpio); + if (gpio_is_valid(priv->gpio_mic_det)) + snd_soc_jack_free_gpios(&simple_card_mic_jack, 1, + &simple_card_mic_jack_gpio); + return asoc_simple_card_unref(pdev); } diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index 7acbfc4..f841786 100644 --- a/sound/soc/intel/Makefile +++ b/sound/soc/intel/Makefile @@ -2,7 +2,8 @@ snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o snd-soc-sst-acpi-objs := sst-acpi.o -snd-soc-sst-mfld-platform-objs := sst-mfld-platform-pcm.o sst-mfld-platform-compress.o +snd-soc-sst-mfld-platform-objs := sst-mfld-platform-pcm.o \ + sst-mfld-platform-compress.o sst-atom-controls.o snd-soc-mfld-machine-objs := mfld_machine.o obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o diff --git a/sound/soc/intel/byt-max98090.c b/sound/soc/intel/byt-max98090.c index b8b8af5..d52681e 100644 --- a/sound/soc/intel/byt-max98090.c +++ b/sound/soc/intel/byt-max98090.c @@ -139,6 +139,7 @@ static struct snd_soc_card byt_max98090_card = { .num_dapm_routes = ARRAY_SIZE(byt_max98090_audio_map), .controls = byt_max98090_controls, .num_controls = ARRAY_SIZE(byt_max98090_controls), + .fully_routed = true, }; static int byt_max98090_probe(struct platform_device *pdev) diff --git a/sound/soc/intel/byt-rt5640.c b/sound/soc/intel/byt-rt5640.c index 234a58d..e03abdf 100644 --- a/sound/soc/intel/byt-rt5640.c +++ b/sound/soc/intel/byt-rt5640.c @@ -17,6 +17,7 @@ #include <linux/platform_device.h> #include <linux/acpi.h> #include <linux/device.h> +#include <linux/dmi.h> #include <linux/slab.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -36,8 +37,6 @@ static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = { static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = { {"Headset Mic", NULL, "MICBIAS1"}, {"IN2P", NULL, "Headset Mic"}, - {"IN2N", NULL, "Headset Mic"}, - {"DMIC1", NULL, "Internal Mic"}, {"Headphone", NULL, "HPOL"}, {"Headphone", NULL, "HPOR"}, {"Speaker", NULL, "SPOLP"}, @@ -46,6 +45,31 @@ static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = { {"Speaker", NULL, "SPORN"}, }; +static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic1_map[] = { + {"DMIC1", NULL, "Internal Mic"}, +}; + +static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic2_map[] = { + {"DMIC2", NULL, "Internal Mic"}, +}; + +static const struct snd_soc_dapm_route byt_rt5640_intmic_in1_map[] = { + {"Internal Mic", NULL, "MICBIAS1"}, + {"IN1P", NULL, "Internal Mic"}, +}; + +enum { + BYT_RT5640_DMIC1_MAP, + BYT_RT5640_DMIC2_MAP, + BYT_RT5640_IN1_MAP, +}; + +#define BYT_RT5640_MAP(quirk) ((quirk) & 0xff) +#define BYT_RT5640_DMIC_EN BIT(16) + +static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP | + BYT_RT5640_DMIC_EN; + static const struct snd_kcontrol_new byt_rt5640_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone"), SOC_DAPM_PIN_SWITCH("Headset Mic"), @@ -77,12 +101,41 @@ static int byt_rt5640_hw_params(struct snd_pcm_substream *substream, return 0; } +static int byt_rt5640_quirk_cb(const struct dmi_system_id *id) +{ + byt_rt5640_quirk = (unsigned long)id->driver_data; + return 1; +} + +static const struct dmi_system_id byt_rt5640_quirk_table[] = { + { + .callback = byt_rt5640_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"), + }, + .driver_data = (unsigned long *)BYT_RT5640_IN1_MAP, + }, + { + .callback = byt_rt5640_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "DellInc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"), + }, + .driver_data = (unsigned long *)(BYT_RT5640_DMIC2_MAP | + BYT_RT5640_DMIC_EN), + }, + {} +}; + static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) { int ret; struct snd_soc_codec *codec = runtime->codec; struct snd_soc_dapm_context *dapm = &codec->dapm; struct snd_soc_card *card = runtime->card; + const struct snd_soc_dapm_route *custom_map; + int num_routes; card->dapm.idle_bias_off = true; @@ -93,6 +146,31 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) return ret; } + dmi_check_system(byt_rt5640_quirk_table); + switch (BYT_RT5640_MAP(byt_rt5640_quirk)) { + case BYT_RT5640_IN1_MAP: + custom_map = byt_rt5640_intmic_in1_map; + num_routes = ARRAY_SIZE(byt_rt5640_intmic_in1_map); + break; + case BYT_RT5640_DMIC2_MAP: + custom_map = byt_rt5640_intmic_dmic2_map; + num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic2_map); + break; + default: + custom_map = byt_rt5640_intmic_dmic1_map; + num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic1_map); + } + + ret = snd_soc_dapm_add_routes(dapm, custom_map, num_routes); + if (ret) + return ret; + + if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) { + ret = rt5640_dmic_enable(codec, 0, 0); + if (ret) + return ret; + } + snd_soc_dapm_ignore_suspend(dapm, "HPOL"); snd_soc_dapm_ignore_suspend(dapm, "HPOR"); @@ -131,6 +209,7 @@ static struct snd_soc_card byt_rt5640_card = { .num_dapm_widgets = ARRAY_SIZE(byt_rt5640_widgets), .dapm_routes = byt_rt5640_audio_map, .num_dapm_routes = ARRAY_SIZE(byt_rt5640_audio_map), + .fully_routed = true, }; static int byt_rt5640_probe(struct platform_device *pdev) diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c new file mode 100644 index 0000000..7104a34 --- /dev/null +++ b/sound/soc/intel/sst-atom-controls.c @@ -0,0 +1,218 @@ +/* + * sst-atom-controls.c - Intel MID Platform driver DPCM ALSA controls for Mrfld + * + * Copyright (C) 2013-14 Intel Corp + * Author: Omair Mohammed Abdullah <omair.m.abdullah@intel.com> + * Vinod Koul <vinod.koul@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/slab.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include "sst-mfld-platform.h" +#include "sst-atom-controls.h" + +static int sst_fill_byte_control(struct sst_data *drv, + u8 ipc_msg, u8 block, + u8 task_id, u8 pipe_id, + u16 len, void *cmd_data) +{ + struct snd_sst_bytes_v2 *byte_data = drv->byte_stream; + + byte_data->type = SST_CMD_BYTES_SET; + byte_data->ipc_msg = ipc_msg; + byte_data->block = block; + byte_data->task_id = task_id; + byte_data->pipe_id = pipe_id; + + if (len > SST_MAX_BIN_BYTES - sizeof(*byte_data)) { + dev_err(&drv->pdev->dev, "command length too big (%u)", len); + return -EINVAL; + } + byte_data->len = len; + memcpy(byte_data->bytes, cmd_data, len); + print_hex_dump_bytes("writing to lpe: ", DUMP_PREFIX_OFFSET, + byte_data, len + sizeof(*byte_data)); + return 0; +} + +static int sst_fill_and_send_cmd_unlocked(struct sst_data *drv, + u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id, + void *cmd_data, u16 len) +{ + int ret = 0; + + ret = sst_fill_byte_control(drv, ipc_msg, + block, task_id, pipe_id, len, cmd_data); + if (ret < 0) + return ret; + return sst->ops->send_byte_stream(sst->dev, drv->byte_stream); +} + +/** + * sst_fill_and_send_cmd - generate the IPC message and send it to the FW + * @ipc_msg: type of IPC (CMD, SET_PARAMS, GET_PARAMS) + * @cmd_data: the IPC payload + */ +static int sst_fill_and_send_cmd(struct sst_data *drv, + u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id, + void *cmd_data, u16 len) +{ + int ret; + + mutex_lock(&drv->lock); + ret = sst_fill_and_send_cmd_unlocked(drv, ipc_msg, block, + task_id, pipe_id, cmd_data, len); + mutex_unlock(&drv->lock); + + return ret; +} + +static int sst_send_algo_cmd(struct sst_data *drv, + struct sst_algo_control *bc) +{ + int len, ret = 0; + struct sst_cmd_set_params *cmd; + + /*bc->max includes sizeof algos + length field*/ + len = sizeof(cmd->dst) + sizeof(cmd->command_id) + bc->max; + + cmd = kzalloc(len, GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + SST_FILL_DESTINATION(2, cmd->dst, bc->pipe_id, bc->module_id); + cmd->command_id = bc->cmd_id; + memcpy(cmd->params, bc->params, bc->max); + + ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS, + SST_FLAG_BLOCKED, bc->task_id, 0, cmd, len); + kfree(cmd); + return ret; +} + +static int sst_algo_bytes_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct sst_algo_control *bc = (void *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = bc->max; + + return 0; +} + +static int sst_algo_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sst_algo_control *bc = (void *)kcontrol->private_value; + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + + switch (bc->type) { + case SST_ALGO_PARAMS: + memcpy(ucontrol->value.bytes.data, bc->params, bc->max); + break; + default: + dev_err(component->dev, "Invalid Input- algo type:%d\n", + bc->type); + return -EINVAL; + + } + return 0; +} + +static int sst_algo_control_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt); + struct sst_algo_control *bc = (void *)kcontrol->private_value; + + dev_dbg(cmpnt->dev, "control_name=%s\n", kcontrol->id.name); + mutex_lock(&drv->lock); + switch (bc->type) { + case SST_ALGO_PARAMS: + memcpy(bc->params, ucontrol->value.bytes.data, bc->max); + break; + default: + mutex_unlock(&drv->lock); + dev_err(cmpnt->dev, "Invalid Input- algo type:%d\n", + bc->type); + return -EINVAL; + } + /*if pipe is enabled, need to send the algo params from here*/ + if (bc->w && bc->w->power) + ret = sst_send_algo_cmd(drv, bc); + mutex_unlock(&drv->lock); + + return ret; +} + +static const struct snd_kcontrol_new sst_algo_controls[] = { + SST_ALGO_KCONTROL_BYTES("media_loop1_out", "fir", 272, SST_MODULE_ID_FIR_24, + SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR), + SST_ALGO_KCONTROL_BYTES("media_loop1_out", "iir", 300, SST_MODULE_ID_IIR_24, + SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_IIR), + SST_ALGO_KCONTROL_BYTES("media_loop1_out", "mdrp", 286, SST_MODULE_ID_MDRP, + SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_SET_MDRP), + SST_ALGO_KCONTROL_BYTES("media_loop2_out", "fir", 272, SST_MODULE_ID_FIR_24, + SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR), + SST_ALGO_KCONTROL_BYTES("media_loop2_out", "iir", 300, SST_MODULE_ID_IIR_24, + SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_VB_SET_IIR), + SST_ALGO_KCONTROL_BYTES("media_loop2_out", "mdrp", 286, SST_MODULE_ID_MDRP, + SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_SET_MDRP), + SST_ALGO_KCONTROL_BYTES("sprot_loop_out", "lpro", 192, SST_MODULE_ID_SPROT, + SST_PATH_INDEX_SPROT_LOOP_OUT, 0, SST_TASK_SBA, SBA_VB_LPRO), + SST_ALGO_KCONTROL_BYTES("codec_in0", "dcr", 52, SST_MODULE_ID_FILT_DCR, + SST_PATH_INDEX_CODEC_IN0, 0, SST_TASK_SBA, SBA_VB_SET_IIR), + SST_ALGO_KCONTROL_BYTES("codec_in1", "dcr", 52, SST_MODULE_ID_FILT_DCR, + SST_PATH_INDEX_CODEC_IN1, 0, SST_TASK_SBA, SBA_VB_SET_IIR), + +}; + +static int sst_algo_control_init(struct device *dev) +{ + int i = 0; + struct sst_algo_control *bc; + /*allocate space to cache the algo parameters in the driver*/ + for (i = 0; i < ARRAY_SIZE(sst_algo_controls); i++) { + bc = (struct sst_algo_control *)sst_algo_controls[i].private_value; + bc->params = devm_kzalloc(dev, bc->max, GFP_KERNEL); + if (bc->params == NULL) + return -ENOMEM; + } + return 0; +} + +int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform) +{ + int ret = 0; + struct sst_data *drv = snd_soc_platform_get_drvdata(platform); + + drv->byte_stream = devm_kzalloc(platform->dev, + SST_MAX_BIN_BYTES, GFP_KERNEL); + if (!drv->byte_stream) + return -ENOMEM; + + /*Initialize algo control params*/ + ret = sst_algo_control_init(platform->dev); + if (ret) + return ret; + ret = snd_soc_add_platform_controls(platform, sst_algo_controls, + ARRAY_SIZE(sst_algo_controls)); + return ret; +} diff --git a/sound/soc/intel/sst-atom-controls.h b/sound/soc/intel/sst-atom-controls.h index 14063ab..a73e894 100644 --- a/sound/soc/intel/sst-atom-controls.h +++ b/sound/soc/intel/sst-atom-controls.h @@ -1,4 +1,6 @@ /* + * sst-atom-controls.h - Intel MID Platform driver header file + * * Copyright (C) 2013-14 Intel Corp * Author: Ramesh Babu <ramesh.babu.koul@intel.com> * Omair M Abdullah <omair.m.abdullah@intel.com> @@ -18,13 +20,423 @@ * */ -#ifndef __SST_CONTROLS_V2_H__ -#define __SST_CONTROLS_V2_H__ +#ifndef __SST_ATOM_CONTROLS_H__ +#define __SST_ATOM_CONTROLS_H__ enum { MERR_DPCM_AUDIO = 0, MERR_DPCM_COMPR, }; +/* define a bit for each mixer input */ +#define SST_MIX_IP(x) (x) + +#define SST_IP_CODEC0 SST_MIX_IP(2) +#define SST_IP_CODEC1 SST_MIX_IP(3) +#define SST_IP_LOOP0 SST_MIX_IP(4) +#define SST_IP_LOOP1 SST_MIX_IP(5) +#define SST_IP_LOOP2 SST_MIX_IP(6) +#define SST_IP_PROBE SST_MIX_IP(7) +#define SST_IP_VOIP SST_MIX_IP(12) +#define SST_IP_PCM0 SST_MIX_IP(13) +#define SST_IP_PCM1 SST_MIX_IP(14) +#define SST_IP_MEDIA0 SST_MIX_IP(17) +#define SST_IP_MEDIA1 SST_MIX_IP(18) +#define SST_IP_MEDIA2 SST_MIX_IP(19) +#define SST_IP_MEDIA3 SST_MIX_IP(20) + +#define SST_IP_LAST SST_IP_MEDIA3 + +#define SST_SWM_INPUT_COUNT (SST_IP_LAST + 1) +#define SST_CMD_SWM_MAX_INPUTS 6 + +#define SST_PATH_ID_SHIFT 8 +#define SST_DEFAULT_LOCATION_ID 0xFFFF +#define SST_DEFAULT_CELL_NBR 0xFF +#define SST_DEFAULT_MODULE_ID 0xFFFF + +/* + * Audio DSP Path Ids. Specified by the audio DSP FW + */ +enum sst_path_index { + SST_PATH_INDEX_CODEC_OUT0 = (0x02 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_CODEC_OUT1 = (0x03 << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_SPROT_LOOP_OUT = (0x04 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA_LOOP1_OUT = (0x05 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA_LOOP2_OUT = (0x06 << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_VOIP_OUT = (0x0C << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PCM0_OUT = (0x0D << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PCM1_OUT = (0x0E << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PCM2_OUT = (0x0F << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_MEDIA0_OUT = (0x12 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA1_OUT = (0x13 << SST_PATH_ID_SHIFT), + + + /* Start of input paths */ + SST_PATH_INDEX_CODEC_IN0 = (0x82 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_CODEC_IN1 = (0x83 << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_SPROT_LOOP_IN = (0x84 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA_LOOP1_IN = (0x85 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA_LOOP2_IN = (0x86 << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_VOIP_IN = (0x8C << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_PCM0_IN = (0x8D << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PCM1_IN = (0x8E << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_MEDIA0_IN = (0x8F << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA1_IN = (0x90 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA2_IN = (0x91 << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_MEDIA3_IN = (0x9C << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_RESERVED = (0xFF << SST_PATH_ID_SHIFT), +}; + +/* + * path IDs + */ +enum sst_swm_inputs { + SST_SWM_IN_CODEC0 = (SST_PATH_INDEX_CODEC_IN0 | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_CODEC1 = (SST_PATH_INDEX_CODEC_IN1 | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_SPROT_LOOP = (SST_PATH_INDEX_SPROT_LOOP_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_MEDIA_LOOP1 = (SST_PATH_INDEX_MEDIA_LOOP1_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_MEDIA_LOOP2 = (SST_PATH_INDEX_MEDIA_LOOP2_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_VOIP = (SST_PATH_INDEX_VOIP_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_PCM0 = (SST_PATH_INDEX_PCM0_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_PCM1 = (SST_PATH_INDEX_PCM1_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_MEDIA0 = (SST_PATH_INDEX_MEDIA0_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ + SST_SWM_IN_MEDIA1 = (SST_PATH_INDEX_MEDIA1_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ + SST_SWM_IN_MEDIA2 = (SST_PATH_INDEX_MEDIA2_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ + SST_SWM_IN_MEDIA3 = (SST_PATH_INDEX_MEDIA3_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ + SST_SWM_IN_END = (SST_PATH_INDEX_RESERVED | SST_DEFAULT_CELL_NBR) +}; + +/* + * path IDs + */ +enum sst_swm_outputs { + SST_SWM_OUT_CODEC0 = (SST_PATH_INDEX_CODEC_OUT0 | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_CODEC1 = (SST_PATH_INDEX_CODEC_OUT1 | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_SPROT_LOOP = (SST_PATH_INDEX_SPROT_LOOP_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_MEDIA_LOOP1 = (SST_PATH_INDEX_MEDIA_LOOP1_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_MEDIA_LOOP2 = (SST_PATH_INDEX_MEDIA_LOOP2_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_VOIP = (SST_PATH_INDEX_VOIP_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_PCM0 = (SST_PATH_INDEX_PCM0_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_PCM1 = (SST_PATH_INDEX_PCM1_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_PCM2 = (SST_PATH_INDEX_PCM2_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_MEDIA0 = (SST_PATH_INDEX_MEDIA0_OUT | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ + SST_SWM_OUT_MEDIA1 = (SST_PATH_INDEX_MEDIA1_OUT | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ + SST_SWM_OUT_END = (SST_PATH_INDEX_RESERVED | SST_DEFAULT_CELL_NBR), +}; + +enum sst_ipc_msg { + SST_IPC_IA_CMD = 1, + SST_IPC_IA_SET_PARAMS, + SST_IPC_IA_GET_PARAMS, +}; + +enum sst_cmd_type { + SST_CMD_BYTES_SET = 1, + SST_CMD_BYTES_GET = 2, +}; + +enum sst_task { + SST_TASK_SBA = 1, + SST_TASK_MMX, +}; + +enum sst_type { + SST_TYPE_CMD = 1, + SST_TYPE_PARAMS, +}; + +enum sst_flag { + SST_FLAG_BLOCKED = 1, + SST_FLAG_NONBLOCK, +}; + +/* + * Enumeration for indexing the gain cells in VB_SET_GAIN DSP command + */ +enum sst_gain_index { + /* GAIN IDs for SB task start here */ + SST_GAIN_INDEX_CODEC_OUT0, + SST_GAIN_INDEX_CODEC_OUT1, + SST_GAIN_INDEX_CODEC_IN0, + SST_GAIN_INDEX_CODEC_IN1, + + SST_GAIN_INDEX_SPROT_LOOP_OUT, + SST_GAIN_INDEX_MEDIA_LOOP1_OUT, + SST_GAIN_INDEX_MEDIA_LOOP2_OUT, + + SST_GAIN_INDEX_PCM0_IN_LEFT, + SST_GAIN_INDEX_PCM0_IN_RIGHT, + + SST_GAIN_INDEX_PCM1_OUT_LEFT, + SST_GAIN_INDEX_PCM1_OUT_RIGHT, + SST_GAIN_INDEX_PCM1_IN_LEFT, + SST_GAIN_INDEX_PCM1_IN_RIGHT, + SST_GAIN_INDEX_PCM2_OUT_LEFT, + + SST_GAIN_INDEX_PCM2_OUT_RIGHT, + SST_GAIN_INDEX_VOIP_OUT, + SST_GAIN_INDEX_VOIP_IN, + + /* Gain IDs for MMX task start here */ + SST_GAIN_INDEX_MEDIA0_IN_LEFT, + SST_GAIN_INDEX_MEDIA0_IN_RIGHT, + SST_GAIN_INDEX_MEDIA1_IN_LEFT, + SST_GAIN_INDEX_MEDIA1_IN_RIGHT, + + SST_GAIN_INDEX_MEDIA2_IN_LEFT, + SST_GAIN_INDEX_MEDIA2_IN_RIGHT, + + SST_GAIN_INDEX_GAIN_END +}; + +/* + * Audio DSP module IDs specified by FW spec + * TODO: Update with all modules + */ +enum sst_module_id { + SST_MODULE_ID_PCM = 0x0001, + SST_MODULE_ID_MP3 = 0x0002, + SST_MODULE_ID_MP24 = 0x0003, + SST_MODULE_ID_AAC = 0x0004, + SST_MODULE_ID_AACP = 0x0005, + SST_MODULE_ID_EAACP = 0x0006, + SST_MODULE_ID_WMA9 = 0x0007, + SST_MODULE_ID_WMA10 = 0x0008, + SST_MODULE_ID_WMA10P = 0x0009, + SST_MODULE_ID_RA = 0x000A, + SST_MODULE_ID_DDAC3 = 0x000B, + SST_MODULE_ID_TRUE_HD = 0x000C, + SST_MODULE_ID_HD_PLUS = 0x000D, + + SST_MODULE_ID_SRC = 0x0064, + SST_MODULE_ID_DOWNMIX = 0x0066, + SST_MODULE_ID_GAIN_CELL = 0x0067, + SST_MODULE_ID_SPROT = 0x006D, + SST_MODULE_ID_BASS_BOOST = 0x006E, + SST_MODULE_ID_STEREO_WDNG = 0x006F, + SST_MODULE_ID_AV_REMOVAL = 0x0070, + SST_MODULE_ID_MIC_EQ = 0x0071, + SST_MODULE_ID_SPL = 0x0072, + SST_MODULE_ID_ALGO_VTSV = 0x0073, + SST_MODULE_ID_NR = 0x0076, + SST_MODULE_ID_BWX = 0x0077, + SST_MODULE_ID_DRP = 0x0078, + SST_MODULE_ID_MDRP = 0x0079, + + SST_MODULE_ID_ANA = 0x007A, + SST_MODULE_ID_AEC = 0x007B, + SST_MODULE_ID_NR_SNS = 0x007C, + SST_MODULE_ID_SER = 0x007D, + SST_MODULE_ID_AGC = 0x007E, + + SST_MODULE_ID_CNI = 0x007F, + SST_MODULE_ID_CONTEXT_ALGO_AWARE = 0x0080, + SST_MODULE_ID_FIR_24 = 0x0081, + SST_MODULE_ID_IIR_24 = 0x0082, + + SST_MODULE_ID_ASRC = 0x0083, + SST_MODULE_ID_TONE_GEN = 0x0084, + SST_MODULE_ID_BMF = 0x0086, + SST_MODULE_ID_EDL = 0x0087, + SST_MODULE_ID_GLC = 0x0088, + + SST_MODULE_ID_FIR_16 = 0x0089, + SST_MODULE_ID_IIR_16 = 0x008A, + SST_MODULE_ID_DNR = 0x008B, + + SST_MODULE_ID_VIRTUALIZER = 0x008C, + SST_MODULE_ID_VISUALIZATION = 0x008D, + SST_MODULE_ID_LOUDNESS_OPTIMIZER = 0x008E, + SST_MODULE_ID_REVERBERATION = 0x008F, + + SST_MODULE_ID_CNI_TX = 0x0090, + SST_MODULE_ID_REF_LINE = 0x0091, + SST_MODULE_ID_VOLUME = 0x0092, + SST_MODULE_ID_FILT_DCR = 0x0094, + SST_MODULE_ID_SLV = 0x009A, + SST_MODULE_ID_NLF = 0x009B, + SST_MODULE_ID_TNR = 0x009C, + SST_MODULE_ID_WNR = 0x009D, + + SST_MODULE_ID_LOG = 0xFF00, + + SST_MODULE_ID_TASK = 0xFFFF, +}; + +enum sst_cmd { + SBA_IDLE = 14, + SBA_VB_SET_SPEECH_PATH = 26, + MMX_SET_GAIN = 33, + SBA_VB_SET_GAIN = 33, + FBA_VB_RX_CNI = 35, + MMX_SET_GAIN_TIMECONST = 36, + SBA_VB_SET_TIMECONST = 36, + SBA_VB_START = 85, + SBA_SET_SWM = 114, + SBA_SET_MDRP = 116, + SBA_HW_SET_SSP = 117, + SBA_SET_MEDIA_LOOP_MAP = 118, + SBA_SET_MEDIA_PATH = 119, + MMX_SET_MEDIA_PATH = 119, + SBA_VB_LPRO = 126, + SBA_VB_SET_FIR = 128, + SBA_VB_SET_IIR = 129, + SBA_SET_SSP_SLOT_MAP = 130, +}; + +enum sst_dsp_switch { + SST_SWITCH_OFF = 0, + SST_SWITCH_ON = 3, +}; + +enum sst_path_switch { + SST_PATH_OFF = 0, + SST_PATH_ON = 1, +}; + +enum sst_swm_state { + SST_SWM_OFF = 0, + SST_SWM_ON = 3, +}; + +#define SST_FILL_LOCATION_IDS(dst, cell_idx, pipe_id) do { \ + dst.location_id.p.cell_nbr_idx = (cell_idx); \ + dst.location_id.p.path_id = (pipe_id); \ + } while (0) +#define SST_FILL_LOCATION_ID(dst, loc_id) (\ + dst.location_id.f = (loc_id)) +#define SST_FILL_MODULE_ID(dst, mod_id) (\ + dst.module_id = (mod_id)) + +#define SST_FILL_DESTINATION1(dst, id) do { \ + SST_FILL_LOCATION_ID(dst, (id) & 0xFFFF); \ + SST_FILL_MODULE_ID(dst, ((id) & 0xFFFF0000) >> 16); \ + } while (0) +#define SST_FILL_DESTINATION2(dst, loc_id, mod_id) do { \ + SST_FILL_LOCATION_ID(dst, loc_id); \ + SST_FILL_MODULE_ID(dst, mod_id); \ + } while (0) +#define SST_FILL_DESTINATION3(dst, cell_idx, path_id, mod_id) do { \ + SST_FILL_LOCATION_IDS(dst, cell_idx, path_id); \ + SST_FILL_MODULE_ID(dst, mod_id); \ + } while (0) + +#define SST_FILL_DESTINATION(level, dst, ...) \ + SST_FILL_DESTINATION##level(dst, __VA_ARGS__) +#define SST_FILL_DEFAULT_DESTINATION(dst) \ + SST_FILL_DESTINATION(2, dst, SST_DEFAULT_LOCATION_ID, SST_DEFAULT_MODULE_ID) + +struct sst_destination_id { + union sst_location_id { + struct { + u8 cell_nbr_idx; /* module index */ + u8 path_id; /* pipe_id */ + } __packed p; /* part */ + u16 f; /* full */ + } __packed location_id; + u16 module_id; +} __packed; +struct sst_dsp_header { + struct sst_destination_id dst; + u16 command_id; + u16 length; +} __packed; + +/* + * + * Common Commands + * + */ +struct sst_cmd_generic { + struct sst_dsp_header header; +} __packed; +struct sst_cmd_set_params { + struct sst_destination_id dst; + u16 command_id; + char params[0]; +} __packed; +#define SST_CONTROL_NAME(xpname, xmname, xinstance, xtype) \ + xpname " " xmname " " #xinstance " " xtype + +#define SST_COMBO_CONTROL_NAME(xpname, xmname, xinstance, xtype, xsubmodule) \ + xpname " " xmname " " #xinstance " " xtype " " xsubmodule +enum sst_algo_kcontrol_type { + SST_ALGO_PARAMS, + SST_ALGO_BYPASS, +}; + +struct sst_algo_control { + enum sst_algo_kcontrol_type type; + int max; + u16 module_id; + u16 pipe_id; + u16 task_id; + u16 cmd_id; + bool bypass; + unsigned char *params; + struct snd_soc_dapm_widget *w; +}; + +/* size of the control = size of params + size of length field */ +#define SST_ALGO_CTL_VALUE(xcount, xtype, xpipe, xmod, xtask, xcmd) \ + (struct sst_algo_control){ \ + .max = xcount + sizeof(u16), .type = xtype, .module_id = xmod, \ + .pipe_id = xpipe, .task_id = xtask, .cmd_id = xcmd, \ + } + +#define SST_ALGO_KCONTROL(xname, xcount, xmod, xpipe, \ + xtask, xcmd, xtype, xinfo, xget, xput) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .info = xinfo, .get = xget, .put = xput, \ + .private_value = (unsigned long)& \ + SST_ALGO_CTL_VALUE(xcount, xtype, xpipe, \ + xmod, xtask, xcmd), \ +} + +#define SST_ALGO_KCONTROL_BYTES(xpname, xmname, xcount, xmod, \ + xpipe, xinstance, xtask, xcmd) \ + SST_ALGO_KCONTROL(SST_CONTROL_NAME(xpname, xmname, xinstance, "params"), \ + xcount, xmod, xpipe, xtask, xcmd, SST_ALGO_PARAMS, \ + sst_algo_bytes_ctl_info, \ + sst_algo_control_get, sst_algo_control_set) + +#define SST_ALGO_KCONTROL_BOOL(xpname, xmname, xmod, xpipe, xinstance, xtask) \ + SST_ALGO_KCONTROL(SST_CONTROL_NAME(xpname, xmname, xinstance, "bypass"), \ + 0, xmod, xpipe, xtask, 0, SST_ALGO_BYPASS, \ + snd_soc_info_bool_ext, \ + sst_algo_control_get, sst_algo_control_set) + +#define SST_ALGO_BYPASS_PARAMS(xpname, xmname, xcount, xmod, xpipe, \ + xinstance, xtask, xcmd) \ + SST_ALGO_KCONTROL_BOOL(xpname, xmname, xmod, xpipe, xinstance, xtask), \ + SST_ALGO_KCONTROL_BYTES(xpname, xmname, xcount, xmod, xpipe, xinstance, xtask, xcmd) + +#define SST_COMBO_ALGO_KCONTROL_BYTES(xpname, xmname, xsubmod, xcount, xmod, \ + xpipe, xinstance, xtask, xcmd) \ + SST_ALGO_KCONTROL(SST_COMBO_CONTROL_NAME(xpname, xmname, xinstance, "params", \ + xsubmod), \ + xcount, xmod, xpipe, xtask, xcmd, SST_ALGO_PARAMS, \ + sst_algo_bytes_ctl_info, \ + sst_algo_control_get, sst_algo_control_set) + + +struct sst_enum { + bool tx; + unsigned short reg; + unsigned int max; + const char * const *texts; + struct snd_soc_dapm_widget *w; +}; #endif diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c index 61bf6da..33fc5c3 100644 --- a/sound/soc/intel/sst-haswell-pcm.c +++ b/sound/soc/intel/sst-haswell-pcm.c @@ -138,11 +138,10 @@ static inline unsigned int hsw_ipc_to_mixer(u32 value) static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct hsw_priv_data *pdata = - snd_soc_platform_get_drvdata(platform); struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg]; struct sst_hsw *hsw = pdata->hsw; u32 volume; @@ -176,11 +175,10 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol, static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct hsw_priv_data *pdata = - snd_soc_platform_get_drvdata(platform); struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg]; struct sst_hsw *hsw = pdata->hsw; u32 volume; @@ -208,8 +206,8 @@ static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol, static int hsw_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); - struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform); + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt); struct sst_hsw *hsw = pdata->hsw; u32 volume; @@ -233,8 +231,8 @@ static int hsw_volume_put(struct snd_kcontrol *kcontrol, static int hsw_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); - struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform); + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt); struct sst_hsw *hsw = pdata->hsw; unsigned int volume = 0; @@ -778,20 +776,11 @@ static const struct snd_soc_dapm_route graph[] = { static int hsw_pcm_probe(struct snd_soc_platform *platform) { + struct hsw_priv_data *priv_data = snd_soc_platform_get_drvdata(platform); struct sst_pdata *pdata = dev_get_platdata(platform->dev); - struct hsw_priv_data *priv_data; - struct device *dma_dev; + struct device *dma_dev = pdata->dma_dev; int i, ret = 0; - if (!pdata) - return -ENODEV; - - dma_dev = pdata->dma_dev; - - priv_data = devm_kzalloc(platform->dev, sizeof(*priv_data), GFP_KERNEL); - priv_data->hsw = pdata->dsp; - snd_soc_platform_set_drvdata(platform, priv_data); - /* allocate DSP buffer page tables */ for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) { @@ -848,27 +837,38 @@ static struct snd_soc_platform_driver hsw_soc_platform = { .ops = &hsw_pcm_ops, .pcm_new = hsw_pcm_new, .pcm_free = hsw_pcm_free, - .controls = hsw_volume_controls, - .num_controls = ARRAY_SIZE(hsw_volume_controls), - .dapm_widgets = widgets, - .num_dapm_widgets = ARRAY_SIZE(widgets), - .dapm_routes = graph, - .num_dapm_routes = ARRAY_SIZE(graph), }; static const struct snd_soc_component_driver hsw_dai_component = { - .name = "haswell-dai", + .name = "haswell-dai", + .controls = hsw_volume_controls, + .num_controls = ARRAY_SIZE(hsw_volume_controls), + .dapm_widgets = widgets, + .num_dapm_widgets = ARRAY_SIZE(widgets), + .dapm_routes = graph, + .num_dapm_routes = ARRAY_SIZE(graph), }; static int hsw_pcm_dev_probe(struct platform_device *pdev) { struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev); + struct hsw_priv_data *priv_data; int ret; + if (!sst_pdata) + return -EINVAL; + + priv_data = devm_kzalloc(&pdev->dev, sizeof(*priv_data), GFP_KERNEL); + if (!priv_data) + return -ENOMEM; + ret = sst_hsw_dsp_init(&pdev->dev, sst_pdata); if (ret < 0) return -ENODEV; + priv_data->hsw = sst_pdata->dsp; + platform_set_drvdata(pdev, priv_data); + ret = snd_soc_register_platform(&pdev->dev, &hsw_soc_platform); if (ret < 0) goto err_plat; diff --git a/sound/soc/intel/sst-mfld-platform-compress.c b/sound/soc/intel/sst-mfld-platform-compress.c index 29c059c..59467775 100644 --- a/sound/soc/intel/sst-mfld-platform-compress.c +++ b/sound/soc/intel/sst-mfld-platform-compress.c @@ -86,7 +86,7 @@ static int sst_platform_compr_free(struct snd_compr_stream *cstream) /*need to check*/ str_id = stream->id; if (str_id) - ret_val = stream->compr_ops->close(str_id); + ret_val = stream->compr_ops->close(sst->dev, str_id); module_put(sst->dev->driver->owner); kfree(stream); pr_debug("%s: %d\n", __func__, ret_val); @@ -158,7 +158,7 @@ static int sst_platform_compr_set_params(struct snd_compr_stream *cstream, cb.drain_cb_param = cstream; cb.drain_notify = sst_drain_notify; - retval = stream->compr_ops->open(&str_params, &cb); + retval = stream->compr_ops->open(sst->dev, &str_params, &cb); if (retval < 0) { pr_err("stream allocation failed %d\n", retval); return retval; @@ -170,10 +170,30 @@ static int sst_platform_compr_set_params(struct snd_compr_stream *cstream, static int sst_platform_compr_trigger(struct snd_compr_stream *cstream, int cmd) { - struct sst_runtime_stream *stream = - cstream->runtime->private_data; - - return stream->compr_ops->control(cmd, stream->id); + struct sst_runtime_stream *stream = cstream->runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (stream->compr_ops->stream_start) + return stream->compr_ops->stream_start(sst->dev, stream->id); + case SNDRV_PCM_TRIGGER_STOP: + if (stream->compr_ops->stream_drop) + return stream->compr_ops->stream_drop(sst->dev, stream->id); + case SND_COMPR_TRIGGER_DRAIN: + if (stream->compr_ops->stream_drain) + return stream->compr_ops->stream_drain(sst->dev, stream->id); + case SND_COMPR_TRIGGER_PARTIAL_DRAIN: + if (stream->compr_ops->stream_partial_drain) + return stream->compr_ops->stream_partial_drain(sst->dev, stream->id); + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (stream->compr_ops->stream_pause) + return stream->compr_ops->stream_pause(sst->dev, stream->id); + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (stream->compr_ops->stream_pause_release) + return stream->compr_ops->stream_pause_release(sst->dev, stream->id); + default: + return -EINVAL; + } } static int sst_platform_compr_pointer(struct snd_compr_stream *cstream, @@ -182,7 +202,7 @@ static int sst_platform_compr_pointer(struct snd_compr_stream *cstream, struct sst_runtime_stream *stream; stream = cstream->runtime->private_data; - stream->compr_ops->tstamp(stream->id, tstamp); + stream->compr_ops->tstamp(sst->dev, stream->id, tstamp); tstamp->byte_offset = tstamp->copied_total % (u32)cstream->runtime->buffer_size; pr_debug("calc bytes offset/copied bytes as %d\n", tstamp->byte_offset); @@ -195,7 +215,7 @@ static int sst_platform_compr_ack(struct snd_compr_stream *cstream, struct sst_runtime_stream *stream; stream = cstream->runtime->private_data; - stream->compr_ops->ack(stream->id, (unsigned long)bytes); + stream->compr_ops->ack(sst->dev, stream->id, (unsigned long)bytes); stream->bytes_written += bytes; return 0; @@ -225,7 +245,7 @@ static int sst_platform_compr_set_metadata(struct snd_compr_stream *cstream, struct sst_runtime_stream *stream = cstream->runtime->private_data; - return stream->compr_ops->set_metadata(stream->id, metadata); + return stream->compr_ops->set_metadata(sst->dev, stream->id, metadata); } struct snd_compr_ops sst_platform_compr_ops = { diff --git a/sound/soc/intel/sst-mfld-platform-pcm.c b/sound/soc/intel/sst-mfld-platform-pcm.c index 706212a..aa9b600 100644 --- a/sound/soc/intel/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/sst-mfld-platform-pcm.c @@ -43,12 +43,12 @@ int sst_register_dsp(struct sst_device *dev) return -ENODEV; mutex_lock(&sst_lock); if (sst) { - pr_err("we already have a device %s\n", sst->name); + dev_err(dev->dev, "we already have a device %s\n", sst->name); module_put(dev->dev->driver->owner); mutex_unlock(&sst_lock); return -EEXIST; } - pr_debug("registering device %s\n", dev->name); + dev_dbg(dev->dev, "registering device %s\n", dev->name); sst = dev; mutex_unlock(&sst_lock); return 0; @@ -70,7 +70,7 @@ int sst_unregister_dsp(struct sst_device *dev) } module_put(sst->dev->driver->owner); - pr_debug("unreg %s\n", sst->name); + dev_dbg(dev->dev, "unreg %s\n", sst->name); sst = NULL; mutex_unlock(&sst_lock); return 0; @@ -252,7 +252,7 @@ int sst_fill_stream_params(void *substream, } static int sst_platform_alloc_stream(struct snd_pcm_substream *substream, - struct snd_soc_platform *platform) + struct snd_soc_dai *dai) { struct sst_runtime_stream *stream = substream->runtime->private_data; @@ -260,7 +260,7 @@ static int sst_platform_alloc_stream(struct snd_pcm_substream *substream, struct snd_sst_params str_params = {0}; struct snd_sst_alloc_params_ext alloc_params = {0}; int ret_val = 0; - struct sst_data *ctx = snd_soc_platform_get_drvdata(platform); + struct sst_data *ctx = snd_soc_dai_get_drvdata(dai); /* set codec params and inform SST driver the same */ sst_fill_pcm_params(substream, ¶m); @@ -277,7 +277,7 @@ static int sst_platform_alloc_stream(struct snd_pcm_substream *substream, stream->stream_info.str_id = str_params.stream_id; - ret_val = stream->ops->open(&str_params); + ret_val = stream->ops->open(sst->dev, &str_params); if (ret_val <= 0) return ret_val; @@ -306,22 +306,31 @@ static int sst_platform_init_stream(struct snd_pcm_substream *substream) { struct sst_runtime_stream *stream = substream->runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; int ret_val; - pr_debug("setting buffer ptr param\n"); + dev_dbg(rtd->dev, "setting buffer ptr param\n"); sst_set_stream_status(stream, SST_PLATFORM_INIT); stream->stream_info.period_elapsed = sst_period_elapsed; stream->stream_info.arg = substream; stream->stream_info.buffer_ptr = 0; stream->stream_info.sfreq = substream->runtime->rate; - ret_val = stream->ops->device_control( - SST_SND_STREAM_INIT, &stream->stream_info); + ret_val = stream->ops->stream_init(sst->dev, &stream->stream_info); if (ret_val) - pr_err("control_set ret error %d\n", ret_val); + dev_err(rtd->dev, "control_set ret error %d\n", ret_val); return ret_val; } -/* end -- helper functions */ + +static int power_up_sst(struct sst_runtime_stream *stream) +{ + return stream->ops->power(sst->dev, true); +} + +static void power_down_sst(struct sst_runtime_stream *stream) +{ + stream->ops->power(sst->dev, false); +} static int sst_media_open(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) @@ -339,7 +348,7 @@ static int sst_media_open(struct snd_pcm_substream *substream, mutex_lock(&sst_lock); if (!sst || !try_module_get(sst->dev->driver->owner)) { - pr_err("no device available to run\n"); + dev_err(dai->dev, "no device available to run\n"); ret_val = -ENODEV; goto out_ops; } @@ -352,6 +361,10 @@ static int sst_media_open(struct snd_pcm_substream *substream, /* allocate memory for SST API set */ runtime->private_data = stream; + ret_val = power_up_sst(stream); + if (ret_val < 0) + return ret_val; + /* Make sure, that the period size is always even */ snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS, 2); @@ -371,26 +384,29 @@ static void sst_media_close(struct snd_pcm_substream *substream, int ret_val = 0, str_id; stream = substream->runtime->private_data; + power_down_sst(stream); + str_id = stream->stream_info.str_id; if (str_id) - ret_val = stream->ops->close(str_id); + ret_val = stream->ops->close(sst->dev, str_id); module_put(sst->dev->driver->owner); kfree(stream); } -static inline unsigned int get_current_pipe_id(struct snd_soc_platform *platform, +static inline unsigned int get_current_pipe_id(struct snd_soc_dai *dai, struct snd_pcm_substream *substream) { - struct sst_data *sst = snd_soc_platform_get_drvdata(platform); + struct sst_data *sst = snd_soc_dai_get_drvdata(dai); struct sst_dev_stream_map *map = sst->pdata->pdev_strm_map; struct sst_runtime_stream *stream = substream->runtime->private_data; u32 str_id = stream->stream_info.str_id; unsigned int pipe_id; + pipe_id = map[str_id].device_id; - pr_debug("%s: got pipe_id = %#x for str_id = %d\n", - __func__, pipe_id, str_id); + dev_dbg(dai->dev, "got pipe_id = %#x for str_id = %d\n", + pipe_id, str_id); return pipe_id; } @@ -403,12 +419,11 @@ static int sst_media_prepare(struct snd_pcm_substream *substream, stream = substream->runtime->private_data; str_id = stream->stream_info.str_id; if (stream->stream_info.str_id) { - ret_val = stream->ops->device_control( - SST_SND_DROP, &str_id); + ret_val = stream->ops->stream_drop(sst->dev, str_id); return ret_val; } - ret_val = sst_platform_alloc_stream(substream, dai->platform); + ret_val = sst_platform_alloc_stream(substream, dai); if (ret_val <= 0) return ret_val; snprintf(substream->pcm->id, sizeof(substream->pcm->id), @@ -461,37 +476,40 @@ static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream, { int ret_val = 0, str_id; struct sst_runtime_stream *stream; - int str_cmd, status; + int status; + struct snd_soc_pcm_runtime *rtd = substream->private_data; - pr_debug("sst_platform_pcm_trigger called\n"); + dev_dbg(rtd->dev, "sst_platform_pcm_trigger called\n"); + if (substream->pcm->internal) + return 0; stream = substream->runtime->private_data; str_id = stream->stream_info.str_id; switch (cmd) { case SNDRV_PCM_TRIGGER_START: - pr_debug("sst: Trigger Start\n"); - str_cmd = SST_SND_START; + dev_dbg(rtd->dev, "sst: Trigger Start\n"); status = SST_PLATFORM_RUNNING; stream->stream_info.arg = substream; + ret_val = stream->ops->stream_start(sst->dev, str_id); break; case SNDRV_PCM_TRIGGER_STOP: - pr_debug("sst: in stop\n"); - str_cmd = SST_SND_DROP; + dev_dbg(rtd->dev, "sst: in stop\n"); status = SST_PLATFORM_DROPPED; + ret_val = stream->ops->stream_drop(sst->dev, str_id); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - pr_debug("sst: in pause\n"); - str_cmd = SST_SND_PAUSE; + dev_dbg(rtd->dev, "sst: in pause\n"); status = SST_PLATFORM_PAUSED; + ret_val = stream->ops->stream_pause(sst->dev, str_id); break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - pr_debug("sst: in pause release\n"); - str_cmd = SST_SND_RESUME; + dev_dbg(rtd->dev, "sst: in pause release\n"); status = SST_PLATFORM_RUNNING; + ret_val = stream->ops->stream_pause_release(sst->dev, str_id); break; default: return -EINVAL; } - ret_val = stream->ops->device_control(str_cmd, &str_id); + if (!ret_val) sst_set_stream_status(stream, status); @@ -505,16 +523,16 @@ static snd_pcm_uframes_t sst_platform_pcm_pointer struct sst_runtime_stream *stream; int ret_val, status; struct pcm_stream_info *str_info; + struct snd_soc_pcm_runtime *rtd = substream->private_data; stream = substream->runtime->private_data; status = sst_get_stream_status(stream); if (status == SST_PLATFORM_INIT) return 0; str_info = &stream->stream_info; - ret_val = stream->ops->device_control( - SST_SND_BUFFER_POINTER, str_info); + ret_val = stream->ops->stream_read_tstamp(sst->dev, str_info); if (ret_val) { - pr_err("sst: error code = %d\n", ret_val); + dev_err(rtd->dev, "sst: error code = %d\n", ret_val); return ret_val; } substream->runtime->delay = str_info->pcm_delay; @@ -530,7 +548,7 @@ static struct snd_pcm_ops sst_platform_ops = { static void sst_pcm_free(struct snd_pcm *pcm) { - pr_debug("sst_pcm_free called\n"); + dev_dbg(pcm->dev, "sst_pcm_free called\n"); snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -547,14 +565,20 @@ static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd) snd_dma_continuous_data(GFP_DMA), SST_MIN_BUFFER, SST_MAX_BUFFER); if (retval) { - pr_err("dma buffer allocationf fail\n"); + dev_err(rtd->dev, "dma buffer allocationf fail\n"); return retval; } } return retval; } -static struct snd_soc_platform_driver sst_soc_platform_drv = { +static int sst_soc_probe(struct snd_soc_platform *platform) +{ + return sst_dsp_init_v2_dpcm(platform); +} + +static struct snd_soc_platform_driver sst_soc_platform_drv = { + .probe = sst_soc_probe, .ops = &sst_platform_ops, .compr_ops = &sst_platform_compr_ops, .pcm_new = sst_pcm_new, @@ -574,13 +598,11 @@ static int sst_platform_probe(struct platform_device *pdev) drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); if (drv == NULL) { - pr_err("kzalloc failed\n"); return -ENOMEM; } pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (pdata == NULL) { - pr_err("kzalloc failed for pdata\n"); return -ENOMEM; } @@ -592,14 +614,14 @@ static int sst_platform_probe(struct platform_device *pdev) ret = snd_soc_register_platform(&pdev->dev, &sst_soc_platform_drv); if (ret) { - pr_err("registering soc platform failed\n"); + dev_err(&pdev->dev, "registering soc platform failed\n"); return ret; } ret = snd_soc_register_component(&pdev->dev, &sst_component, sst_platform_dai, ARRAY_SIZE(sst_platform_dai)); if (ret) { - pr_err("registering cpu dais failed\n"); + dev_err(&pdev->dev, "registering cpu dais failed\n"); snd_soc_unregister_platform(&pdev->dev); } return ret; @@ -610,7 +632,7 @@ static int sst_platform_remove(struct platform_device *pdev) snd_soc_unregister_component(&pdev->dev); snd_soc_unregister_platform(&pdev->dev); - pr_debug("sst_platform_remove success\n"); + dev_dbg(&pdev->dev, "sst_platform_remove success\n"); return 0; } diff --git a/sound/soc/intel/sst-mfld-platform.h b/sound/soc/intel/sst-mfld-platform.h index 6c6a42c..19f83ec 100644 --- a/sound/soc/intel/sst-mfld-platform.h +++ b/sound/soc/intel/sst-mfld-platform.h @@ -54,20 +54,6 @@ enum sst_drv_status { SST_PLATFORM_DROPPED, }; -enum sst_controls { - SST_SND_ALLOC = 0x00, - SST_SND_PAUSE = 0x01, - SST_SND_RESUME = 0x02, - SST_SND_DROP = 0x03, - SST_SND_FREE = 0x04, - SST_SND_BUFFER_POINTER = 0x05, - SST_SND_STREAM_INIT = 0x06, - SST_SND_START = 0x07, - SST_SET_BYTE_STREAM = 0x100A, - SST_GET_BYTE_STREAM = 0x100B, - SST_MAX_CONTROLS = SST_GET_BYTE_STREAM, -}; - enum sst_stream_ops { STREAM_OPS_PLAYBACK = 0, STREAM_OPS_CAPTURE, @@ -113,24 +99,37 @@ struct sst_compress_cb { struct compress_sst_ops { const char *name; - int (*open) (struct snd_sst_params *str_params, - struct sst_compress_cb *cb); - int (*control) (unsigned int cmd, unsigned int str_id); - int (*tstamp) (unsigned int str_id, struct snd_compr_tstamp *tstamp); - int (*ack) (unsigned int str_id, unsigned long bytes); - int (*close) (unsigned int str_id); - int (*get_caps) (struct snd_compr_caps *caps); - int (*get_codec_caps) (struct snd_compr_codec_caps *codec); - int (*set_metadata) (unsigned int str_id, + int (*open)(struct device *dev, + struct snd_sst_params *str_params, struct sst_compress_cb *cb); + int (*stream_start)(struct device *dev, unsigned int str_id); + int (*stream_drop)(struct device *dev, unsigned int str_id); + int (*stream_drain)(struct device *dev, unsigned int str_id); + int (*stream_partial_drain)(struct device *dev, unsigned int str_id); + int (*stream_pause)(struct device *dev, unsigned int str_id); + int (*stream_pause_release)(struct device *dev, unsigned int str_id); + + int (*tstamp)(struct device *dev, unsigned int str_id, + struct snd_compr_tstamp *tstamp); + int (*ack)(struct device *dev, unsigned int str_id, + unsigned long bytes); + int (*close)(struct device *dev, unsigned int str_id); + int (*get_caps)(struct snd_compr_caps *caps); + int (*get_codec_caps)(struct snd_compr_codec_caps *codec); + int (*set_metadata)(struct device *dev, unsigned int str_id, struct snd_compr_metadata *mdata); - }; struct sst_ops { - int (*open) (struct snd_sst_params *str_param); - int (*device_control) (int cmd, void *arg); - int (*set_generic_params)(enum sst_controls cmd, void *arg); - int (*close) (unsigned int str_id); + int (*open)(struct device *dev, struct snd_sst_params *str_param); + int (*stream_init)(struct device *dev, struct pcm_stream_info *str_info); + int (*stream_start)(struct device *dev, int str_id); + int (*stream_drop)(struct device *dev, int str_id); + int (*stream_pause)(struct device *dev, int str_id); + int (*stream_pause_release)(struct device *dev, int str_id); + int (*stream_read_tstamp)(struct device *dev, struct pcm_stream_info *str_info); + int (*send_byte_stream)(struct device *dev, struct snd_sst_bytes_v2 *bytes); + int (*close)(struct device *dev, unsigned int str_id); + int (*power)(struct device *dev, bool state); }; struct sst_runtime_stream { @@ -152,6 +151,8 @@ struct sst_device { }; struct sst_data; + +int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform); void sst_set_stream_status(struct sst_runtime_stream *stream, int state); int sst_fill_stream_params(void *substream, const struct sst_data *ctx, struct snd_sst_params *str_params, bool is_compress); @@ -166,6 +167,7 @@ struct sst_algo_int_control_v2 { struct sst_data { struct platform_device *pdev; struct sst_platform_data *pdata; + struct snd_sst_bytes_v2 *byte_stream; struct mutex lock; }; int sst_register_dsp(struct sst_device *sst); diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c index 943922c..b10ae80 100644 --- a/sound/soc/omap/rx51.c +++ b/sound/soc/omap/rx51.c @@ -168,7 +168,7 @@ static int rx51_spk_event(struct snd_soc_dapm_widget *w, static int rx51_hp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { - struct snd_soc_codec *codec = w->dapm->codec; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); if (SND_SOC_DAPM_EVENT_ON(event)) tpa6130a2_stereo_enable(codec, 1); diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig index c196a46..78fc159 100644 --- a/sound/soc/rockchip/Kconfig +++ b/sound/soc/rockchip/Kconfig @@ -2,11 +2,10 @@ config SND_SOC_ROCKCHIP tristate "ASoC support for Rockchip" depends on COMPILE_TEST || ARCH_ROCKCHIP select SND_SOC_GENERIC_DMAENGINE_PCM - select SND_ROCKCHIP_I2S help Say Y or M if you want to add support for codecs attached to the Rockchip SoCs' Audio interfaces. You will also need to select the audio interfaces to support below. -config SND_ROCKCHIP_I2S +config SND_SOC_ROCKCHIP_I2S tristate diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile index 1006418..b921909 100644 --- a/sound/soc/rockchip/Makefile +++ b/sound/soc/rockchip/Makefile @@ -1,4 +1,4 @@ # ROCKCHIP Platform Support snd-soc-i2s-objs := rockchip_i2s.o -obj-$(CONFIG_SND_ROCKCHIP_I2S) += snd-soc-i2s.o +obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-i2s.o diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index fb9e05c..f373e37 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -108,8 +108,10 @@ static void rockchip_snd_txctrl(struct rk_i2s_dev *i2s, int on) while (val) { regmap_read(i2s->regmap, I2S_CLR, &val); retry--; - if (!retry) + if (!retry) { dev_warn(i2s->dev, "fail to clear\n"); + break; + } } } } @@ -244,16 +246,6 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream, regmap_update_bits(i2s->regmap, I2S_TXCR, I2S_TXCR_VDW_MASK, val); regmap_update_bits(i2s->regmap, I2S_RXCR, I2S_RXCR_VDW_MASK, val); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - dai->playback_dma_data = &i2s->playback_dma_data; - regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK, - I2S_DMACR_TDL(1) | I2S_DMACR_TDE_ENABLE); - } else { - dai->capture_dma_data = &i2s->capture_dma_data; - regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK, - I2S_DMACR_RDL(1) | I2S_DMACR_RDE_ENABLE); - } - return 0; } @@ -301,6 +293,16 @@ static int rockchip_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, return ret; } +static int rockchip_i2s_dai_probe(struct snd_soc_dai *dai) +{ + struct rk_i2s_dev *i2s = snd_soc_dai_get_drvdata(dai); + + dai->capture_dma_data = &i2s->capture_dma_data; + dai->playback_dma_data = &i2s->playback_dma_data; + + return 0; +} + static const struct snd_soc_dai_ops rockchip_i2s_dai_ops = { .hw_params = rockchip_i2s_hw_params, .set_sysclk = rockchip_i2s_set_sysclk, @@ -309,7 +311,9 @@ static const struct snd_soc_dai_ops rockchip_i2s_dai_ops = { }; static struct snd_soc_dai_driver rockchip_i2s_dai = { + .probe = rockchip_i2s_dai_probe, .playback = { + .stream_name = "Playback", .channels_min = 2, .channels_max = 8, .rates = SNDRV_PCM_RATE_8000_192000, @@ -319,6 +323,7 @@ static struct snd_soc_dai_driver rockchip_i2s_dai = { SNDRV_PCM_FMTBIT_S24_LE), }, .capture = { + .stream_name = "Capture", .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_192000, @@ -420,6 +425,11 @@ static int rockchip_i2s_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Can't retrieve i2s bus clock\n"); return PTR_ERR(i2s->hclk); } + ret = clk_prepare_enable(i2s->hclk); + if (ret) { + dev_err(i2s->dev, "hclock enable failed %d\n", ret); + return ret; + } i2s->mclk = devm_clk_get(&pdev->dev, "i2s_clk"); if (IS_ERR(i2s->mclk)) { diff --git a/sound/soc/samsung/idma.c b/sound/soc/samsung/idma.c index db6cefa..0e8dd98 100644 --- a/sound/soc/samsung/idma.c +++ b/sound/soc/samsung/idma.c @@ -351,7 +351,7 @@ static void idma_free(struct snd_pcm *pcm) if (!buf->area) return; - iounmap(buf->area); + iounmap((void __iomem *)buf->area); buf->area = NULL; buf->addr = 0; @@ -369,7 +369,7 @@ static int preallocate_idma_buffer(struct snd_pcm *pcm, int stream) buf->dev.type = SNDRV_DMA_TYPE_CONTINUOUS; buf->addr = idma.lp_tx_addr; buf->bytes = idma_hardware.buffer_bytes_max; - buf->area = (unsigned char *)ioremap(buf->addr, buf->bytes); + buf->area = (unsigned char * __force)ioremap(buf->addr, buf->bytes); return 0; } diff --git a/sound/soc/samsung/odroidx2_max98090.c b/sound/soc/samsung/odroidx2_max98090.c index 278edf9..3c8f604 100644 --- a/sound/soc/samsung/odroidx2_max98090.c +++ b/sound/soc/samsung/odroidx2_max98090.c @@ -66,12 +66,12 @@ static struct snd_soc_card odroidx2 = { .late_probe = odroidx2_late_probe, }; -struct odroidx2_drv_data odroidx2_drvdata = { +static const struct odroidx2_drv_data odroidx2_drvdata = { .dapm_widgets = odroidx2_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(odroidx2_dapm_widgets), }; -struct odroidx2_drv_data odroidu3_drvdata = { +static const struct odroidx2_drv_data odroidu3_drvdata = { .dapm_widgets = odroidu3_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(odroidu3_dapm_widgets), }; diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c index 9902efc..a054826 100644 --- a/sound/soc/samsung/speyside.c +++ b/sound/soc/samsung/speyside.c @@ -228,10 +228,12 @@ static struct snd_soc_dai_link speyside_dai[] = { }, }; -static int speyside_wm9081_init(struct snd_soc_dapm_context *dapm) +static int speyside_wm9081_init(struct snd_soc_component *component) { + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + /* At any time the WM9081 is active it will have this clock */ - return snd_soc_codec_set_sysclk(dapm->codec, WM9081_SYSCLK_MCLK, 0, + return snd_soc_codec_set_sysclk(codec, WM9081_SYSCLK_MCLK, 0, MCLK_AUDIO_RATE, 0); } diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index c763443..66fddec 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -1297,9 +1297,14 @@ static int fsi_dma_transfer(struct fsi_priv *fsi, struct fsi_stream *io) struct snd_pcm_substream *substream = io->substream; struct dma_async_tx_descriptor *desc; int is_play = fsi_stream_is_play(fsi, io); - enum dma_data_direction dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + enum dma_transfer_direction dir; int ret = -EIO; + if (is_play) + dir = DMA_MEM_TO_DEV; + else + dir = DMA_DEV_TO_MEM; + desc = dmaengine_prep_dma_cyclic(io->chan, substream->runtime->dma_addr, snd_pcm_lib_buffer_bytes(substream), diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 19f7896..1922ec5 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -798,10 +798,8 @@ if (name##_node) { \ mod_parse(src); mod_parse(dvc); - if (playback) - of_node_put(playback); - if (capture) - of_node_put(capture); + of_node_put(playback); + of_node_put(capture); } dai_i++; diff --git a/sound/soc/sh/siu_pcm.c b/sound/soc/sh/siu_pcm.c index 488f9be..32eb6da 100644 --- a/sound/soc/sh/siu_pcm.c +++ b/sound/soc/sh/siu_pcm.c @@ -139,7 +139,7 @@ static int siu_pcm_wr_set(struct siu_port *port_info, desc->callback = siu_dma_tx_complete; desc->callback_param = siu_stream; - cookie = desc->tx_submit(desc); + cookie = dmaengine_submit(desc); if (cookie < 0) { dev_err(dev, "Failed to submit a dma transfer\n"); return cookie; @@ -189,7 +189,7 @@ static int siu_pcm_rd_set(struct siu_port *port_info, desc->callback = siu_dma_tx_complete; desc->callback_param = siu_stream; - cookie = desc->tx_submit(desc); + cookie = dmaengine_submit(desc); if (cookie < 0) { dev_err(dev, "Failed to submit dma descriptor\n"); return cookie; diff --git a/sound/soc/sirf/sirf-usp.c b/sound/soc/sirf/sirf-usp.c index 3a73037..186dc7f 100644 --- a/sound/soc/sirf/sirf-usp.c +++ b/sound/soc/sirf/sirf-usp.c @@ -100,6 +100,16 @@ static int sirf_usp_pcm_set_dai_fmt(struct snd_soc_dai *dai, return -EINVAL; } + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + usp->daifmt_format |= (fmt & SND_SOC_DAIFMT_INV_MASK); + break; + default: + return -EINVAL; + } + return 0; } @@ -177,7 +187,7 @@ static int sirf_usp_pcm_hw_params(struct snd_pcm_substream *substream, shifter_len = data_len; - switch (usp->daifmt_format) { + switch (usp->daifmt_format & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: regmap_update_bits(usp->regmap, USP_RX_FRAME_CTRL, USP_I2S_SYNC_CHG, USP_I2S_SYNC_CHG); @@ -193,6 +203,18 @@ static int sirf_usp_pcm_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + switch (usp->daifmt_format & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + regmap_update_bits(usp->regmap, USP_MODE1, + USP_RXD_ACT_EDGE_FALLING | USP_TXD_ACT_EDGE_FALLING, + USP_RXD_ACT_EDGE_FALLING); + break; + default: + return -EINVAL; + } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) regmap_update_bits(usp->regmap, USP_TX_FRAME_CTRL, USP_TXC_DATA_LEN_MASK | USP_TXC_FRAME_LEN_MASK diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index d074aa9..4c8f8a2 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -270,79 +270,54 @@ static const struct file_operations codec_reg_fops = { .llseek = default_llseek, }; -static struct dentry *soc_debugfs_create_dir(struct dentry *parent, - const char *fmt, ...) +static void soc_init_component_debugfs(struct snd_soc_component *component) { - struct dentry *de; - va_list ap; - char *s; + if (component->debugfs_prefix) { + char *name; - va_start(ap, fmt); - s = kvasprintf(GFP_KERNEL, fmt, ap); - va_end(ap); + name = kasprintf(GFP_KERNEL, "%s:%s", + component->debugfs_prefix, component->name); + if (name) { + component->debugfs_root = debugfs_create_dir(name, + component->card->debugfs_card_root); + kfree(name); + } + } else { + component->debugfs_root = debugfs_create_dir(component->name, + component->card->debugfs_card_root); + } - if (!s) - return NULL; + if (!component->debugfs_root) { + dev_warn(component->dev, + "ASoC: Failed to create component debugfs directory\n"); + return; + } - de = debugfs_create_dir(s, parent); - kfree(s); + snd_soc_dapm_debugfs_init(snd_soc_component_get_dapm(component), + component->debugfs_root); - return de; + if (component->init_debugfs) + component->init_debugfs(component); } -static void soc_init_codec_debugfs(struct snd_soc_codec *codec) +static void soc_cleanup_component_debugfs(struct snd_soc_component *component) { - struct dentry *debugfs_card_root = codec->component.card->debugfs_card_root; + debugfs_remove_recursive(component->debugfs_root); +} - codec->debugfs_codec_root = soc_debugfs_create_dir(debugfs_card_root, - "codec:%s", - codec->component.name); - if (!codec->debugfs_codec_root) { - dev_warn(codec->dev, - "ASoC: Failed to create codec debugfs directory\n"); - return; - } +static void soc_init_codec_debugfs(struct snd_soc_component *component) +{ + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); - debugfs_create_bool("cache_sync", 0444, codec->debugfs_codec_root, + debugfs_create_bool("cache_sync", 0444, codec->component.debugfs_root, &codec->cache_sync); - debugfs_create_bool("cache_only", 0444, codec->debugfs_codec_root, - &codec->cache_only); codec->debugfs_reg = debugfs_create_file("codec_reg", 0644, - codec->debugfs_codec_root, + codec->component.debugfs_root, codec, &codec_reg_fops); if (!codec->debugfs_reg) dev_warn(codec->dev, "ASoC: Failed to create codec register debugfs file\n"); - - snd_soc_dapm_debugfs_init(&codec->dapm, codec->debugfs_codec_root); -} - -static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec) -{ - debugfs_remove_recursive(codec->debugfs_codec_root); -} - -static void soc_init_platform_debugfs(struct snd_soc_platform *platform) -{ - struct dentry *debugfs_card_root = platform->component.card->debugfs_card_root; - - platform->debugfs_platform_root = soc_debugfs_create_dir(debugfs_card_root, - "platform:%s", - platform->component.name); - if (!platform->debugfs_platform_root) { - dev_warn(platform->dev, - "ASoC: Failed to create platform debugfs directory\n"); - return; - } - - snd_soc_dapm_debugfs_init(&platform->component.dapm, - platform->debugfs_platform_root); -} - -static void soc_cleanup_platform_debugfs(struct snd_soc_platform *platform) -{ - debugfs_remove_recursive(platform->debugfs_platform_root); } static ssize_t codec_list_read_file(struct file *file, char __user *user_buf, @@ -474,19 +449,15 @@ static void soc_cleanup_card_debugfs(struct snd_soc_card *card) #else -static inline void soc_init_codec_debugfs(struct snd_soc_codec *codec) -{ -} +#define soc_init_codec_debugfs NULL -static inline void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec) +static inline void soc_init_component_debugfs( + struct snd_soc_component *component) { } -static inline void soc_init_platform_debugfs(struct snd_soc_platform *platform) -{ -} - -static inline void soc_cleanup_platform_debugfs(struct snd_soc_platform *platform) +static inline void soc_cleanup_component_debugfs( + struct snd_soc_component *component) { } @@ -579,10 +550,8 @@ int snd_soc_suspend(struct device *dev) struct snd_soc_codec *codec; int i, j; - /* If the initialization of this soc device failed, there is no codec - * associated with it. Just bail out in this case. - */ - if (list_empty(&card->codec_dev_list)) + /* If the card is not initialized yet there is nothing to do */ + if (!card->instantiated) return 0; /* Due to the resume being scheduled into a workqueue we could @@ -668,7 +637,7 @@ int snd_soc_suspend(struct device *dev) list_for_each_entry(codec, &card->codec_dev_list, card_list) { /* If there are paths active then the CODEC will be held with * bias _ON and should not be suspended. */ - if (!codec->suspended && codec->driver->suspend) { + if (!codec->suspended) { switch (codec->dapm.bias_level) { case SND_SOC_BIAS_STANDBY: /* @@ -682,8 +651,10 @@ int snd_soc_suspend(struct device *dev) "ASoC: idle_bias_off CODEC on over suspend\n"); break; } + case SND_SOC_BIAS_OFF: - codec->driver->suspend(codec); + if (codec->driver->suspend) + codec->driver->suspend(codec); codec->suspended = 1; codec->cache_sync = 1; if (codec->component.regmap) @@ -757,11 +728,12 @@ static void soc_resume_deferred(struct work_struct *work) * left with bias OFF or STANDBY and suspended so we must now * resume. Otherwise the suspend was suppressed. */ - if (codec->driver->resume && codec->suspended) { + if (codec->suspended) { switch (codec->dapm.bias_level) { case SND_SOC_BIAS_STANDBY: case SND_SOC_BIAS_OFF: - codec->driver->resume(codec); + if (codec->driver->resume) + codec->driver->resume(codec); codec->suspended = 0; break; default: @@ -835,10 +807,8 @@ int snd_soc_resume(struct device *dev) struct snd_soc_card *card = dev_get_drvdata(dev); int i, ac97_control = 0; - /* If the initialization of this soc device failed, there is no codec - * associated with it. Just bail out in this case. - */ - if (list_empty(&card->codec_dev_list)) + /* If the card is not initialized yet there is nothing to do */ + if (!card->instantiated) return 0; /* activate pins from sleep state */ @@ -887,35 +857,40 @@ EXPORT_SYMBOL_GPL(snd_soc_resume); static const struct snd_soc_dai_ops null_dai_ops = { }; -static struct snd_soc_codec *soc_find_codec( - const struct device_node *codec_of_node, - const char *codec_name) +static struct snd_soc_component *soc_find_component( + const struct device_node *of_node, const char *name) { - struct snd_soc_codec *codec; + struct snd_soc_component *component; - list_for_each_entry(codec, &codec_list, list) { - if (codec_of_node) { - if (codec->dev->of_node != codec_of_node) - continue; - } else { - if (strcmp(codec->component.name, codec_name)) - continue; + list_for_each_entry(component, &component_list, list) { + if (of_node) { + if (component->dev->of_node == of_node) + return component; + } else if (strcmp(component->name, name) == 0) { + return component; } - - return codec; } return NULL; } -static struct snd_soc_dai *soc_find_codec_dai(struct snd_soc_codec *codec, - const char *codec_dai_name) +static struct snd_soc_dai *snd_soc_find_dai( + const struct snd_soc_dai_link_component *dlc) { - struct snd_soc_dai *codec_dai; + struct snd_soc_component *component; + struct snd_soc_dai *dai; - list_for_each_entry(codec_dai, &codec->component.dai_list, list) { - if (!strcmp(codec_dai->name, codec_dai_name)) { - return codec_dai; + /* Find CPU DAI from registered DAIs*/ + list_for_each_entry(component, &component_list, list) { + if (dlc->of_node && component->dev->of_node != dlc->of_node) + continue; + if (dlc->name && strcmp(dev_name(component->dev), dlc->name)) + continue; + list_for_each_entry(dai, &component->dai_list, list) { + if (dlc->dai_name && strcmp(dai->name, dlc->dai_name)) + continue; + + return dai; } } @@ -926,33 +901,19 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num) { struct snd_soc_dai_link *dai_link = &card->dai_link[num]; struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; - struct snd_soc_component *component; struct snd_soc_dai_link_component *codecs = dai_link->codecs; + struct snd_soc_dai_link_component cpu_dai_component; struct snd_soc_dai **codec_dais = rtd->codec_dais; struct snd_soc_platform *platform; - struct snd_soc_dai *cpu_dai; const char *platform_name; int i; dev_dbg(card->dev, "ASoC: binding %s at idx %d\n", dai_link->name, num); - /* Find CPU DAI from registered DAIs*/ - list_for_each_entry(component, &component_list, list) { - if (dai_link->cpu_of_node && - component->dev->of_node != dai_link->cpu_of_node) - continue; - if (dai_link->cpu_name && - strcmp(dev_name(component->dev), dai_link->cpu_name)) - continue; - list_for_each_entry(cpu_dai, &component->dai_list, list) { - if (dai_link->cpu_dai_name && - strcmp(cpu_dai->name, dai_link->cpu_dai_name)) - continue; - - rtd->cpu_dai = cpu_dai; - } - } - + cpu_dai_component.name = dai_link->cpu_name; + cpu_dai_component.of_node = dai_link->cpu_of_node; + cpu_dai_component.dai_name = dai_link->cpu_dai_name; + rtd->cpu_dai = snd_soc_find_dai(&cpu_dai_component); if (!rtd->cpu_dai) { dev_err(card->dev, "ASoC: CPU DAI %s not registered\n", dai_link->cpu_dai_name); @@ -963,15 +924,7 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num) /* Find CODEC from registered CODECs */ for (i = 0; i < rtd->num_codecs; i++) { - struct snd_soc_codec *codec; - codec = soc_find_codec(codecs[i].of_node, codecs[i].name); - if (!codec) { - dev_err(card->dev, "ASoC: CODEC %s not registered\n", - codecs[i].name); - return -EPROBE_DEFER; - } - - codec_dais[i] = soc_find_codec_dai(codec, codecs[i].dai_name); + codec_dais[i] = snd_soc_find_dai(&codecs[i]); if (!codec_dais[i]) { dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n", codecs[i].dai_name); @@ -1012,68 +965,46 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num) return 0; } -static int soc_remove_platform(struct snd_soc_platform *platform) +static void soc_remove_component(struct snd_soc_component *component) { - int ret; - - if (platform->driver->remove) { - ret = platform->driver->remove(platform); - if (ret < 0) - dev_err(platform->dev, "ASoC: failed to remove %d\n", - ret); - } - - /* Make sure all DAPM widgets are freed */ - snd_soc_dapm_free(&platform->component.dapm); - - soc_cleanup_platform_debugfs(platform); - platform->probed = 0; - module_put(platform->dev->driver->owner); - - return 0; -} + if (!component->probed) + return; -static void soc_remove_codec(struct snd_soc_codec *codec) -{ - int err; + /* This is a HACK and will be removed soon */ + if (component->codec) + list_del(&component->codec->card_list); - if (codec->driver->remove) { - err = codec->driver->remove(codec); - if (err < 0) - dev_err(codec->dev, "ASoC: failed to remove %d\n", err); - } + if (component->remove) + component->remove(component); - /* Make sure all DAPM widgets are freed */ - snd_soc_dapm_free(&codec->dapm); + snd_soc_dapm_free(snd_soc_component_get_dapm(component)); - soc_cleanup_codec_debugfs(codec); - codec->probed = 0; - list_del(&codec->card_list); - module_put(codec->dev->driver->owner); + soc_cleanup_component_debugfs(component); + component->probed = 0; + module_put(component->dev->driver->owner); } -static void soc_remove_codec_dai(struct snd_soc_dai *codec_dai, int order) +static void soc_remove_dai(struct snd_soc_dai *dai, int order) { int err; - if (codec_dai && codec_dai->probed && - codec_dai->driver->remove_order == order) { - if (codec_dai->driver->remove) { - err = codec_dai->driver->remove(codec_dai); + if (dai && dai->probed && + dai->driver->remove_order == order) { + if (dai->driver->remove) { + err = dai->driver->remove(dai); if (err < 0) - dev_err(codec_dai->dev, + dev_err(dai->dev, "ASoC: failed to remove %s: %d\n", - codec_dai->name, err); + dai->name, err); } - codec_dai->probed = 0; + dai->probed = 0; } } static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order) { struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int i, err; + int i; /* unregister the rtd device */ if (rtd->dev_registered) { @@ -1085,22 +1016,9 @@ static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order) /* remove the CODEC DAI */ for (i = 0; i < rtd->num_codecs; i++) - soc_remove_codec_dai(rtd->codec_dais[i], order); + soc_remove_dai(rtd->codec_dais[i], order); - /* remove the cpu_dai */ - if (cpu_dai && cpu_dai->probed && - cpu_dai->driver->remove_order == order) { - if (cpu_dai->driver->remove) { - err = cpu_dai->driver->remove(cpu_dai); - if (err < 0) - dev_err(cpu_dai->dev, - "ASoC: failed to remove %s: %d\n", - cpu_dai->name, err); - } - cpu_dai->probed = 0; - if (!cpu_dai->codec) - module_put(cpu_dai->dev->driver->owner); - } + soc_remove_dai(rtd->cpu_dai, order); } static void soc_remove_link_components(struct snd_soc_card *card, int num, @@ -1109,29 +1027,24 @@ static void soc_remove_link_components(struct snd_soc_card *card, int num, struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_platform *platform = rtd->platform; - struct snd_soc_codec *codec; + struct snd_soc_component *component; int i; /* remove the platform */ - if (platform && platform->probed && - platform->driver->remove_order == order) { - soc_remove_platform(platform); - } + if (platform && platform->component.driver->remove_order == order) + soc_remove_component(&platform->component); /* remove the CODEC-side CODEC */ for (i = 0; i < rtd->num_codecs; i++) { - codec = rtd->codec_dais[i]->codec; - if (codec && codec->probed && - codec->driver->remove_order == order) - soc_remove_codec(codec); + component = rtd->codec_dais[i]->component; + if (component->driver->remove_order == order) + soc_remove_component(component); } /* remove any CPU-side CODEC */ if (cpu_dai) { - codec = cpu_dai->codec; - if (codec && codec->probed && - codec->driver->remove_order == order) - soc_remove_codec(codec); + if (cpu_dai->component->driver->remove_order == order) + soc_remove_component(cpu_dai->component); } } @@ -1173,137 +1086,78 @@ static void soc_set_name_prefix(struct snd_soc_card *card, } } -static int soc_probe_codec(struct snd_soc_card *card, - struct snd_soc_codec *codec) +static int soc_probe_component(struct snd_soc_card *card, + struct snd_soc_component *component) { - int ret = 0; - const struct snd_soc_codec_driver *driver = codec->driver; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); struct snd_soc_dai *dai; + int ret; - codec->component.card = card; - codec->dapm.card = card; - soc_set_name_prefix(card, &codec->component); + if (component->probed) + return 0; + + component->card = card; + dapm->card = card; + soc_set_name_prefix(card, component); - if (!try_module_get(codec->dev->driver->owner)) + if (!try_module_get(component->dev->driver->owner)) return -ENODEV; - soc_init_codec_debugfs(codec); + soc_init_component_debugfs(component); - if (driver->dapm_widgets) { - ret = snd_soc_dapm_new_controls(&codec->dapm, - driver->dapm_widgets, - driver->num_dapm_widgets); + if (component->dapm_widgets) { + ret = snd_soc_dapm_new_controls(dapm, component->dapm_widgets, + component->num_dapm_widgets); if (ret != 0) { - dev_err(codec->dev, + dev_err(component->dev, "Failed to create new controls %d\n", ret); goto err_probe; } } - /* Create DAPM widgets for each DAI stream */ - list_for_each_entry(dai, &codec->component.dai_list, list) { - ret = snd_soc_dapm_new_dai_widgets(&codec->dapm, dai); - + list_for_each_entry(dai, &component->dai_list, list) { + ret = snd_soc_dapm_new_dai_widgets(dapm, dai); if (ret != 0) { - dev_err(codec->dev, + dev_err(component->dev, "Failed to create DAI widgets %d\n", ret); goto err_probe; } } - codec->dapm.idle_bias_off = driver->idle_bias_off; - - if (driver->probe) { - ret = driver->probe(codec); + if (component->probe) { + ret = component->probe(component); if (ret < 0) { - dev_err(codec->dev, - "ASoC: failed to probe CODEC %d\n", ret); + dev_err(component->dev, + "ASoC: failed to probe component %d\n", ret); goto err_probe; } - WARN(codec->dapm.idle_bias_off && - codec->dapm.bias_level != SND_SOC_BIAS_OFF, + + WARN(dapm->idle_bias_off && + dapm->bias_level != SND_SOC_BIAS_OFF, "codec %s can not start from non-off bias with idle_bias_off==1\n", - codec->component.name); + component->name); } - if (driver->controls) - snd_soc_add_codec_controls(codec, driver->controls, - driver->num_controls); - if (driver->dapm_routes) - snd_soc_dapm_add_routes(&codec->dapm, driver->dapm_routes, - driver->num_dapm_routes); - - /* mark codec as probed and add to card codec list */ - codec->probed = 1; - list_add(&codec->card_list, &card->codec_dev_list); - list_add(&codec->dapm.list, &card->dapm_list); - - return 0; - -err_probe: - soc_cleanup_codec_debugfs(codec); - module_put(codec->dev->driver->owner); - - return ret; -} - -static int soc_probe_platform(struct snd_soc_card *card, - struct snd_soc_platform *platform) -{ - int ret = 0; - const struct snd_soc_platform_driver *driver = platform->driver; - struct snd_soc_component *component; - struct snd_soc_dai *dai; - - platform->component.card = card; - platform->component.dapm.card = card; + if (component->controls) + snd_soc_add_component_controls(component, component->controls, + component->num_controls); + if (component->dapm_routes) + snd_soc_dapm_add_routes(dapm, component->dapm_routes, + component->num_dapm_routes); - if (!try_module_get(platform->dev->driver->owner)) - return -ENODEV; - - soc_init_platform_debugfs(platform); + component->probed = 1; + list_add(&dapm->list, &card->dapm_list); - if (driver->dapm_widgets) - snd_soc_dapm_new_controls(&platform->component.dapm, - driver->dapm_widgets, driver->num_dapm_widgets); - - /* Create DAPM widgets for each DAI stream */ - list_for_each_entry(component, &component_list, list) { - if (component->dev != platform->dev) - continue; - list_for_each_entry(dai, &component->dai_list, list) - snd_soc_dapm_new_dai_widgets(&platform->component.dapm, - dai); - } - - platform->component.dapm.idle_bias_off = 1; - - if (driver->probe) { - ret = driver->probe(platform); - if (ret < 0) { - dev_err(platform->dev, - "ASoC: failed to probe platform %d\n", ret); - goto err_probe; - } - } - - if (driver->controls) - snd_soc_add_platform_controls(platform, driver->controls, - driver->num_controls); - if (driver->dapm_routes) - snd_soc_dapm_add_routes(&platform->component.dapm, - driver->dapm_routes, driver->num_dapm_routes); - - /* mark platform as probed and add to card platform list */ - platform->probed = 1; - list_add(&platform->component.dapm.list, &card->dapm_list); + /* This is a HACK and will be removed soon */ + if (component->codec) + list_add(&component->codec->card_list, &card->codec_dev_list); return 0; err_probe: - soc_cleanup_platform_debugfs(platform); - module_put(platform->dev->driver->owner); + soc_cleanup_component_debugfs(component); + module_put(component->dev->driver->owner); return ret; } @@ -1342,17 +1196,21 @@ static int soc_post_component_init(struct snd_soc_pcm_runtime *rtd, } rtd->dev_registered = 1; - /* add DAPM sysfs entries for this codec */ - ret = snd_soc_dapm_sys_add(rtd->dev); - if (ret < 0) - dev_err(rtd->dev, - "ASoC: failed to add codec dapm sysfs entries: %d\n", ret); + if (rtd->codec) { + /* add DAPM sysfs entries for this codec */ + ret = snd_soc_dapm_sys_add(rtd->dev); + if (ret < 0) + dev_err(rtd->dev, + "ASoC: failed to add codec dapm sysfs entries: %d\n", + ret); - /* add codec sysfs entries */ - ret = device_create_file(rtd->dev, &dev_attr_codec_reg); - if (ret < 0) - dev_err(rtd->dev, - "ASoC: failed to add codec sysfs files: %d\n", ret); + /* add codec sysfs entries */ + ret = device_create_file(rtd->dev, &dev_attr_codec_reg); + if (ret < 0) + dev_err(rtd->dev, + "ASoC: failed to add codec sysfs files: %d\n", + ret); + } return 0; } @@ -1361,33 +1219,31 @@ static int soc_probe_link_components(struct snd_soc_card *card, int num, int order) { struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; int i, ret; /* probe the CPU-side component, if it is a CODEC */ - if (cpu_dai->codec && - !cpu_dai->codec->probed && - cpu_dai->codec->driver->probe_order == order) { - ret = soc_probe_codec(card, cpu_dai->codec); + component = rtd->cpu_dai->component; + if (component->driver->probe_order == order) { + ret = soc_probe_component(card, component); if (ret < 0) return ret; } /* probe the CODEC-side components */ for (i = 0; i < rtd->num_codecs; i++) { - if (!rtd->codec_dais[i]->codec->probed && - rtd->codec_dais[i]->codec->driver->probe_order == order) { - ret = soc_probe_codec(card, rtd->codec_dais[i]->codec); + component = rtd->codec_dais[i]->component; + if (component->driver->probe_order == order) { + ret = soc_probe_component(card, component); if (ret < 0) return ret; } } /* probe the platform */ - if (!platform->probed && - platform->driver->probe_order == order) { - ret = soc_probe_platform(card, platform); + if (platform->component.driver->probe_order == order) { + ret = soc_probe_component(card, &platform->component); if (ret < 0) return ret; } @@ -1482,18 +1338,12 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) /* probe the cpu_dai */ if (!cpu_dai->probed && cpu_dai->driver->probe_order == order) { - if (!cpu_dai->codec) { - if (!try_module_get(cpu_dai->dev->driver->owner)) - return -ENODEV; - } - if (cpu_dai->driver->probe) { ret = cpu_dai->driver->probe(cpu_dai); if (ret < 0) { dev_err(cpu_dai->dev, "ASoC: failed to probe CPU DAI %s: %d\n", cpu_dai->name, ret); - module_put(cpu_dai->dev->driver->owner); return ret; } } @@ -1654,17 +1504,24 @@ static int soc_bind_aux_dev(struct snd_soc_card *card, int num) { struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num]; struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num]; - const char *codecname = aux_dev->codec_name; + const char *name = aux_dev->codec_name; - rtd->codec = soc_find_codec(aux_dev->codec_of_node, codecname); - if (!rtd->codec) { + rtd->component = soc_find_component(aux_dev->codec_of_node, name); + if (!rtd->component) { if (aux_dev->codec_of_node) - codecname = of_node_full_name(aux_dev->codec_of_node); + name = of_node_full_name(aux_dev->codec_of_node); - dev_err(card->dev, "ASoC: %s not registered\n", codecname); + dev_err(card->dev, "ASoC: %s not registered\n", name); return -EPROBE_DEFER; } + /* + * Some places still reference rtd->codec, so we have to keep that + * initialized if the component is a CODEC. Once all those references + * have been removed, this code can be removed as well. + */ + rtd->codec = rtd->component->codec; + return 0; } @@ -1674,18 +1531,13 @@ static int soc_probe_aux_dev(struct snd_soc_card *card, int num) struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num]; int ret; - if (rtd->codec->probed) { - dev_err(rtd->codec->dev, "ASoC: codec already probed\n"); - return -EBUSY; - } - - ret = soc_probe_codec(card, rtd->codec); + ret = soc_probe_component(card, rtd->component); if (ret < 0) return ret; /* do machine specific initialization */ if (aux_dev->init) { - ret = aux_dev->init(&rtd->codec->dapm); + ret = aux_dev->init(rtd->component); if (ret < 0) { dev_err(card->dev, "ASoC: failed to init %s: %d\n", aux_dev->name, ret); @@ -1699,7 +1551,7 @@ static int soc_probe_aux_dev(struct snd_soc_card *card, int num) static void soc_remove_aux_dev(struct snd_soc_card *card, int num) { struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num]; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_component *component = rtd->component; /* unregister the rtd device */ if (rtd->dev_registered) { @@ -1708,8 +1560,8 @@ static void soc_remove_aux_dev(struct snd_soc_card *card, int num) rtd->dev_registered = 0; } - if (codec && codec->probed) - soc_remove_codec(codec); + if (component && component->probed) + soc_remove_component(component); } static int snd_soc_init_codec_cache(struct snd_soc_codec *codec) @@ -2107,19 +1959,14 @@ static struct platform_driver soc_driver = { int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, struct snd_ac97_bus_ops *ops, int num) { - mutex_lock(&codec->mutex); - codec->ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL); - if (codec->ac97 == NULL) { - mutex_unlock(&codec->mutex); + if (codec->ac97 == NULL) return -ENOMEM; - } codec->ac97->bus = kzalloc(sizeof(struct snd_ac97_bus), GFP_KERNEL); if (codec->ac97->bus == NULL) { kfree(codec->ac97); codec->ac97 = NULL; - mutex_unlock(&codec->mutex); return -ENOMEM; } @@ -2132,7 +1979,6 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, */ codec->ac97_created = 1; - mutex_unlock(&codec->mutex); return 0; } EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec); @@ -2302,7 +2148,6 @@ EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops_of_reset); */ void snd_soc_free_ac97_codec(struct snd_soc_codec *codec) { - mutex_lock(&codec->mutex); #ifdef CONFIG_SND_SOC_AC97_BUS soc_unregister_ac97_codec(codec); #endif @@ -2310,7 +2155,6 @@ void snd_soc_free_ac97_codec(struct snd_soc_codec *codec) kfree(codec->ac97); codec->ac97 = NULL; codec->ac97_created = 0; - mutex_unlock(&codec->mutex); } EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec); @@ -3027,9 +2871,10 @@ int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol, unsigned int val, val_mask; int ret; - val = ((ucontrol->value.integer.value[0] + min) & mask); if (invert) - val = max - val; + val = (max - ucontrol->value.integer.value[0]) & mask; + else + val = ((ucontrol->value.integer.value[0] + min) & mask); val_mask = mask << shift; val = val << shift; @@ -3038,9 +2883,10 @@ int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol, return ret; if (snd_soc_volsw_is_stereo(mc)) { - val = ((ucontrol->value.integer.value[1] + min) & mask); if (invert) - val = max - val; + val = (max - ucontrol->value.integer.value[1]) & mask; + else + val = ((ucontrol->value.integer.value[1] + min) & mask); val_mask = mask << shift; val = val << shift; @@ -3085,8 +2931,9 @@ int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol, if (invert) ucontrol->value.integer.value[0] = max - ucontrol->value.integer.value[0]; - ucontrol->value.integer.value[0] = - ucontrol->value.integer.value[0] - min; + else + ucontrol->value.integer.value[0] = + ucontrol->value.integer.value[0] - min; if (snd_soc_volsw_is_stereo(mc)) { ret = snd_soc_component_read(component, rreg, &val); @@ -3097,8 +2944,9 @@ int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol, if (invert) ucontrol->value.integer.value[1] = max - ucontrol->value.integer.value[1]; - ucontrol->value.integer.value[1] = - ucontrol->value.integer.value[1] - min; + else + ucontrol->value.integer.value[1] = + ucontrol->value.integer.value[1] - min; } return 0; @@ -3928,8 +3776,11 @@ EXPORT_SYMBOL_GPL(snd_soc_register_card); */ int snd_soc_unregister_card(struct snd_soc_card *card) { - if (card->instantiated) + if (card->instantiated) { + card->instantiated = false; + snd_soc_dapm_shutdown(card); soc_cleanup_card_resources(card); + } dev_dbg(card->dev, "ASoC: Unregistered card '%s'\n", card->name); return 0; @@ -4116,6 +3967,8 @@ static int snd_soc_component_initialize(struct snd_soc_component *component, component->dev = dev; component->driver = driver; + component->probe = component->driver->probe; + component->remove = component->driver->remove; if (!component->dapm_ptr) component->dapm_ptr = &component->dapm; @@ -4124,19 +3977,42 @@ static int snd_soc_component_initialize(struct snd_soc_component *component, dapm->dev = dev; dapm->component = component; dapm->bias_level = SND_SOC_BIAS_OFF; + dapm->idle_bias_off = true; if (driver->seq_notifier) dapm->seq_notifier = snd_soc_component_seq_notifier; if (driver->stream_event) dapm->stream_event = snd_soc_component_stream_event; + component->controls = driver->controls; + component->num_controls = driver->num_controls; + component->dapm_widgets = driver->dapm_widgets; + component->num_dapm_widgets = driver->num_dapm_widgets; + component->dapm_routes = driver->dapm_routes; + component->num_dapm_routes = driver->num_dapm_routes; + INIT_LIST_HEAD(&component->dai_list); mutex_init(&component->io_mutex); return 0; } +static void snd_soc_component_init_regmap(struct snd_soc_component *component) +{ + if (!component->regmap) + component->regmap = dev_get_regmap(component->dev, NULL); + if (component->regmap) { + int val_bytes = regmap_get_val_bytes(component->regmap); + /* Errors are legitimate for non-integer byte multiples */ + if (val_bytes > 0) + component->val_bytes = val_bytes; + } +} + static void snd_soc_component_add_unlocked(struct snd_soc_component *component) { + if (!component->write && !component->read) + snd_soc_component_init_regmap(component); + list_add(&component->list, &component_list); } @@ -4225,22 +4101,18 @@ found: } EXPORT_SYMBOL_GPL(snd_soc_unregister_component); -static int snd_soc_platform_drv_write(struct snd_soc_component *component, - unsigned int reg, unsigned int val) +static int snd_soc_platform_drv_probe(struct snd_soc_component *component) { struct snd_soc_platform *platform = snd_soc_component_to_platform(component); - return platform->driver->write(platform, reg, val); + return platform->driver->probe(platform); } -static int snd_soc_platform_drv_read(struct snd_soc_component *component, - unsigned int reg, unsigned int *val) +static void snd_soc_platform_drv_remove(struct snd_soc_component *component) { struct snd_soc_platform *platform = snd_soc_component_to_platform(component); - *val = platform->driver->read(platform, reg); - - return 0; + platform->driver->remove(platform); } /** @@ -4261,10 +4133,15 @@ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform, platform->dev = dev; platform->driver = platform_drv; - if (platform_drv->write) - platform->component.write = snd_soc_platform_drv_write; - if (platform_drv->read) - platform->component.read = snd_soc_platform_drv_read; + + if (platform_drv->probe) + platform->component.probe = snd_soc_platform_drv_probe; + if (platform_drv->remove) + platform->component.remove = snd_soc_platform_drv_remove; + +#ifdef CONFIG_DEBUG_FS + platform->component.debugfs_prefix = "platform"; +#endif mutex_lock(&client_mutex); snd_soc_component_add_unlocked(&platform->component); @@ -4315,10 +4192,10 @@ void snd_soc_remove_platform(struct snd_soc_platform *platform) snd_soc_component_del_unlocked(&platform->component); mutex_unlock(&client_mutex); - snd_soc_component_cleanup(&platform->component); - dev_dbg(platform->dev, "ASoC: Unregistered platform '%s'\n", platform->component.name); + + snd_soc_component_cleanup(&platform->component); } EXPORT_SYMBOL_GPL(snd_soc_remove_platform); @@ -4386,6 +4263,20 @@ static void fixup_codec_formats(struct snd_soc_pcm_stream *stream) stream->formats |= codec_format_map[i]; } +static int snd_soc_codec_drv_probe(struct snd_soc_component *component) +{ + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + + return codec->driver->probe(codec); +} + +static void snd_soc_codec_drv_remove(struct snd_soc_component *component) +{ + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + + codec->driver->remove(codec); +} + static int snd_soc_codec_drv_write(struct snd_soc_component *component, unsigned int reg, unsigned int val) { @@ -4424,7 +4315,6 @@ int snd_soc_register_codec(struct device *dev, { struct snd_soc_codec *codec; struct snd_soc_dai *dai; - struct regmap *regmap; int ret, i; dev_dbg(dev, "codec register %s\n", dev_name(dev)); @@ -4434,18 +4324,37 @@ int snd_soc_register_codec(struct device *dev, return -ENOMEM; codec->component.dapm_ptr = &codec->dapm; + codec->component.codec = codec; ret = snd_soc_component_initialize(&codec->component, &codec_drv->component_driver, dev); if (ret) goto err_free; + if (codec_drv->controls) { + codec->component.controls = codec_drv->controls; + codec->component.num_controls = codec_drv->num_controls; + } + if (codec_drv->dapm_widgets) { + codec->component.dapm_widgets = codec_drv->dapm_widgets; + codec->component.num_dapm_widgets = codec_drv->num_dapm_widgets; + } + if (codec_drv->dapm_routes) { + codec->component.dapm_routes = codec_drv->dapm_routes; + codec->component.num_dapm_routes = codec_drv->num_dapm_routes; + } + + if (codec_drv->probe) + codec->component.probe = snd_soc_codec_drv_probe; + if (codec_drv->remove) + codec->component.remove = snd_soc_codec_drv_remove; if (codec_drv->write) codec->component.write = snd_soc_codec_drv_write; if (codec_drv->read) codec->component.read = snd_soc_codec_drv_read; codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time; - codec->dapm.codec = codec; + codec->dapm.idle_bias_off = codec_drv->idle_bias_off; + codec->dapm.suspend_bias_off = codec_drv->suspend_bias_off; if (codec_drv->seq_notifier) codec->dapm.seq_notifier = codec_drv->seq_notifier; if (codec_drv->set_bias_level) @@ -4455,23 +4364,13 @@ int snd_soc_register_codec(struct device *dev, codec->component.val_bytes = codec_drv->reg_word_size; mutex_init(&codec->mutex); - if (!codec->component.write) { - if (codec_drv->get_regmap) - regmap = codec_drv->get_regmap(dev); - else - regmap = dev_get_regmap(dev, NULL); - - if (regmap) { - ret = snd_soc_component_init_io(&codec->component, - regmap); - if (ret) { - dev_err(codec->dev, - "Failed to set cache I/O:%d\n", - ret); - goto err_cleanup; - } - } - } +#ifdef CONFIG_DEBUG_FS + codec->component.init_debugfs = soc_init_codec_debugfs; + codec->component.debugfs_prefix = "codec"; +#endif + + if (codec_drv->get_regmap) + codec->component.regmap = codec_drv->get_regmap(dev); for (i = 0; i < num_dai; i++) { fixup_codec_formats(&dai_drv[i].playback); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 177bd86..c61cb9c 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -326,12 +326,13 @@ static struct list_head *dapm_kcontrol_get_path_list( list_for_each_entry(path, dapm_kcontrol_get_path_list(kcontrol), \ list_kcontrol) -static unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol) +unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol) { struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); return data->value; } +EXPORT_SYMBOL_GPL(dapm_kcontrol_get_value); static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol, unsigned int value) @@ -591,9 +592,9 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, int shared; struct snd_kcontrol *kcontrol; bool wname_in_long_name, kcname_in_long_name; - char *long_name; + char *long_name = NULL; const char *name; - int ret; + int ret = 0; prefix = soc_dapm_prefix(dapm); if (prefix) @@ -652,15 +653,17 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, kcontrol = snd_soc_cnew(&w->kcontrol_news[kci], NULL, name, prefix); - kfree(long_name); - if (!kcontrol) - return -ENOMEM; + if (!kcontrol) { + ret = -ENOMEM; + goto exit_free; + } + kcontrol->private_free = dapm_kcontrol_free; ret = dapm_kcontrol_data_alloc(w, kcontrol); if (ret) { snd_ctl_free_one(kcontrol); - return ret; + goto exit_free; } ret = snd_ctl_add(card, kcontrol); @@ -668,17 +671,18 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, dev_err(dapm->dev, "ASoC: failed to add widget %s dapm kcontrol %s: %d\n", w->name, name, ret); - return ret; + goto exit_free; } } ret = dapm_kcontrol_add_widget(kcontrol, w); - if (ret) - return ret; + if (ret == 0) + w->kcontrols[kci] = kcontrol; - w->kcontrols[kci] = kcontrol; +exit_free: + kfree(long_name); - return 0; + return ret; } /* create new dapm mixer control */ @@ -1683,6 +1687,22 @@ static void dapm_power_one_widget(struct snd_soc_dapm_widget *w, } } +static bool dapm_idle_bias_off(struct snd_soc_dapm_context *dapm) +{ + if (dapm->idle_bias_off) + return true; + + switch (snd_power_get_state(dapm->card->snd_card)) { + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + return dapm->suspend_bias_off; + default: + break; + } + + return false; +} + /* * Scan each dapm widget for complete audio path. * A complete path is a route that has valid endpoints i.e.:- @@ -1706,7 +1726,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) trace_snd_soc_dapm_start(card); list_for_each_entry(d, &card->dapm_list, list) { - if (d->idle_bias_off) + if (dapm_idle_bias_off(d)) d->target_bias_level = SND_SOC_BIAS_OFF; else d->target_bias_level = SND_SOC_BIAS_STANDBY; @@ -1772,7 +1792,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) if (d->target_bias_level > bias) bias = d->target_bias_level; list_for_each_entry(d, &card->dapm_list, list) - if (!d->idle_bias_off) + if (!dapm_idle_bias_off(d)) d->target_bias_level = bias; trace_snd_soc_dapm_walk_done(card); @@ -3109,7 +3129,8 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, } w->dapm = dapm; - w->codec = dapm->codec; + if (dapm->component) + w->codec = dapm->component->codec; INIT_LIST_HEAD(&w->sources); INIT_LIST_HEAD(&w->sinks); INIT_LIST_HEAD(&w->list); diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index 6307f85..b329b84 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -336,10 +336,12 @@ static const struct snd_pcm_ops dmaengine_pcm_ops = { }; static const struct snd_soc_platform_driver dmaengine_pcm_platform = { + .component_driver = { + .probe_order = SND_SOC_COMP_ORDER_LATE, + }, .ops = &dmaengine_pcm_ops, .pcm_new = dmaengine_pcm_new, .pcm_free = dmaengine_pcm_free, - .probe_order = SND_SOC_COMP_ORDER_LATE, }; static const char * const dmaengine_pcm_dma_channel_names[] = { diff --git a/sound/soc/soc-io.c b/sound/soc/soc-io.c index 7767fbd..9b39390 100644 --- a/sound/soc/soc-io.c +++ b/sound/soc/soc-io.c @@ -271,31 +271,3 @@ int snd_soc_platform_write(struct snd_soc_platform *platform, return snd_soc_component_write(&platform->component, reg, val); } EXPORT_SYMBOL_GPL(snd_soc_platform_write); - -/** - * snd_soc_component_init_io() - Initialize regmap IO - * - * @component: component to initialize - * @regmap: regmap instance to use for IO operations - * - * Return: 0 on success, a negative error code otherwise - */ -int snd_soc_component_init_io(struct snd_soc_component *component, - struct regmap *regmap) -{ - int ret; - - if (!regmap) - return -EINVAL; - - ret = regmap_get_val_bytes(regmap); - /* Errors are legitimate for non-integer byte - * multiples */ - if (ret > 0) - component->val_bytes = ret; - - component->regmap = regmap; - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_component_init_io); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 642c862..002311a 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -352,7 +352,7 @@ static void soc_pcm_apply_msb(struct snd_pcm_substream *substream) } else { for (i = 0; i < rtd->num_codecs; i++) { codec_dai = rtd->codec_dais[i]; - if (codec_dai->driver->playback.sig_bits == 0) { + if (codec_dai->driver->capture.sig_bits == 0) { bits = 0; break; } diff --git a/sound/soc/tegra/tegra_max98090.c b/sound/soc/tegra/tegra_max98090.c index b86cd99..01921d7 100644 --- a/sound/soc/tegra/tegra_max98090.c +++ b/sound/soc/tegra/tegra_max98090.c @@ -42,6 +42,7 @@ struct tegra_max98090 { struct tegra_asoc_utils_data util_data; int gpio_hp_det; + int gpio_mic_det; }; static int tegra_max98090_asoc_hw_params(struct snd_pcm_substream *substream, @@ -112,6 +113,22 @@ static struct snd_soc_jack_gpio tegra_max98090_hp_jack_gpio = { .invert = 1, }; +static struct snd_soc_jack tegra_max98090_mic_jack; + +static struct snd_soc_jack_pin tegra_max98090_mic_jack_pins[] = { + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static struct snd_soc_jack_gpio tegra_max98090_mic_jack_gpio = { + .name = "Mic detection", + .report = SND_JACK_MICROPHONE, + .debounce_time = 150, + .invert = 1, +}; + static const struct snd_soc_dapm_widget tegra_max98090_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphones", NULL), SND_SOC_DAPM_SPK("Speakers", NULL), @@ -141,6 +158,19 @@ static int tegra_max98090_asoc_init(struct snd_soc_pcm_runtime *rtd) &tegra_max98090_hp_jack_gpio); } + if (gpio_is_valid(machine->gpio_mic_det)) { + snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE, + &tegra_max98090_mic_jack); + snd_soc_jack_add_pins(&tegra_max98090_mic_jack, + ARRAY_SIZE(tegra_max98090_mic_jack_pins), + tegra_max98090_mic_jack_pins); + + tegra_max98090_mic_jack_gpio.gpio = machine->gpio_mic_det; + snd_soc_jack_add_gpios(&tegra_max98090_mic_jack, + 1, + &tegra_max98090_mic_jack_gpio); + } + return 0; } @@ -153,6 +183,11 @@ static int tegra_max98090_card_remove(struct snd_soc_card *card) &tegra_max98090_hp_jack_gpio); } + if (gpio_is_valid(machine->gpio_mic_det)) { + snd_soc_jack_free_gpios(&tegra_max98090_mic_jack, 1, + &tegra_max98090_mic_jack_gpio); + } + return 0; } @@ -201,6 +236,11 @@ static int tegra_max98090_probe(struct platform_device *pdev) if (machine->gpio_hp_det == -EPROBE_DEFER) return -EPROBE_DEFER; + machine->gpio_mic_det = + of_get_named_gpio(np, "nvidia,mic-det-gpios", 0); + if (machine->gpio_mic_det == -EPROBE_DEFER) + return -EPROBE_DEFER; + ret = snd_soc_of_parse_card_name(card, "nvidia,model"); if (ret) goto err; diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c index f0829de..cd71fd8 100644 --- a/sound/soc/txx9/txx9aclc.c +++ b/sound/soc/txx9/txx9aclc.c @@ -16,6 +16,7 @@ #include <linux/platform_device.h> #include <linux/scatterlist.h> #include <linux/slab.h> +#include <linux/dmaengine.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -137,7 +138,7 @@ txx9aclc_dma_submit(struct txx9aclc_dmadata *dmadata, dma_addr_t buf_dma_addr) } desc->callback = txx9aclc_dma_complete; desc->callback_param = dmadata; - desc->tx_submit(desc); + dmaengine_submit(desc); return desc; } @@ -160,7 +161,7 @@ static void txx9aclc_dma_tasklet(unsigned long data) void __iomem *base = drvdata->base; spin_unlock_irqrestore(&dmadata->dma_lock, flags); - chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); + dmaengine_terminate_all(chan); /* first time */ for (i = 0; i < NR_DMA_CHAIN; i++) { desc = txx9aclc_dma_submit(dmadata, @@ -169,7 +170,7 @@ static void txx9aclc_dma_tasklet(unsigned long data) return; } dmadata->dmacount = NR_DMA_CHAIN; - chan->device->device_issue_pending(chan); + dma_async_issue_pending(chan); spin_lock_irqsave(&dmadata->dma_lock, flags); __raw_writel(ctlbit, base + ACCTLEN); dmadata->frag_count = NR_DMA_CHAIN % dmadata->frags; @@ -188,7 +189,7 @@ static void txx9aclc_dma_tasklet(unsigned long data) dmadata->frag_count * dmadata->frag_bytes); if (!desc) return; - chan->device->device_issue_pending(chan); + dma_async_issue_pending(chan); spin_lock_irqsave(&dmadata->dma_lock, flags); dmadata->frag_count++; @@ -266,7 +267,7 @@ static int txx9aclc_pcm_close(struct snd_pcm_substream *substream) struct dma_chan *chan = dmadata->dma_chan; dmadata->frag_count = -1; - chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); + dmaengine_terminate_all(chan); return 0; } @@ -398,8 +399,7 @@ static int txx9aclc_pcm_remove(struct snd_soc_platform *platform) struct dma_chan *chan = dmadata->dma_chan; if (chan) { dmadata->frag_count = -1; - chan->device->device_control(chan, - DMA_TERMINATE_ALL, 0); + dmaengine_terminate_all(chan); dma_release_channel(chan); } dev->dmadata[i].dma_chan = NULL; diff --git a/sound/usb/caiaq/audio.c b/sound/usb/caiaq/audio.c index 7103b09..2728447 100644 --- a/sound/usb/caiaq/audio.c +++ b/sound/usb/caiaq/audio.c @@ -816,6 +816,11 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *cdev) return -EINVAL; } + if (cdev->n_streams < 2) { + dev_err(dev, "bogus number of streams: %d\n", cdev->n_streams); + return -EINVAL; + } + ret = snd_pcm_new(cdev->chip.card, cdev->product_name, 0, cdev->n_audio_out, cdev->n_audio_in, &cdev->pcm); diff --git a/sound/usb/midi.c b/sound/usb/midi.c index b2b6f398..d3d4952 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -1506,6 +1506,12 @@ static struct port_info { PORT_INFO(vendor, product, num, name, 0, \ SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | \ SNDRV_SEQ_PORT_TYPE_HARDWARE) +#define GM_SYNTH_PORT(vendor, product, num, name, voices) \ + PORT_INFO(vendor, product, num, name, voices, \ + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | \ + SNDRV_SEQ_PORT_TYPE_MIDI_GM | \ + SNDRV_SEQ_PORT_TYPE_HARDWARE | \ + SNDRV_SEQ_PORT_TYPE_SYNTHESIZER) #define ROLAND_SYNTH_PORT(vendor, product, num, name, voices) \ PORT_INFO(vendor, product, num, name, voices, \ SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | \ @@ -1525,6 +1531,11 @@ static struct port_info { SNDRV_SEQ_PORT_TYPE_MIDI_MT32 | \ SNDRV_SEQ_PORT_TYPE_HARDWARE | \ SNDRV_SEQ_PORT_TYPE_SYNTHESIZER) + /* Yamaha MOTIF XF */ + GM_SYNTH_PORT(0x0499, 0x105c, 0, "%s Tone Generator", 128), + CONTROL_PORT(0x0499, 0x105c, 1, "%s Remote Control"), + EXTERNAL_PORT(0x0499, 0x105c, 2, "%s Thru"), + CONTROL_PORT(0x0499, 0x105c, 3, "%s Editor"), /* Roland UA-100 */ CONTROL_PORT(0x0582, 0x0000, 2, "%s Control"), /* Roland SC-8850 */ diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 19a921e..d2aa45a 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1174,5 +1174,21 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, } } + /* XMOS based USB DACs */ + switch (chip->usb_id) { + /* iFi Audio micro/nano iDSD */ + case USB_ID(0x20b1, 0x3008): + if (fp->altsetting == 2) + return SNDRV_PCM_FMTBIT_DSD_U32_LE; + break; + /* DIYINHK DSD DXD 384kHz USB to I2S/DSD */ + case USB_ID(0x20b1, 0x2009): + if (fp->altsetting == 3) + return SNDRV_PCM_FMTBIT_DSD_U32_LE; + break; + default: + break; + } + return 0; } |