diff options
Diffstat (limited to 'sound')
523 files changed, 36039 insertions, 7201 deletions
diff --git a/sound/aoa/core/gpio-feature.c b/sound/aoa/core/gpio-feature.c index f34153962d07..71960089e207 100644 --- a/sound/aoa/core/gpio-feature.c +++ b/sound/aoa/core/gpio-feature.c @@ -118,7 +118,7 @@ static void get_irq(struct device_node * np, int *irqptr) if (np) *irqptr = irq_of_parse_and_map(np, 0); else - *irqptr = NO_IRQ; + *irqptr = 0; } /* 0x4 is outenable, 0x1 is out, thus 4 or 5 */ @@ -336,7 +336,7 @@ static int ftr_set_notify(struct gpio_runtime *rt, return -EINVAL; } - if (irq == NO_IRQ) + if (!irq) return -ENODEV; mutex_lock(¬if->mutex); diff --git a/sound/aoa/fabrics/layout.c b/sound/aoa/fabrics/layout.c index 8f71f7e4d966..a0c4a5de809c 100644 --- a/sound/aoa/fabrics/layout.c +++ b/sound/aoa/fabrics/layout.c @@ -112,6 +112,7 @@ MODULE_ALIAS("sound-layout-100"); MODULE_ALIAS("aoa-device-id-14"); MODULE_ALIAS("aoa-device-id-22"); +MODULE_ALIAS("aoa-device-id-31"); MODULE_ALIAS("aoa-device-id-35"); MODULE_ALIAS("aoa-device-id-44"); @@ -362,6 +363,13 @@ static struct layout layouts[] = { .connections = tas_connections_nolineout, }, }, + /* PowerBook6,1 */ + { .device_id = 31, + .codecs[0] = { + .name = "tas", + .connections = tas_connections_nolineout, + }, + }, /* PowerBook6,5 */ { .device_id = 44, .codecs[0] = { @@ -1161,12 +1169,7 @@ static struct soundbus_driver aoa_soundbus_driver = { static int __init aoa_fabric_layout_init(void) { - int err; - - err = soundbus_register_driver(&aoa_soundbus_driver); - if (err) - return err; - return 0; + return soundbus_register_driver(&aoa_soundbus_driver); } static void __exit aoa_fabric_layout_exit(void) diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c index 1cbf210080a1..000b58522106 100644 --- a/sound/aoa/soundbus/i2sbus/core.c +++ b/sound/aoa/soundbus/i2sbus/core.c @@ -197,7 +197,7 @@ static int i2sbus_add_dev(struct macio_dev *macio, * so restrict to those we do handle for now. */ if (id && (*id == 22 || *id == 14 || *id == 35 || - *id == 44)) { + *id == 31 || *id == 44)) { snprintf(dev->sound.modalias, 32, "aoa-device-id-%d", *id); ok = 1; diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 2c498488af6c..fec1dfdb14ad 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -553,13 +553,9 @@ snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg) * we should allow parameter change only when stream has been * opened not in other cases */ - params = kmalloc(sizeof(*params), GFP_KERNEL); - if (!params) - return -ENOMEM; - if (copy_from_user(params, (void __user *)arg, sizeof(*params))) { - retval = -EFAULT; - goto out; - } + params = memdup_user((void __user *)arg, sizeof(*params)); + if (IS_ERR(params)) + return PTR_ERR(params); retval = snd_compress_check_input(params); if (retval) @@ -784,7 +780,7 @@ static int snd_compress_wait_for_drain(struct snd_compr_stream *stream) ret = wait_event_interruptible(stream->runtime->sleep, (stream->runtime->state != SNDRV_PCM_STATE_DRAINING)); if (ret == -ERESTARTSYS) - pr_debug("wait aborted by a signal"); + pr_debug("wait aborted by a signal\n"); else if (ret) pr_debug("wait for drain failed with %d\n", ret); @@ -966,7 +962,7 @@ static int snd_compress_dev_register(struct snd_device *device) compr->card, compr->device, &snd_compr_file_ops, compr, &compr->dev); if (ret < 0) { - pr_err("snd_register_device failed\n %d", ret); + pr_err("snd_register_device failed %d\n", ret); return ret; } return ret; diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c index e2f27022b363..1ac0c423903e 100644 --- a/sound/core/hrtimer.c +++ b/sound/core/hrtimer.c @@ -58,7 +58,7 @@ static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt) /* calculate the drift */ delta = ktime_sub(hrt->base->get_time(), hrtimer_get_expires(hrt)); - if (delta.tv64 > 0) + if (delta > 0) ticks += ktime_divns(delta, ticks * resolution); snd_timer_interrupt(stime->timer, ticks); diff --git a/sound/core/info.c b/sound/core/info.c index 895362a696c9..8ab72e0f5932 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -325,10 +325,15 @@ static ssize_t snd_info_text_entry_write(struct file *file, size_t next; int err = 0; + if (!entry->c.text.write) + return -EIO; pos = *offset; if (!valid_pos(pos, count)) return -EIO; next = pos + count; + /* don't handle too large text inputs */ + if (next > 16 * 1024) + return -EIO; mutex_lock(&entry->access); buf = data->wbuffer; if (!buf) { @@ -366,7 +371,9 @@ static int snd_info_seq_show(struct seq_file *seq, void *p) struct snd_info_private_data *data = seq->private; struct snd_info_entry *entry = data->entry; - if (entry->c.text.read) { + if (!entry->c.text.read) { + return -EIO; + } else { data->rbuffer->buffer = (char *)seq; /* XXX hack! */ entry->c.text.read(entry, data->rbuffer); } diff --git a/sound/core/misc.c b/sound/core/misc.c index f2e8226c88fb..21b228046e88 100644 --- a/sound/core/misc.c +++ b/sound/core/misc.c @@ -71,6 +71,7 @@ void __snd_printk(unsigned int level, const char *path, int line, int kern_level; struct va_format vaf; char verbose_fmt[] = KERN_DEFAULT "ALSA %s:%d %pV"; + bool level_found = false; #endif #ifdef CONFIG_SND_DEBUG @@ -83,15 +84,22 @@ void __snd_printk(unsigned int level, const char *path, int line, vaf.fmt = format; vaf.va = &args; - kern_level = printk_get_level(format); - if (kern_level) { - const char *end_of_header = printk_skip_level(format); - memcpy(verbose_fmt, format, end_of_header - format); + while ((kern_level = printk_get_level(vaf.fmt)) != 0) { + const char *end_of_header = printk_skip_level(vaf.fmt); + + /* Ignore KERN_CONT. We print filename:line for each piece. */ + if (kern_level >= '0' && kern_level <= '7') { + memcpy(verbose_fmt, vaf.fmt, end_of_header - vaf.fmt); + level_found = true; + } + vaf.fmt = end_of_header; - } else if (level) + } + + if (!level_found && level) memcpy(verbose_fmt, KERN_DEBUG, sizeof(KERN_DEBUG) - 1); - printk(verbose_fmt, sanity_file_name(path), line, &vaf); + printk(verbose_fmt, sanity_file_name(path), line, &vaf); #else vprintk(format, args); #endif diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index ebc9fdfe64df..698a01419515 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -2501,7 +2501,7 @@ static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long return put_user(SNDRV_OSS_VERSION, p); if (cmd == OSS_ALSAEMULVER) return put_user(1, p); -#if defined(CONFIG_SND_MIXER_OSS) || (defined(MODULE) && defined(CONFIG_SND_MIXER_OSS_MODULE)) +#if IS_REACHABLE(CONFIG_SND_MIXER_OSS) if (((cmd >> 8) & 0xff) == 'M') { /* mixer ioctl - for OSS compatibility */ struct snd_pcm_substream *substream; int idx; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index c61fd50f771f..9d33c1e85c79 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -2637,9 +2637,11 @@ static int snd_pcm_hwsync(struct snd_pcm_substream *substream) break; /* Fall through */ case SNDRV_PCM_STATE_PREPARED: - case SNDRV_PCM_STATE_SUSPENDED: err = 0; break; + case SNDRV_PCM_STATE_SUSPENDED: + err = -ESTRPIPE; + break; case SNDRV_PCM_STATE_XRUN: err = -EPIPE; break; diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index b450a27588c8..2096bb0835c8 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -1610,7 +1610,7 @@ static int snd_rawmidi_dev_free(struct snd_device *device) return snd_rawmidi_free(rmidi); } -#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) +#if IS_REACHABLE(CONFIG_SND_SEQUENCER) static void snd_rawmidi_dev_seq_free(struct snd_seq_device *device) { struct snd_rawmidi *rmidi = device->private_data; @@ -1691,7 +1691,7 @@ static int snd_rawmidi_dev_register(struct snd_device *device) } } rmidi->proc_entry = entry; -#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) +#if IS_REACHABLE(CONFIG_SND_SEQUENCER) if (!rmidi->ops || !rmidi->ops->dev_register) { /* own registration mechanism */ if (snd_seq_device_new(rmidi->card, rmidi->device, SNDRV_SEQ_DEV_ID_MIDISYNTH, 0, &rmidi->seq_dev) >= 0) { rmidi->seq_dev->private_data = rmidi; diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index d6d9419d8bac..4c935202ce23 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -87,21 +87,6 @@ static int snd_seq_deliver_single_event(struct snd_seq_client *client, /* */ - -static inline mm_segment_t snd_enter_user(void) -{ - mm_segment_t fs = get_fs(); - set_fs(get_ds()); - return fs; -} - -static inline void snd_leave_user(mm_segment_t fs) -{ - set_fs(fs); -} - -/* - */ static inline unsigned short snd_seq_file_flags(struct file *file) { switch (file->f_mode & (FMODE_READ | FMODE_WRITE)) { @@ -1128,59 +1113,69 @@ static unsigned int snd_seq_poll(struct file *file, poll_table * wait) /*-----------------------------------------------------*/ +static int snd_seq_ioctl_pversion(struct snd_seq_client *client, void *arg) +{ + int *pversion = arg; + + *pversion = SNDRV_SEQ_VERSION; + return 0; +} + +static int snd_seq_ioctl_client_id(struct snd_seq_client *client, void *arg) +{ + int *client_id = arg; + + *client_id = client->number; + return 0; +} /* SYSTEM_INFO ioctl() */ -static int snd_seq_ioctl_system_info(struct snd_seq_client *client, void __user *arg) +static int snd_seq_ioctl_system_info(struct snd_seq_client *client, void *arg) { - struct snd_seq_system_info info; + struct snd_seq_system_info *info = arg; - memset(&info, 0, sizeof(info)); + memset(info, 0, sizeof(*info)); /* fill the info fields */ - info.queues = SNDRV_SEQ_MAX_QUEUES; - info.clients = SNDRV_SEQ_MAX_CLIENTS; - info.ports = SNDRV_SEQ_MAX_PORTS; - info.channels = 256; /* fixed limit */ - info.cur_clients = client_usage.cur; - info.cur_queues = snd_seq_queue_get_cur_queues(); - - if (copy_to_user(arg, &info, sizeof(info))) - return -EFAULT; + info->queues = SNDRV_SEQ_MAX_QUEUES; + info->clients = SNDRV_SEQ_MAX_CLIENTS; + info->ports = SNDRV_SEQ_MAX_PORTS; + info->channels = 256; /* fixed limit */ + info->cur_clients = client_usage.cur; + info->cur_queues = snd_seq_queue_get_cur_queues(); + return 0; } /* RUNNING_MODE ioctl() */ -static int snd_seq_ioctl_running_mode(struct snd_seq_client *client, void __user *arg) +static int snd_seq_ioctl_running_mode(struct snd_seq_client *client, void *arg) { - struct snd_seq_running_info info; + struct snd_seq_running_info *info = arg; struct snd_seq_client *cptr; int err = 0; - if (copy_from_user(&info, arg, sizeof(info))) - return -EFAULT; - /* requested client number */ - cptr = snd_seq_client_use_ptr(info.client); + cptr = snd_seq_client_use_ptr(info->client); if (cptr == NULL) return -ENOENT; /* don't change !!! */ #ifdef SNDRV_BIG_ENDIAN - if (! info.big_endian) { + if (!info->big_endian) { err = -EINVAL; goto __err; } #else - if (info.big_endian) { + if (info->big_endian) { err = -EINVAL; goto __err; } #endif - if (info.cpu_mode > sizeof(long)) { + if (info->cpu_mode > sizeof(long)) { err = -EINVAL; goto __err; } - cptr->convert32 = (info.cpu_mode < sizeof(long)); + cptr->convert32 = (info->cpu_mode < sizeof(long)); __err: snd_seq_client_unlock(cptr); return err; @@ -1214,51 +1209,43 @@ static void get_client_info(struct snd_seq_client *cptr, } static int snd_seq_ioctl_get_client_info(struct snd_seq_client *client, - void __user *arg) + void *arg) { + struct snd_seq_client_info *client_info = arg; struct snd_seq_client *cptr; - struct snd_seq_client_info client_info; - - if (copy_from_user(&client_info, arg, sizeof(client_info))) - return -EFAULT; /* requested client number */ - cptr = snd_seq_client_use_ptr(client_info.client); + cptr = snd_seq_client_use_ptr(client_info->client); if (cptr == NULL) return -ENOENT; /* don't change !!! */ - get_client_info(cptr, &client_info); + get_client_info(cptr, client_info); snd_seq_client_unlock(cptr); - if (copy_to_user(arg, &client_info, sizeof(client_info))) - return -EFAULT; return 0; } /* CLIENT_INFO ioctl() */ static int snd_seq_ioctl_set_client_info(struct snd_seq_client *client, - void __user *arg) + void *arg) { - struct snd_seq_client_info client_info; - - if (copy_from_user(&client_info, arg, sizeof(client_info))) - return -EFAULT; + struct snd_seq_client_info *client_info = arg; /* it is not allowed to set the info fields for an another client */ - if (client->number != client_info.client) + if (client->number != client_info->client) return -EPERM; /* also client type must be set now */ - if (client->type != client_info.type) + if (client->type != client_info->type) return -EINVAL; /* fill the info fields */ - if (client_info.name[0]) - strlcpy(client->name, client_info.name, sizeof(client->name)); + if (client_info->name[0]) + strlcpy(client->name, client_info->name, sizeof(client->name)); - client->filter = client_info.filter; - client->event_lost = client_info.event_lost; - memcpy(client->event_filter, client_info.event_filter, 32); + client->filter = client_info->filter; + client->event_lost = client_info->event_lost; + memcpy(client->event_filter, client_info->event_filter, 32); return 0; } @@ -1267,30 +1254,26 @@ static int snd_seq_ioctl_set_client_info(struct snd_seq_client *client, /* * CREATE PORT ioctl() */ -static int snd_seq_ioctl_create_port(struct snd_seq_client *client, - void __user *arg) +static int snd_seq_ioctl_create_port(struct snd_seq_client *client, void *arg) { + struct snd_seq_port_info *info = arg; struct snd_seq_client_port *port; - struct snd_seq_port_info info; struct snd_seq_port_callback *callback; - if (copy_from_user(&info, arg, sizeof(info))) - return -EFAULT; - /* it is not allowed to create the port for an another client */ - if (info.addr.client != client->number) + if (info->addr.client != client->number) return -EPERM; - port = snd_seq_create_port(client, (info.flags & SNDRV_SEQ_PORT_FLG_GIVEN_PORT) ? info.addr.port : -1); + port = snd_seq_create_port(client, (info->flags & SNDRV_SEQ_PORT_FLG_GIVEN_PORT) ? info->addr.port : -1); if (port == NULL) return -ENOMEM; - if (client->type == USER_CLIENT && info.kernel) { + if (client->type == USER_CLIENT && info->kernel) { snd_seq_delete_port(client, port->addr.port); return -EINVAL; } if (client->type == KERNEL_CLIENT) { - if ((callback = info.kernel) != NULL) { + if ((callback = info->kernel) != NULL) { if (callback->owner) port->owner = callback->owner; port->private_data = callback->private_data; @@ -1303,37 +1286,29 @@ static int snd_seq_ioctl_create_port(struct snd_seq_client *client, } } - info.addr = port->addr; + info->addr = port->addr; - snd_seq_set_port_info(port, &info); + snd_seq_set_port_info(port, info); snd_seq_system_client_ev_port_start(port->addr.client, port->addr.port); - if (copy_to_user(arg, &info, sizeof(info))) - return -EFAULT; - return 0; } /* * DELETE PORT ioctl() */ -static int snd_seq_ioctl_delete_port(struct snd_seq_client *client, - void __user *arg) +static int snd_seq_ioctl_delete_port(struct snd_seq_client *client, void *arg) { - struct snd_seq_port_info info; + struct snd_seq_port_info *info = arg; int err; - /* set passed parameters */ - if (copy_from_user(&info, arg, sizeof(info))) - return -EFAULT; - /* it is not allowed to remove the port for an another client */ - if (info.addr.client != client->number) + if (info->addr.client != client->number) return -EPERM; - err = snd_seq_delete_port(client, info.addr.port); + err = snd_seq_delete_port(client, info->addr.port); if (err >= 0) - snd_seq_system_client_ev_port_exit(client->number, info.addr.port); + snd_seq_system_client_ev_port_exit(client->number, info->addr.port); return err; } @@ -1341,32 +1316,27 @@ static int snd_seq_ioctl_delete_port(struct snd_seq_client *client, /* * GET_PORT_INFO ioctl() (on any client) */ -static int snd_seq_ioctl_get_port_info(struct snd_seq_client *client, - void __user *arg) +static int snd_seq_ioctl_get_port_info(struct snd_seq_client *client, void *arg) { + struct snd_seq_port_info *info = arg; struct snd_seq_client *cptr; struct snd_seq_client_port *port; - struct snd_seq_port_info info; - if (copy_from_user(&info, arg, sizeof(info))) - return -EFAULT; - cptr = snd_seq_client_use_ptr(info.addr.client); + cptr = snd_seq_client_use_ptr(info->addr.client); if (cptr == NULL) return -ENXIO; - port = snd_seq_port_use_ptr(cptr, info.addr.port); + port = snd_seq_port_use_ptr(cptr, info->addr.port); if (port == NULL) { snd_seq_client_unlock(cptr); return -ENOENT; /* don't change */ } /* get port info */ - snd_seq_get_port_info(port, &info); + snd_seq_get_port_info(port, info); snd_seq_port_unlock(port); snd_seq_client_unlock(cptr); - if (copy_to_user(arg, &info, sizeof(info))) - return -EFAULT; return 0; } @@ -1374,20 +1344,16 @@ static int snd_seq_ioctl_get_port_info(struct snd_seq_client *client, /* * SET_PORT_INFO ioctl() (only ports on this/own client) */ -static int snd_seq_ioctl_set_port_info(struct snd_seq_client *client, - void __user *arg) +static int snd_seq_ioctl_set_port_info(struct snd_seq_client *client, void *arg) { + struct snd_seq_port_info *info = arg; struct snd_seq_client_port *port; - struct snd_seq_port_info info; - - if (copy_from_user(&info, arg, sizeof(info))) - return -EFAULT; - if (info.addr.client != client->number) /* only set our own ports ! */ + if (info->addr.client != client->number) /* only set our own ports ! */ return -EPERM; - port = snd_seq_port_use_ptr(client, info.addr.port); + port = snd_seq_port_use_ptr(client, info->addr.port); if (port) { - snd_seq_set_port_info(port, &info); + snd_seq_set_port_info(port, info); snd_seq_port_unlock(port); } return 0; @@ -1453,34 +1419,31 @@ int snd_seq_client_notify_subscription(int client, int port, * add to port's subscription list IOCTL interface */ static int snd_seq_ioctl_subscribe_port(struct snd_seq_client *client, - void __user *arg) + void *arg) { + struct snd_seq_port_subscribe *subs = arg; int result = -EINVAL; struct snd_seq_client *receiver = NULL, *sender = NULL; struct snd_seq_client_port *sport = NULL, *dport = NULL; - struct snd_seq_port_subscribe subs; - - if (copy_from_user(&subs, arg, sizeof(subs))) - return -EFAULT; - if ((receiver = snd_seq_client_use_ptr(subs.dest.client)) == NULL) + if ((receiver = snd_seq_client_use_ptr(subs->dest.client)) == NULL) goto __end; - if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL) + if ((sender = snd_seq_client_use_ptr(subs->sender.client)) == NULL) goto __end; - if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL) + if ((sport = snd_seq_port_use_ptr(sender, subs->sender.port)) == NULL) goto __end; - if ((dport = snd_seq_port_use_ptr(receiver, subs.dest.port)) == NULL) + if ((dport = snd_seq_port_use_ptr(receiver, subs->dest.port)) == NULL) goto __end; - result = check_subscription_permission(client, sport, dport, &subs); + result = check_subscription_permission(client, sport, dport, subs); if (result < 0) goto __end; /* connect them */ - result = snd_seq_port_connect(client, sender, sport, receiver, dport, &subs); + result = snd_seq_port_connect(client, sender, sport, receiver, dport, subs); if (! result) /* broadcast announce */ snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, 0, - &subs, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED); + subs, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED); __end: if (sport) snd_seq_port_unlock(sport); @@ -1498,33 +1461,30 @@ static int snd_seq_ioctl_subscribe_port(struct snd_seq_client *client, * remove from port's subscription list */ static int snd_seq_ioctl_unsubscribe_port(struct snd_seq_client *client, - void __user *arg) + void *arg) { + struct snd_seq_port_subscribe *subs = arg; int result = -ENXIO; struct snd_seq_client *receiver = NULL, *sender = NULL; struct snd_seq_client_port *sport = NULL, *dport = NULL; - struct snd_seq_port_subscribe subs; - - if (copy_from_user(&subs, arg, sizeof(subs))) - return -EFAULT; - if ((receiver = snd_seq_client_use_ptr(subs.dest.client)) == NULL) + if ((receiver = snd_seq_client_use_ptr(subs->dest.client)) == NULL) goto __end; - if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL) + if ((sender = snd_seq_client_use_ptr(subs->sender.client)) == NULL) goto __end; - if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL) + if ((sport = snd_seq_port_use_ptr(sender, subs->sender.port)) == NULL) goto __end; - if ((dport = snd_seq_port_use_ptr(receiver, subs.dest.port)) == NULL) + if ((dport = snd_seq_port_use_ptr(receiver, subs->dest.port)) == NULL) goto __end; - result = check_subscription_permission(client, sport, dport, &subs); + result = check_subscription_permission(client, sport, dport, subs); if (result < 0) goto __end; - result = snd_seq_port_disconnect(client, sender, sport, receiver, dport, &subs); + result = snd_seq_port_disconnect(client, sender, sport, receiver, dport, subs); if (! result) /* broadcast announce */ snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, 0, - &subs, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED); + subs, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED); __end: if (sport) snd_seq_port_unlock(sport); @@ -1539,17 +1499,13 @@ static int snd_seq_ioctl_unsubscribe_port(struct snd_seq_client *client, /* CREATE_QUEUE ioctl() */ -static int snd_seq_ioctl_create_queue(struct snd_seq_client *client, - void __user *arg) +static int snd_seq_ioctl_create_queue(struct snd_seq_client *client, void *arg) { - struct snd_seq_queue_info info; + struct snd_seq_queue_info *info = arg; int result; struct snd_seq_queue *q; - if (copy_from_user(&info, arg, sizeof(info))) - return -EFAULT; - - result = snd_seq_queue_alloc(client->number, info.locked, info.flags); + result = snd_seq_queue_alloc(client->number, info->locked, info->flags); if (result < 0) return result; @@ -1557,181 +1513,150 @@ static int snd_seq_ioctl_create_queue(struct snd_seq_client *client, if (q == NULL) return -EINVAL; - info.queue = q->queue; - info.locked = q->locked; - info.owner = q->owner; + info->queue = q->queue; + info->locked = q->locked; + info->owner = q->owner; /* set queue name */ - if (! info.name[0]) - snprintf(info.name, sizeof(info.name), "Queue-%d", q->queue); - strlcpy(q->name, info.name, sizeof(q->name)); + if (!info->name[0]) + snprintf(info->name, sizeof(info->name), "Queue-%d", q->queue); + strlcpy(q->name, info->name, sizeof(q->name)); queuefree(q); - if (copy_to_user(arg, &info, sizeof(info))) - return -EFAULT; - return 0; } /* DELETE_QUEUE ioctl() */ -static int snd_seq_ioctl_delete_queue(struct snd_seq_client *client, - void __user *arg) +static int snd_seq_ioctl_delete_queue(struct snd_seq_client *client, void *arg) { - struct snd_seq_queue_info info; - - if (copy_from_user(&info, arg, sizeof(info))) - return -EFAULT; + struct snd_seq_queue_info *info = arg; - return snd_seq_queue_delete(client->number, info.queue); + return snd_seq_queue_delete(client->number, info->queue); } /* GET_QUEUE_INFO ioctl() */ static int snd_seq_ioctl_get_queue_info(struct snd_seq_client *client, - void __user *arg) + void *arg) { - struct snd_seq_queue_info info; + struct snd_seq_queue_info *info = arg; struct snd_seq_queue *q; - if (copy_from_user(&info, arg, sizeof(info))) - return -EFAULT; - - q = queueptr(info.queue); + q = queueptr(info->queue); if (q == NULL) return -EINVAL; - memset(&info, 0, sizeof(info)); - info.queue = q->queue; - info.owner = q->owner; - info.locked = q->locked; - strlcpy(info.name, q->name, sizeof(info.name)); + memset(info, 0, sizeof(*info)); + info->queue = q->queue; + info->owner = q->owner; + info->locked = q->locked; + strlcpy(info->name, q->name, sizeof(info->name)); queuefree(q); - if (copy_to_user(arg, &info, sizeof(info))) - return -EFAULT; - return 0; } /* SET_QUEUE_INFO ioctl() */ static int snd_seq_ioctl_set_queue_info(struct snd_seq_client *client, - void __user *arg) + void *arg) { - struct snd_seq_queue_info info; + struct snd_seq_queue_info *info = arg; struct snd_seq_queue *q; - if (copy_from_user(&info, arg, sizeof(info))) - return -EFAULT; - - if (info.owner != client->number) + if (info->owner != client->number) return -EINVAL; /* change owner/locked permission */ - if (snd_seq_queue_check_access(info.queue, client->number)) { - if (snd_seq_queue_set_owner(info.queue, client->number, info.locked) < 0) + if (snd_seq_queue_check_access(info->queue, client->number)) { + if (snd_seq_queue_set_owner(info->queue, client->number, info->locked) < 0) return -EPERM; - if (info.locked) - snd_seq_queue_use(info.queue, client->number, 1); + if (info->locked) + snd_seq_queue_use(info->queue, client->number, 1); } else { return -EPERM; } - q = queueptr(info.queue); + q = queueptr(info->queue); if (! q) return -EINVAL; if (q->owner != client->number) { queuefree(q); return -EPERM; } - strlcpy(q->name, info.name, sizeof(q->name)); + strlcpy(q->name, info->name, sizeof(q->name)); queuefree(q); return 0; } /* GET_NAMED_QUEUE ioctl() */ -static int snd_seq_ioctl_get_named_queue(struct snd_seq_client *client, void __user *arg) +static int snd_seq_ioctl_get_named_queue(struct snd_seq_client *client, + void *arg) { - struct snd_seq_queue_info info; + struct snd_seq_queue_info *info = arg; struct snd_seq_queue *q; - if (copy_from_user(&info, arg, sizeof(info))) - return -EFAULT; - - q = snd_seq_queue_find_name(info.name); + q = snd_seq_queue_find_name(info->name); if (q == NULL) return -EINVAL; - info.queue = q->queue; - info.owner = q->owner; - info.locked = q->locked; + info->queue = q->queue; + info->owner = q->owner; + info->locked = q->locked; queuefree(q); - if (copy_to_user(arg, &info, sizeof(info))) - return -EFAULT; - return 0; } /* GET_QUEUE_STATUS ioctl() */ static int snd_seq_ioctl_get_queue_status(struct snd_seq_client *client, - void __user *arg) + void *arg) { - struct snd_seq_queue_status status; + struct snd_seq_queue_status *status = arg; struct snd_seq_queue *queue; struct snd_seq_timer *tmr; - if (copy_from_user(&status, arg, sizeof(status))) - return -EFAULT; - - queue = queueptr(status.queue); + queue = queueptr(status->queue); if (queue == NULL) return -EINVAL; - memset(&status, 0, sizeof(status)); - status.queue = queue->queue; + memset(status, 0, sizeof(*status)); + status->queue = queue->queue; tmr = queue->timer; - status.events = queue->tickq->cells + queue->timeq->cells; + status->events = queue->tickq->cells + queue->timeq->cells; - status.time = snd_seq_timer_get_cur_time(tmr); - status.tick = snd_seq_timer_get_cur_tick(tmr); + status->time = snd_seq_timer_get_cur_time(tmr); + status->tick = snd_seq_timer_get_cur_tick(tmr); - status.running = tmr->running; + status->running = tmr->running; - status.flags = queue->flags; + status->flags = queue->flags; queuefree(queue); - if (copy_to_user(arg, &status, sizeof(status))) - return -EFAULT; return 0; } /* GET_QUEUE_TEMPO ioctl() */ static int snd_seq_ioctl_get_queue_tempo(struct snd_seq_client *client, - void __user *arg) + void *arg) { - struct snd_seq_queue_tempo tempo; + struct snd_seq_queue_tempo *tempo = arg; struct snd_seq_queue *queue; struct snd_seq_timer *tmr; - if (copy_from_user(&tempo, arg, sizeof(tempo))) - return -EFAULT; - - queue = queueptr(tempo.queue); + queue = queueptr(tempo->queue); if (queue == NULL) return -EINVAL; - memset(&tempo, 0, sizeof(tempo)); - tempo.queue = queue->queue; + memset(tempo, 0, sizeof(*tempo)); + tempo->queue = queue->queue; tmr = queue->timer; - tempo.tempo = tmr->tempo; - tempo.ppq = tmr->ppq; - tempo.skew_value = tmr->skew; - tempo.skew_base = tmr->skew_base; + tempo->tempo = tmr->tempo; + tempo->ppq = tmr->ppq; + tempo->skew_value = tmr->skew; + tempo->skew_base = tmr->skew_base; queuefree(queue); - if (copy_to_user(arg, &tempo, sizeof(tempo))) - return -EFAULT; return 0; } @@ -1747,31 +1672,25 @@ int snd_seq_set_queue_tempo(int client, struct snd_seq_queue_tempo *tempo) EXPORT_SYMBOL(snd_seq_set_queue_tempo); static int snd_seq_ioctl_set_queue_tempo(struct snd_seq_client *client, - void __user *arg) + void *arg) { + struct snd_seq_queue_tempo *tempo = arg; int result; - struct snd_seq_queue_tempo tempo; - - if (copy_from_user(&tempo, arg, sizeof(tempo))) - return -EFAULT; - result = snd_seq_set_queue_tempo(client->number, &tempo); + result = snd_seq_set_queue_tempo(client->number, tempo); return result < 0 ? result : 0; } /* GET_QUEUE_TIMER ioctl() */ static int snd_seq_ioctl_get_queue_timer(struct snd_seq_client *client, - void __user *arg) + void *arg) { - struct snd_seq_queue_timer timer; + struct snd_seq_queue_timer *timer = arg; struct snd_seq_queue *queue; struct snd_seq_timer *tmr; - if (copy_from_user(&timer, arg, sizeof(timer))) - return -EFAULT; - - queue = queueptr(timer.queue); + queue = queueptr(timer->queue); if (queue == NULL) return -EINVAL; @@ -1780,41 +1699,36 @@ static int snd_seq_ioctl_get_queue_timer(struct snd_seq_client *client, return -ERESTARTSYS; } tmr = queue->timer; - memset(&timer, 0, sizeof(timer)); - timer.queue = queue->queue; + memset(timer, 0, sizeof(*timer)); + timer->queue = queue->queue; - timer.type = tmr->type; + timer->type = tmr->type; if (tmr->type == SNDRV_SEQ_TIMER_ALSA) { - timer.u.alsa.id = tmr->alsa_id; - timer.u.alsa.resolution = tmr->preferred_resolution; + timer->u.alsa.id = tmr->alsa_id; + timer->u.alsa.resolution = tmr->preferred_resolution; } mutex_unlock(&queue->timer_mutex); queuefree(queue); - if (copy_to_user(arg, &timer, sizeof(timer))) - return -EFAULT; return 0; } /* SET_QUEUE_TIMER ioctl() */ static int snd_seq_ioctl_set_queue_timer(struct snd_seq_client *client, - void __user *arg) + void *arg) { + struct snd_seq_queue_timer *timer = arg; int result = 0; - struct snd_seq_queue_timer timer; - - if (copy_from_user(&timer, arg, sizeof(timer))) - return -EFAULT; - if (timer.type != SNDRV_SEQ_TIMER_ALSA) + if (timer->type != SNDRV_SEQ_TIMER_ALSA) return -EINVAL; - if (snd_seq_queue_check_access(timer.queue, client->number)) { + if (snd_seq_queue_check_access(timer->queue, client->number)) { struct snd_seq_queue *q; struct snd_seq_timer *tmr; - q = queueptr(timer.queue); + q = queueptr(timer->queue); if (q == NULL) return -ENXIO; if (mutex_lock_interruptible(&q->timer_mutex)) { @@ -1822,13 +1736,13 @@ static int snd_seq_ioctl_set_queue_timer(struct snd_seq_client *client, return -ERESTARTSYS; } tmr = q->timer; - snd_seq_queue_timer_close(timer.queue); - tmr->type = timer.type; + snd_seq_queue_timer_close(timer->queue); + tmr->type = timer->type; if (tmr->type == SNDRV_SEQ_TIMER_ALSA) { - tmr->alsa_id = timer.u.alsa.id; - tmr->preferred_resolution = timer.u.alsa.resolution; + tmr->alsa_id = timer->u.alsa.id; + tmr->preferred_resolution = timer->u.alsa.resolution; } - result = snd_seq_queue_timer_open(timer.queue); + result = snd_seq_queue_timer_open(timer->queue); mutex_unlock(&q->timer_mutex); queuefree(q); } else { @@ -1841,38 +1755,30 @@ static int snd_seq_ioctl_set_queue_timer(struct snd_seq_client *client, /* GET_QUEUE_CLIENT ioctl() */ static int snd_seq_ioctl_get_queue_client(struct snd_seq_client *client, - void __user *arg) + void *arg) { - struct snd_seq_queue_client info; + struct snd_seq_queue_client *info = arg; int used; - if (copy_from_user(&info, arg, sizeof(info))) - return -EFAULT; - - used = snd_seq_queue_is_used(info.queue, client->number); + used = snd_seq_queue_is_used(info->queue, client->number); if (used < 0) return -EINVAL; - info.used = used; - info.client = client->number; + info->used = used; + info->client = client->number; - if (copy_to_user(arg, &info, sizeof(info))) - return -EFAULT; return 0; } /* SET_QUEUE_CLIENT ioctl() */ static int snd_seq_ioctl_set_queue_client(struct snd_seq_client *client, - void __user *arg) + void *arg) { + struct snd_seq_queue_client *info = arg; int err; - struct snd_seq_queue_client info; - - if (copy_from_user(&info, arg, sizeof(info))) - return -EFAULT; - if (info.used >= 0) { - err = snd_seq_queue_use(info.queue, client->number, info.used); + if (info->used >= 0) { + err = snd_seq_queue_use(info->queue, client->number, info->used); if (err < 0) return err; } @@ -1883,78 +1789,70 @@ static int snd_seq_ioctl_set_queue_client(struct snd_seq_client *client, /* GET_CLIENT_POOL ioctl() */ static int snd_seq_ioctl_get_client_pool(struct snd_seq_client *client, - void __user *arg) + void *arg) { - struct snd_seq_client_pool info; + struct snd_seq_client_pool *info = arg; struct snd_seq_client *cptr; - if (copy_from_user(&info, arg, sizeof(info))) - return -EFAULT; - - cptr = snd_seq_client_use_ptr(info.client); + cptr = snd_seq_client_use_ptr(info->client); if (cptr == NULL) return -ENOENT; - memset(&info, 0, sizeof(info)); - info.client = cptr->number; - info.output_pool = cptr->pool->size; - info.output_room = cptr->pool->room; - info.output_free = info.output_pool; - info.output_free = snd_seq_unused_cells(cptr->pool); + memset(info, 0, sizeof(*info)); + info->client = cptr->number; + info->output_pool = cptr->pool->size; + info->output_room = cptr->pool->room; + info->output_free = info->output_pool; + info->output_free = snd_seq_unused_cells(cptr->pool); if (cptr->type == USER_CLIENT) { - info.input_pool = cptr->data.user.fifo_pool_size; - info.input_free = info.input_pool; + info->input_pool = cptr->data.user.fifo_pool_size; + info->input_free = info->input_pool; if (cptr->data.user.fifo) - info.input_free = snd_seq_unused_cells(cptr->data.user.fifo->pool); + info->input_free = snd_seq_unused_cells(cptr->data.user.fifo->pool); } else { - info.input_pool = 0; - info.input_free = 0; + info->input_pool = 0; + info->input_free = 0; } snd_seq_client_unlock(cptr); - if (copy_to_user(arg, &info, sizeof(info))) - return -EFAULT; return 0; } /* SET_CLIENT_POOL ioctl() */ static int snd_seq_ioctl_set_client_pool(struct snd_seq_client *client, - void __user *arg) + void *arg) { - struct snd_seq_client_pool info; + struct snd_seq_client_pool *info = arg; int rc; - if (copy_from_user(&info, arg, sizeof(info))) - return -EFAULT; - - if (client->number != info.client) + if (client->number != info->client) return -EINVAL; /* can't change other clients */ - if (info.output_pool >= 1 && info.output_pool <= SNDRV_SEQ_MAX_EVENTS && + if (info->output_pool >= 1 && info->output_pool <= SNDRV_SEQ_MAX_EVENTS && (! snd_seq_write_pool_allocated(client) || - info.output_pool != client->pool->size)) { + info->output_pool != client->pool->size)) { if (snd_seq_write_pool_allocated(client)) { /* remove all existing cells */ snd_seq_queue_client_leave_cells(client->number); snd_seq_pool_done(client->pool); } - client->pool->size = info.output_pool; + client->pool->size = info->output_pool; rc = snd_seq_pool_init(client->pool); if (rc < 0) return rc; } if (client->type == USER_CLIENT && client->data.user.fifo != NULL && - info.input_pool >= 1 && - info.input_pool <= SNDRV_SEQ_MAX_CLIENT_EVENTS && - info.input_pool != client->data.user.fifo_pool_size) { + info->input_pool >= 1 && + info->input_pool <= SNDRV_SEQ_MAX_CLIENT_EVENTS && + info->input_pool != client->data.user.fifo_pool_size) { /* change pool size */ - rc = snd_seq_fifo_resize(client->data.user.fifo, info.input_pool); + rc = snd_seq_fifo_resize(client->data.user.fifo, info->input_pool); if (rc < 0) return rc; - client->data.user.fifo_pool_size = info.input_pool; + client->data.user.fifo_pool_size = info->input_pool; } - if (info.output_room >= 1 && - info.output_room <= client->pool->size) { - client->pool->room = info.output_room; + if (info->output_room >= 1 && + info->output_room <= client->pool->size) { + client->pool->room = info->output_room; } return snd_seq_ioctl_get_client_pool(client, arg); @@ -1963,17 +1861,14 @@ static int snd_seq_ioctl_set_client_pool(struct snd_seq_client *client, /* REMOVE_EVENTS ioctl() */ static int snd_seq_ioctl_remove_events(struct snd_seq_client *client, - void __user *arg) + void *arg) { - struct snd_seq_remove_events info; - - if (copy_from_user(&info, arg, sizeof(info))) - return -EFAULT; + struct snd_seq_remove_events *info = arg; /* * Input mostly not implemented XXX. */ - if (info.remove_mode & SNDRV_SEQ_REMOVE_INPUT) { + if (info->remove_mode & SNDRV_SEQ_REMOVE_INPUT) { /* * No restrictions so for a user client we can clear * the whole fifo @@ -1982,8 +1877,8 @@ static int snd_seq_ioctl_remove_events(struct snd_seq_client *client, snd_seq_fifo_clear(client->data.user.fifo); } - if (info.remove_mode & SNDRV_SEQ_REMOVE_OUTPUT) - snd_seq_queue_remove_cells(client->number, &info); + if (info->remove_mode & SNDRV_SEQ_REMOVE_OUTPUT) + snd_seq_queue_remove_cells(client->number, info); return 0; } @@ -1993,26 +1888,23 @@ static int snd_seq_ioctl_remove_events(struct snd_seq_client *client, * get subscription info */ static int snd_seq_ioctl_get_subscription(struct snd_seq_client *client, - void __user *arg) + void *arg) { + struct snd_seq_port_subscribe *subs = arg; int result; struct snd_seq_client *sender = NULL; struct snd_seq_client_port *sport = NULL; - struct snd_seq_port_subscribe subs; struct snd_seq_subscribers *p; - if (copy_from_user(&subs, arg, sizeof(subs))) - return -EFAULT; - result = -EINVAL; - if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL) + if ((sender = snd_seq_client_use_ptr(subs->sender.client)) == NULL) goto __end; - if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL) + if ((sport = snd_seq_port_use_ptr(sender, subs->sender.port)) == NULL) goto __end; - p = snd_seq_port_get_subscription(&sport->c_src, &subs.dest); + p = snd_seq_port_get_subscription(&sport->c_src, &subs->dest); if (p) { result = 0; - subs = p->info; + *subs = p->info; } else result = -ENOENT; @@ -2021,10 +1913,7 @@ static int snd_seq_ioctl_get_subscription(struct snd_seq_client *client, snd_seq_port_unlock(sport); if (sender) snd_seq_client_unlock(sender); - if (result >= 0) { - if (copy_to_user(arg, &subs, sizeof(subs))) - return -EFAULT; - } + return result; } @@ -2032,26 +1921,22 @@ static int snd_seq_ioctl_get_subscription(struct snd_seq_client *client, /* * get subscription info - check only its presence */ -static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, - void __user *arg) +static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, void *arg) { + struct snd_seq_query_subs *subs = arg; int result = -ENXIO; struct snd_seq_client *cptr = NULL; struct snd_seq_client_port *port = NULL; - struct snd_seq_query_subs subs; struct snd_seq_port_subs_info *group; struct list_head *p; int i; - if (copy_from_user(&subs, arg, sizeof(subs))) - return -EFAULT; - - if ((cptr = snd_seq_client_use_ptr(subs.root.client)) == NULL) + if ((cptr = snd_seq_client_use_ptr(subs->root.client)) == NULL) goto __end; - if ((port = snd_seq_port_use_ptr(cptr, subs.root.port)) == NULL) + if ((port = snd_seq_port_use_ptr(cptr, subs->root.port)) == NULL) goto __end; - switch (subs.type) { + switch (subs->type) { case SNDRV_SEQ_QUERY_SUBS_READ: group = &port->c_src; break; @@ -2064,22 +1949,22 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, down_read(&group->list_mutex); /* search for the subscriber */ - subs.num_subs = group->count; + subs->num_subs = group->count; i = 0; result = -ENOENT; list_for_each(p, &group->list_head) { - if (i++ == subs.index) { + if (i++ == subs->index) { /* found! */ struct snd_seq_subscribers *s; - if (subs.type == SNDRV_SEQ_QUERY_SUBS_READ) { + if (subs->type == SNDRV_SEQ_QUERY_SUBS_READ) { s = list_entry(p, struct snd_seq_subscribers, src_list); - subs.addr = s->info.dest; + subs->addr = s->info.dest; } else { s = list_entry(p, struct snd_seq_subscribers, dest_list); - subs.addr = s->info.sender; + subs->addr = s->info.sender; } - subs.flags = s->info.flags; - subs.queue = s->info.queue; + subs->flags = s->info.flags; + subs->queue = s->info.queue; result = 0; break; } @@ -2091,10 +1976,7 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, snd_seq_port_unlock(port); if (cptr) snd_seq_client_unlock(cptr); - if (result >= 0) { - if (copy_to_user(arg, &subs, sizeof(subs))) - return -EFAULT; - } + return result; } @@ -2103,31 +1985,26 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, * query next client */ static int snd_seq_ioctl_query_next_client(struct snd_seq_client *client, - void __user *arg) + void *arg) { + struct snd_seq_client_info *info = arg; struct snd_seq_client *cptr = NULL; - struct snd_seq_client_info info; - - if (copy_from_user(&info, arg, sizeof(info))) - return -EFAULT; /* search for next client */ - info.client++; - if (info.client < 0) - info.client = 0; - for (; info.client < SNDRV_SEQ_MAX_CLIENTS; info.client++) { - cptr = snd_seq_client_use_ptr(info.client); + info->client++; + if (info->client < 0) + info->client = 0; + for (; info->client < SNDRV_SEQ_MAX_CLIENTS; info->client++) { + cptr = snd_seq_client_use_ptr(info->client); if (cptr) break; /* found */ } if (cptr == NULL) return -ENOENT; - get_client_info(cptr, &info); + get_client_info(cptr, info); snd_seq_client_unlock(cptr); - if (copy_to_user(arg, &info, sizeof(info))) - return -EFAULT; return 0; } @@ -2135,43 +2012,41 @@ static int snd_seq_ioctl_query_next_client(struct snd_seq_client *client, * query next port */ static int snd_seq_ioctl_query_next_port(struct snd_seq_client *client, - void __user *arg) + void *arg) { + struct snd_seq_port_info *info = arg; struct snd_seq_client *cptr; struct snd_seq_client_port *port = NULL; - struct snd_seq_port_info info; - if (copy_from_user(&info, arg, sizeof(info))) - return -EFAULT; - cptr = snd_seq_client_use_ptr(info.addr.client); + cptr = snd_seq_client_use_ptr(info->addr.client); if (cptr == NULL) return -ENXIO; /* search for next port */ - info.addr.port++; - port = snd_seq_port_query_nearest(cptr, &info); + info->addr.port++; + port = snd_seq_port_query_nearest(cptr, info); if (port == NULL) { snd_seq_client_unlock(cptr); return -ENOENT; } /* get port info */ - info.addr = port->addr; - snd_seq_get_port_info(port, &info); + info->addr = port->addr; + snd_seq_get_port_info(port, info); snd_seq_port_unlock(port); snd_seq_client_unlock(cptr); - if (copy_to_user(arg, &info, sizeof(info))) - return -EFAULT; return 0; } /* -------------------------------------------------------- */ -static struct seq_ioctl_table { +static const struct ioctl_handler { unsigned int cmd; - int (*func)(struct snd_seq_client *client, void __user * arg); -} ioctl_tables[] = { + int (*func)(struct snd_seq_client *client, void *arg); +} ioctl_handlers[] = { + { SNDRV_SEQ_IOCTL_PVERSION, snd_seq_ioctl_pversion }, + { SNDRV_SEQ_IOCTL_CLIENT_ID, snd_seq_ioctl_client_id }, { SNDRV_SEQ_IOCTL_SYSTEM_INFO, snd_seq_ioctl_system_info }, { SNDRV_SEQ_IOCTL_RUNNING_MODE, snd_seq_ioctl_running_mode }, { SNDRV_SEQ_IOCTL_GET_CLIENT_INFO, snd_seq_ioctl_get_client_info }, @@ -2204,40 +2079,65 @@ static struct seq_ioctl_table { { 0, NULL }, }; -static int snd_seq_do_ioctl(struct snd_seq_client *client, unsigned int cmd, - void __user *arg) +static long snd_seq_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { - struct seq_ioctl_table *p; + struct snd_seq_client *client = file->private_data; + /* To use kernel stack for ioctl data. */ + union { + int pversion; + int client_id; + struct snd_seq_system_info system_info; + struct snd_seq_running_info running_info; + struct snd_seq_client_info client_info; + struct snd_seq_port_info port_info; + struct snd_seq_port_subscribe port_subscribe; + struct snd_seq_queue_info queue_info; + struct snd_seq_queue_status queue_status; + struct snd_seq_queue_tempo tempo; + struct snd_seq_queue_timer queue_timer; + struct snd_seq_queue_client queue_client; + struct snd_seq_client_pool client_pool; + struct snd_seq_remove_events remove_events; + struct snd_seq_query_subs query_subs; + } buf; + const struct ioctl_handler *handler; + unsigned long size; + int err; - switch (cmd) { - case SNDRV_SEQ_IOCTL_PVERSION: - /* return sequencer version number */ - return put_user(SNDRV_SEQ_VERSION, (int __user *)arg) ? -EFAULT : 0; - case SNDRV_SEQ_IOCTL_CLIENT_ID: - /* return the id of this client */ - return put_user(client->number, (int __user *)arg) ? -EFAULT : 0; - } + if (snd_BUG_ON(!client)) + return -ENXIO; - if (! arg) - return -EFAULT; - for (p = ioctl_tables; p->cmd; p++) { - if (p->cmd == cmd) - return p->func(client, arg); + for (handler = ioctl_handlers; handler->cmd > 0; ++handler) { + if (handler->cmd == cmd) + break; } - pr_debug("ALSA: seq unknown ioctl() 0x%x (type='%c', number=0x%02x)\n", - cmd, _IOC_TYPE(cmd), _IOC_NR(cmd)); - return -ENOTTY; -} + if (handler->cmd == 0) + return -ENOTTY; + memset(&buf, 0, sizeof(buf)); -static long snd_seq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct snd_seq_client *client = file->private_data; + /* + * All of ioctl commands for ALSA sequencer get an argument of size + * within 13 bits. We can safely pick up the size from the command. + */ + size = _IOC_SIZE(handler->cmd); + if (handler->cmd & IOC_IN) { + if (copy_from_user(&buf, (const void __user *)arg, size)) + return -EFAULT; + } - if (snd_BUG_ON(!client)) - return -ENXIO; - - return snd_seq_do_ioctl(client, cmd, (void __user *) arg); + err = handler->func(client, &buf); + if (err >= 0) { + /* Some commands includes a bug in 'dir' field. */ + if (handler->cmd == SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT || + handler->cmd == SNDRV_SEQ_IOCTL_SET_CLIENT_POOL || + (handler->cmd & IOC_OUT)) + if (copy_to_user((void __user *)arg, &buf, size)) + return -EFAULT; + } + + return err; } #ifdef CONFIG_COMPAT @@ -2423,23 +2323,35 @@ int snd_seq_kernel_client_dispatch(int client, struct snd_seq_event * ev, EXPORT_SYMBOL(snd_seq_kernel_client_dispatch); -/* - * exported, called by kernel clients to perform same functions as with - * userland ioctl() +/** + * snd_seq_kernel_client_ctl - operate a command for a client with data in + * kernel space. + * @clientid: A numerical ID for a client. + * @cmd: An ioctl(2) command for ALSA sequencer operation. + * @arg: A pointer to data in kernel space. + * + * Against its name, both kernel/application client can be handled by this + * kernel API. A pointer of 'arg' argument should be in kernel space. + * + * Return: 0 at success. Negative error code at failure. */ int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg) { + const struct ioctl_handler *handler; struct snd_seq_client *client; - mm_segment_t fs; - int result; client = clientptr(clientid); if (client == NULL) return -ENXIO; - fs = snd_enter_user(); - result = snd_seq_do_ioctl(client, cmd, (void __force __user *)arg); - snd_leave_user(fs); - return result; + + for (handler = ioctl_handlers; handler->cmd > 0; ++handler) { + if (handler->cmd == cmd) + return handler->func(client, arg); + } + + pr_debug("ALSA: seq unknown ioctl() 0x%x (type='%c', number=0x%02x)\n", + cmd, _IOC_TYPE(cmd), _IOC_NR(cmd)); + return -ENOTTY; } EXPORT_SYMBOL(snd_seq_kernel_client_ctl); diff --git a/sound/core/seq/seq_compat.c b/sound/core/seq/seq_compat.c index 65175902a68a..8c3507216676 100644 --- a/sound/core/seq/seq_compat.c +++ b/sound/core/seq/seq_compat.c @@ -47,7 +47,6 @@ static int snd_seq_call_port_info_ioctl(struct snd_seq_client *client, unsigned { int err = -EFAULT; struct snd_seq_port_info *data; - mm_segment_t fs; data = kmalloc(sizeof(*data), GFP_KERNEL); if (!data) @@ -59,9 +58,7 @@ static int snd_seq_call_port_info_ioctl(struct snd_seq_client *client, unsigned goto error; data->kernel = NULL; - fs = snd_enter_user(); - err = snd_seq_do_ioctl(client, cmd, data); - snd_leave_user(fs); + err = snd_seq_kernel_client_ctl(client->number, cmd, data); if (err < 0) goto error; @@ -123,7 +120,7 @@ static long snd_seq_ioctl_compat(struct file *file, unsigned int cmd, unsigned l case SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION: case SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT: case SNDRV_SEQ_IOCTL_RUNNING_MODE: - return snd_seq_do_ioctl(client, cmd, argp); + return snd_seq_ioctl(file, cmd, arg); case SNDRV_SEQ_IOCTL_CREATE_PORT32: return snd_seq_call_port_info_ioctl(client, SNDRV_SEQ_IOCTL_CREATE_PORT, argp); case SNDRV_SEQ_IOCTL_DELETE_PORT32: diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c index dcc102813aef..37d9cfbc29f9 100644 --- a/sound/core/seq/seq_timer.c +++ b/sound/core/seq/seq_timer.c @@ -448,8 +448,8 @@ snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr) ktime_get_ts64(&tm); tm = timespec64_sub(tm, tmr->last_update); - cur_time.tv_nsec = tm.tv_nsec; - cur_time.tv_sec = tm.tv_sec; + cur_time.tv_nsec += tm.tv_nsec; + cur_time.tv_sec += tm.tv_sec; snd_seq_sanity_real_time(&cur_time); } spin_unlock_irqrestore(&tmr->lock, flags); diff --git a/sound/drivers/opl3/opl3_lib.c b/sound/drivers/opl3/opl3_lib.c index 369cef212ea9..cd9e9f31720f 100644 --- a/sound/drivers/opl3/opl3_lib.c +++ b/sound/drivers/opl3/opl3_lib.c @@ -528,7 +528,7 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3, opl3->hwdep = hw; opl3->seq_dev_num = seq_device; -#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) +#if IS_REACHABLE(CONFIG_SND_SEQUENCER) if (snd_seq_device_new(card, seq_device, SNDRV_SEQ_DEV_ID_OPL3, sizeof(struct snd_opl3 *), &opl3->seq_dev) >= 0) { strcpy(opl3->seq_dev->name, hw->name); diff --git a/sound/drivers/pcsp/pcsp_lib.c b/sound/drivers/pcsp/pcsp_lib.c index 3689f5f6be64..aca2d7d5f059 100644 --- a/sound/drivers/pcsp/pcsp_lib.c +++ b/sound/drivers/pcsp/pcsp_lib.c @@ -166,7 +166,7 @@ static int pcsp_start_playing(struct snd_pcsp *chip) atomic_set(&chip->timer_active, 1); chip->thalf = 0; - hrtimer_start(&pcsp_chip.timer, ktime_set(0, 0), HRTIMER_MODE_REL); + hrtimer_start(&pcsp_chip.timer, 0, HRTIMER_MODE_REL); return 0; } diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile index af7ed6643266..dd454867240d 100644 --- a/sound/firewire/bebob/Makefile +++ b/sound/firewire/bebob/Makefile @@ -1,4 +1,5 @@ snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \ - bebob_pcm.o bebob_hwdep.o bebob_terratec.o bebob_yamaha.o \ - bebob_focusrite.o bebob_maudio.o bebob.o + bebob_pcm.o bebob_hwdep.o bebob_terratec.o \ + bebob_yamaha_terratec.o bebob_focusrite.o bebob_maudio.o \ + bebob.o obj-$(CONFIG_SND_BEBOB) += snd-bebob.o diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index f7e2cbd2a313..730ea91d9be8 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c @@ -172,12 +172,12 @@ get_saffire_spec(struct fw_unit *unit) static bool check_audiophile_booted(struct fw_unit *unit) { - char name[24] = {0}; + char name[28] = {0}; if (fw_csr_string(unit->directory, CSR_MODEL, name, sizeof(name)) < 0) return false; - return strncmp(name, "FW Audiophile Bootloader", 15) != 0; + return strncmp(name, "FW Audiophile Bootloader", 24) != 0; } static void @@ -458,17 +458,17 @@ static const struct ieee1394_device_id bebob_id_table[] = { /* TerraTec Electronic GmbH, PHASE 88 Rack FW */ SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000003, &phase88_rack_spec), /* TerraTec Electronic GmbH, PHASE 24 FW */ - SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000004, &phase24_series_spec), + SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000004, &yamaha_terratec_spec), /* TerraTec Electronic GmbH, Phase X24 FW */ - SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000007, &phase24_series_spec), + SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000007, &yamaha_terratec_spec), /* TerraTec Electronic GmbH, EWS MIC2/MIC8 */ SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000005, &spec_normal), /* Terratec Electronic GmbH, Aureon 7.1 Firewire */ SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000002, &spec_normal), /* Yamaha, GO44 */ - SND_BEBOB_DEV_ENTRY(VEN_YAMAHA, 0x0010000b, &yamaha_go_spec), + SND_BEBOB_DEV_ENTRY(VEN_YAMAHA, 0x0010000b, &yamaha_terratec_spec), /* YAMAHA, GO46 */ - SND_BEBOB_DEV_ENTRY(VEN_YAMAHA, 0x0010000c, &yamaha_go_spec), + SND_BEBOB_DEV_ENTRY(VEN_YAMAHA, 0x0010000c, &yamaha_terratec_spec), /* Focusrite, SaffirePro 26 I/O */ SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, 0x00000003, &saffirepro_26_spec), /* Focusrite, SaffirePro 10 I/O */ diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index e7f1bb925b12..175da875162d 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -235,8 +235,7 @@ int snd_bebob_create_hwdep_device(struct snd_bebob *bebob); /* model specific operations */ extern const struct snd_bebob_spec phase88_rack_spec; -extern const struct snd_bebob_spec phase24_series_spec; -extern const struct snd_bebob_spec yamaha_go_spec; +extern const struct snd_bebob_spec yamaha_terratec_spec; extern const struct snd_bebob_spec saffirepro_26_spec; extern const struct snd_bebob_spec saffirepro_10_spec; extern const struct snd_bebob_spec saffire_le_spec; diff --git a/sound/firewire/bebob/bebob_terratec.c b/sound/firewire/bebob/bebob_terratec.c index c38358b82ada..2fdaf93e7a8d 100644 --- a/sound/firewire/bebob/bebob_terratec.c +++ b/sound/firewire/bebob/bebob_terratec.c @@ -36,25 +36,6 @@ end: return err; } -static enum snd_bebob_clock_type phase24_series_clk_src_types[] = { - SND_BEBOB_CLOCK_TYPE_INTERNAL, - SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* S/PDIF */ -}; -static int -phase24_series_clk_src_get(struct snd_bebob *bebob, unsigned int *id) -{ - int err; - - err = avc_audio_get_selector(bebob->unit, 0, 4, id); - if (err < 0) - return err; - - if (*id >= ARRAY_SIZE(phase24_series_clk_src_types)) - return -EIO; - - return 0; -} - static const struct snd_bebob_rate_spec phase_series_rate_spec = { .get = &snd_bebob_stream_get_rate, .set = &snd_bebob_stream_set_rate, @@ -71,15 +52,3 @@ const struct snd_bebob_spec phase88_rack_spec = { .rate = &phase_series_rate_spec, .meter = NULL }; - -/* 'PHASE 24 FW' and 'PHASE X24 FW' */ -static const struct snd_bebob_clock_spec phase24_series_clk = { - .num = ARRAY_SIZE(phase24_series_clk_src_types), - .types = phase24_series_clk_src_types, - .get = &phase24_series_clk_src_get, -}; -const struct snd_bebob_spec phase24_series_spec = { - .clock = &phase24_series_clk, - .rate = &phase_series_rate_spec, - .meter = NULL -}; diff --git a/sound/firewire/bebob/bebob_yamaha.c b/sound/firewire/bebob/bebob_yamaha_terratec.c index 90d4404f77ce..a6be3e7138e0 100644 --- a/sound/firewire/bebob/bebob_yamaha.c +++ b/sound/firewire/bebob/bebob_yamaha_terratec.c @@ -14,7 +14,7 @@ * must be accompanied. If changing the state, a LED on the device starts to * blink and its sync status is false. In this state, the device sounds nothing * even if streaming. To start streaming at the current sampling rate is only - * way to revocer this state. GO46 is better for stand-alone mixer. + * way to recover this state. GO46 is better for stand-alone mixer. * * Both of them have a capability to change its sampling rate up to 192.0kHz. * At 192.0kHz, the device reports 4 PCM-in, 1 MIDI-in, 6 PCM-out, 1 MIDI-out. @@ -25,7 +25,10 @@ * streaming with many asynchronous transactions brings sounds with noises. * Unfortunately current 'ffado-mixer' generated many asynchronous transaction * to observe device's state, mainly check cmp connection and signal format. I - * reccomend users to close ffado-mixer at 192.0kHz if mixer is needless. + * recommend users to close ffado-mixer at 192.0kHz if mixer is needless. + * + * Terratec PHASE 24 FW and PHASE X24 FW are internally the same as + * Yamaha GO 44 and GO 46. Yamaha and Terratec had cooperated for these models. */ static enum snd_bebob_clock_type clk_src_types[] = { @@ -55,7 +58,7 @@ static const struct snd_bebob_rate_spec rate_spec = { .get = &snd_bebob_stream_get_rate, .set = &snd_bebob_stream_set_rate, }; -const struct snd_bebob_spec yamaha_go_spec = { +const struct snd_bebob_spec yamaha_terratec_spec = { .clock = &clock_spec, .rate = &rate_spec, .meter = NULL diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index 4aa0249826fd..6074fe1f00f7 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -302,7 +302,7 @@ static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream) int snd_dice_create_pcm(struct snd_dice *dice) { - static struct snd_pcm_ops capture_ops = { + static const struct snd_pcm_ops capture_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, @@ -314,7 +314,7 @@ int snd_dice_create_pcm(struct snd_dice *dice) .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; - static struct snd_pcm_ops playback_ops = { + static const struct snd_pcm_ops playback_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/firewire/digi00x/digi00x-pcm.c b/sound/firewire/digi00x/digi00x-pcm.c index cac28f70aef7..613f05872770 100644 --- a/sound/firewire/digi00x/digi00x-pcm.c +++ b/sound/firewire/digi00x/digi00x-pcm.c @@ -329,7 +329,7 @@ static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) return amdtp_stream_pcm_pointer(&dg00x->rx_stream); } -static struct snd_pcm_ops pcm_capture_ops = { +static const struct snd_pcm_ops pcm_capture_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, @@ -341,7 +341,7 @@ static struct snd_pcm_ops pcm_capture_ops = { .page = snd_pcm_lib_get_vmalloc_page, }; -static struct snd_pcm_ops pcm_playback_ops = { +static const struct snd_pcm_ops pcm_playback_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c index ee47924aef0d..827161bc269c 100644 --- a/sound/firewire/fireworks/fireworks_stream.c +++ b/sound/firewire/fireworks/fireworks_stream.c @@ -117,7 +117,7 @@ destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream) conn = &efw->in_conn; amdtp_stream_destroy(stream); - cmp_connection_destroy(&efw->out_conn); + cmp_connection_destroy(conn); } static int diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c index ca4dfcf43175..7683238283b6 100644 --- a/sound/firewire/lib.c +++ b/sound/firewire/lib.c @@ -114,7 +114,7 @@ static void async_midi_port_callback(struct fw_card *card, int rcode, snd_rawmidi_transmit_ack(substream, port->consume_bytes); else if (!rcode_is_permanent_error(rcode)) /* To start next transaction immediately for recovery. */ - port->next_ktime = ktime_set(0, 0); + port->next_ktime = 0; else /* Don't continue processing. */ port->error = true; @@ -156,7 +156,7 @@ static void midi_port_work(struct work_struct *work) if (port->consume_bytes <= 0) { /* Do it in next chance, immediately. */ if (port->consume_bytes == 0) { - port->next_ktime = ktime_set(0, 0); + port->next_ktime = 0; schedule_work(&port->work); } else { /* Fatal error. */ @@ -219,7 +219,7 @@ int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port, port->addr = addr; port->fill = fill; port->idling = true; - port->next_ktime = ktime_set(0, 0); + port->next_ktime = 0; port->error = false; INIT_WORK(&port->work, midi_port_work); diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c index 8d233417695d..f3530f89a025 100644 --- a/sound/firewire/oxfw/oxfw-pcm.c +++ b/sound/firewire/oxfw/oxfw-pcm.c @@ -388,7 +388,7 @@ static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstm) int snd_oxfw_create_pcm(struct snd_oxfw *oxfw) { - static struct snd_pcm_ops capture_ops = { + static const struct snd_pcm_ops capture_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, @@ -400,7 +400,7 @@ int snd_oxfw_create_pcm(struct snd_oxfw *oxfw) .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; - static struct snd_pcm_ops playback_ops = { + static const struct snd_pcm_ops playback_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/firewire/tascam/tascam-pcm.c b/sound/firewire/tascam/tascam-pcm.c index 380d3db969a5..79db1b651f5c 100644 --- a/sound/firewire/tascam/tascam-pcm.c +++ b/sound/firewire/tascam/tascam-pcm.c @@ -268,7 +268,7 @@ static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) return amdtp_stream_pcm_pointer(&tscm->rx_stream); } -static struct snd_pcm_ops pcm_capture_ops = { +static const struct snd_pcm_ops pcm_capture_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, @@ -280,7 +280,7 @@ static struct snd_pcm_ops pcm_capture_ops = { .page = snd_pcm_lib_get_vmalloc_page, }; -static struct snd_pcm_ops pcm_playback_ops = { +static const struct snd_pcm_ops pcm_playback_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/firewire/tascam/tascam-stream.c b/sound/firewire/tascam/tascam-stream.c index 4ad3bd7fd445..f1657a4e0621 100644 --- a/sound/firewire/tascam/tascam-stream.c +++ b/sound/firewire/tascam/tascam-stream.c @@ -343,7 +343,7 @@ int snd_tscm_stream_init_duplex(struct snd_tscm *tscm) if (err < 0) amdtp_stream_destroy(&tscm->rx_stream); - return 0; + return err; } /* At bus reset, streaming is stopped and some registers are clear. */ diff --git a/sound/hda/ext/hdac_ext_controller.c b/sound/hda/ext/hdac_ext_controller.c index 860f8cad6602..261469188566 100644 --- a/sound/hda/ext/hdac_ext_controller.c +++ b/sound/hda/ext/hdac_ext_controller.c @@ -29,81 +29,6 @@ */ #define HDAC_MAX_CAPS 10 -/** - * snd_hdac_ext_bus_parse_capabilities - parse capablity structure - * @ebus: the pointer to extended bus object - * - * Returns 0 if successful, or a negative error code. - */ -int snd_hdac_ext_bus_parse_capabilities(struct hdac_ext_bus *ebus) -{ - unsigned int cur_cap; - unsigned int offset; - struct hdac_bus *bus = &ebus->bus; - unsigned int counter = 0; - - offset = snd_hdac_chip_readl(bus, LLCH); - - /* Lets walk the linked capabilities list */ - do { - cur_cap = _snd_hdac_chip_read(l, bus, offset); - - dev_dbg(bus->dev, "Capability version: 0x%x\n", - ((cur_cap & AZX_CAP_HDR_VER_MASK) >> AZX_CAP_HDR_VER_OFF)); - - dev_dbg(bus->dev, "HDA capability ID: 0x%x\n", - (cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF); - - switch ((cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF) { - case AZX_ML_CAP_ID: - dev_dbg(bus->dev, "Found ML capability\n"); - ebus->mlcap = bus->remap_addr + offset; - break; - - case AZX_GTS_CAP_ID: - dev_dbg(bus->dev, "Found GTS capability offset=%x\n", offset); - ebus->gtscap = bus->remap_addr + offset; - break; - - case AZX_PP_CAP_ID: - /* PP capability found, the Audio DSP is present */ - dev_dbg(bus->dev, "Found PP capability offset=%x\n", offset); - ebus->ppcap = bus->remap_addr + offset; - break; - - case AZX_SPB_CAP_ID: - /* SPIB capability found, handler function */ - dev_dbg(bus->dev, "Found SPB capability\n"); - ebus->spbcap = bus->remap_addr + offset; - break; - - case AZX_DRSM_CAP_ID: - /* DMA resume capability found, handler function */ - dev_dbg(bus->dev, "Found DRSM capability\n"); - ebus->drsmcap = bus->remap_addr + offset; - break; - - default: - dev_dbg(bus->dev, "Unknown capability %d\n", cur_cap); - break; - } - - counter++; - - if (counter > HDAC_MAX_CAPS) { - dev_err(bus->dev, "We exceeded HDAC Ext capablities!!!\n"); - break; - } - - /* read the offset of next capabiity */ - offset = cur_cap & AZX_CAP_HDR_NXT_PTR_MASK; - - } while (offset); - - return 0; -} -EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_parse_capabilities); - /* * processing pipe helpers - these helpers are useful for dealing with HDA * new capability of processing pipelines @@ -118,15 +43,15 @@ void snd_hdac_ext_bus_ppcap_enable(struct hdac_ext_bus *ebus, bool enable) { struct hdac_bus *bus = &ebus->bus; - if (!ebus->ppcap) { + if (!bus->ppcap) { dev_err(bus->dev, "Address of PP capability is NULL"); return; } if (enable) - snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_GPROCEN); + snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_GPROCEN); else - snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_GPROCEN, 0); + snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_GPROCEN, 0); } EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_enable); @@ -139,15 +64,15 @@ void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_ext_bus *ebus, bool enable) { struct hdac_bus *bus = &ebus->bus; - if (!ebus->ppcap) { + if (!bus->ppcap) { dev_err(bus->dev, "Address of PP capability is NULL\n"); return; } if (enable) - snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_PIE); + snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_PIE); else - snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_PIE, 0); + snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_PIE, 0); } EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_int_enable); @@ -171,7 +96,7 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus) struct hdac_ext_link *hlink; struct hdac_bus *bus = &ebus->bus; - link_count = readl(ebus->mlcap + AZX_REG_ML_MLCD) + 1; + link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1; dev_dbg(bus->dev, "In %s Link count: %d\n", __func__, link_count); @@ -181,7 +106,7 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus) return -ENOMEM; hlink->index = idx; hlink->bus = bus; - hlink->ml_addr = ebus->mlcap + AZX_ML_BASE + + hlink->ml_addr = bus->mlcap + AZX_ML_BASE + (AZX_ML_INTERVAL * idx); hlink->lcaps = readl(hlink->ml_addr + AZX_REG_ML_LCAP); hlink->lsdiid = readw(hlink->ml_addr + AZX_REG_ML_LSDIID); diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c index 626f3bb24c55..3be051ab5533 100644 --- a/sound/hda/ext/hdac_ext_stream.c +++ b/sound/hda/ext/hdac_ext_stream.c @@ -40,27 +40,27 @@ void snd_hdac_ext_stream_init(struct hdac_ext_bus *ebus, { struct hdac_bus *bus = &ebus->bus; - if (ebus->ppcap) { - stream->pphc_addr = ebus->ppcap + AZX_PPHC_BASE + + if (bus->ppcap) { + stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE + AZX_PPHC_INTERVAL * idx; - stream->pplc_addr = ebus->ppcap + AZX_PPLC_BASE + + stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE + AZX_PPLC_MULTI * ebus->num_streams + AZX_PPLC_INTERVAL * idx; } - if (ebus->spbcap) { - stream->spib_addr = ebus->spbcap + AZX_SPB_BASE + + if (bus->spbcap) { + stream->spib_addr = bus->spbcap + AZX_SPB_BASE + AZX_SPB_INTERVAL * idx + AZX_SPB_SPIB; - stream->fifo_addr = ebus->spbcap + AZX_SPB_BASE + + stream->fifo_addr = bus->spbcap + AZX_SPB_BASE + AZX_SPB_INTERVAL * idx + AZX_SPB_MAXFIFO; } - if (ebus->drsmcap) - stream->dpibr_addr = ebus->drsmcap + AZX_DRSM_BASE + + if (bus->drsmcap) + stream->dpibr_addr = bus->drsmcap + AZX_DRSM_BASE + AZX_DRSM_INTERVAL * idx; stream->decoupled = false; @@ -131,10 +131,10 @@ void snd_hdac_ext_stream_decouple(struct hdac_ext_bus *ebus, spin_lock_irq(&bus->reg_lock); if (decouple) - snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, 0, + snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_PROCEN(hstream->index)); else - snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, + snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_PROCEN(hstream->index), 0); stream->decoupled = decouple; spin_unlock_irq(&bus->reg_lock); @@ -255,7 +255,7 @@ hdac_ext_link_stream_assign(struct hdac_ext_bus *ebus, struct hdac_stream *stream = NULL; struct hdac_bus *hbus = &ebus->bus; - if (!ebus->ppcap) { + if (!hbus->ppcap) { dev_err(hbus->dev, "stream type not supported\n"); return NULL; } @@ -296,7 +296,7 @@ hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus, struct hdac_stream *stream = NULL; struct hdac_bus *hbus = &ebus->bus; - if (!ebus->ppcap) { + if (!hbus->ppcap) { dev_err(hbus->dev, "stream type not supported\n"); return NULL; } @@ -423,21 +423,21 @@ void snd_hdac_ext_stream_spbcap_enable(struct hdac_ext_bus *ebus, u32 register_mask = 0; struct hdac_bus *bus = &ebus->bus; - if (!ebus->spbcap) { - dev_err(bus->dev, "Address of SPB capability is NULL"); + if (!bus->spbcap) { + dev_err(bus->dev, "Address of SPB capability is NULL\n"); return; } mask |= (1 << index); - register_mask = readl(ebus->spbcap + AZX_REG_SPB_SPBFCCTL); + register_mask = readl(bus->spbcap + AZX_REG_SPB_SPBFCCTL); mask |= register_mask; if (enable) - snd_hdac_updatel(ebus->spbcap, AZX_REG_SPB_SPBFCCTL, 0, mask); + snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, 0, mask); else - snd_hdac_updatel(ebus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, 0); + snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, 0); } EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_spbcap_enable); @@ -452,8 +452,8 @@ int snd_hdac_ext_stream_set_spib(struct hdac_ext_bus *ebus, { struct hdac_bus *bus = &ebus->bus; - if (!ebus->spbcap) { - dev_err(bus->dev, "Address of SPB capability is NULL"); + if (!bus->spbcap) { + dev_err(bus->dev, "Address of SPB capability is NULL\n"); return -EINVAL; } @@ -475,8 +475,8 @@ int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_ext_bus *ebus, { struct hdac_bus *bus = &ebus->bus; - if (!ebus->spbcap) { - dev_err(bus->dev, "Address of SPB capability is NULL"); + if (!bus->spbcap) { + dev_err(bus->dev, "Address of SPB capability is NULL\n"); return -EINVAL; } @@ -515,21 +515,21 @@ void snd_hdac_ext_stream_drsm_enable(struct hdac_ext_bus *ebus, u32 register_mask = 0; struct hdac_bus *bus = &ebus->bus; - if (!ebus->drsmcap) { - dev_err(bus->dev, "Address of DRSM capability is NULL"); + if (!bus->drsmcap) { + dev_err(bus->dev, "Address of DRSM capability is NULL\n"); return; } mask |= (1 << index); - register_mask = readl(ebus->drsmcap + AZX_REG_SPB_SPBFCCTL); + register_mask = readl(bus->drsmcap + AZX_REG_SPB_SPBFCCTL); mask |= register_mask; if (enable) - snd_hdac_updatel(ebus->drsmcap, AZX_REG_DRSM_CTL, 0, mask); + snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, 0, mask); else - snd_hdac_updatel(ebus->drsmcap, AZX_REG_DRSM_CTL, mask, 0); + snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, 0); } EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_drsm_enable); @@ -544,8 +544,8 @@ int snd_hdac_ext_stream_set_dpibr(struct hdac_ext_bus *ebus, { struct hdac_bus *bus = &ebus->bus; - if (!ebus->drsmcap) { - dev_err(bus->dev, "Address of DRSM capability is NULL"); + if (!bus->drsmcap) { + dev_err(bus->dev, "Address of DRSM capability is NULL\n"); return -EINVAL; } diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c index 9fee464e5d49..043065867656 100644 --- a/sound/hda/hdac_controller.c +++ b/sound/hda/hdac_controller.c @@ -255,6 +255,81 @@ int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr, } EXPORT_SYMBOL_GPL(snd_hdac_bus_get_response); +#define HDAC_MAX_CAPS 10 +/** + * snd_hdac_bus_parse_capabilities - parse capability structure + * @bus: the pointer to bus object + * + * Returns 0 if successful, or a negative error code. + */ +int snd_hdac_bus_parse_capabilities(struct hdac_bus *bus) +{ + unsigned int cur_cap; + unsigned int offset; + unsigned int counter = 0; + + offset = snd_hdac_chip_readl(bus, LLCH); + + /* Lets walk the linked capabilities list */ + do { + cur_cap = _snd_hdac_chip_read(l, bus, offset); + + dev_dbg(bus->dev, "Capability version: 0x%x\n", + (cur_cap & AZX_CAP_HDR_VER_MASK) >> AZX_CAP_HDR_VER_OFF); + + dev_dbg(bus->dev, "HDA capability ID: 0x%x\n", + (cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF); + + switch ((cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF) { + case AZX_ML_CAP_ID: + dev_dbg(bus->dev, "Found ML capability\n"); + bus->mlcap = bus->remap_addr + offset; + break; + + case AZX_GTS_CAP_ID: + dev_dbg(bus->dev, "Found GTS capability offset=%x\n", offset); + bus->gtscap = bus->remap_addr + offset; + break; + + case AZX_PP_CAP_ID: + /* PP capability found, the Audio DSP is present */ + dev_dbg(bus->dev, "Found PP capability offset=%x\n", offset); + bus->ppcap = bus->remap_addr + offset; + break; + + case AZX_SPB_CAP_ID: + /* SPIB capability found, handler function */ + dev_dbg(bus->dev, "Found SPB capability\n"); + bus->spbcap = bus->remap_addr + offset; + break; + + case AZX_DRSM_CAP_ID: + /* DMA resume capability found, handler function */ + dev_dbg(bus->dev, "Found DRSM capability\n"); + bus->drsmcap = bus->remap_addr + offset; + break; + + default: + dev_dbg(bus->dev, "Unknown capability %d\n", cur_cap); + break; + } + + counter++; + + if (counter > HDAC_MAX_CAPS) { + dev_err(bus->dev, "We exceeded HDAC capabilities!!!\n"); + break; + } + + /* read the offset of next capability */ + offset = cur_cap & AZX_CAP_HDR_NXT_PTR_MASK; + + } while (offset); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_parse_capabilities); + /* * Lowlevel interface */ diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c index c9af022676c2..0659bf389489 100644 --- a/sound/hda/hdac_i915.c +++ b/sound/hda/hdac_i915.c @@ -193,6 +193,7 @@ static int pin2port(struct hdac_device *codec, hda_nid_t pin_nid) * snd_hdac_sync_audio_rate - Set N/CTS based on the sample rate * @codec: HDA codec * @nid: the pin widget NID + * @dev_id: device identifier * @rate: the sample rate to set * * This function is supposed to be used only by a HD-audio controller @@ -201,18 +202,20 @@ static int pin2port(struct hdac_device *codec, hda_nid_t pin_nid) * This function sets N/CTS value based on the given sample rate. * Returns zero for success, or a negative error code. */ -int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid, int rate) +int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid, + int dev_id, int rate) { struct hdac_bus *bus = codec->bus; struct i915_audio_component *acomp = bus->audio_component; - int port; + int port, pipe; if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate) return -ENODEV; port = pin2port(codec, nid); if (port < 0) return -EINVAL; - return acomp->ops->sync_audio_rate(acomp->dev, port, rate); + pipe = dev_id; + return acomp->ops->sync_audio_rate(acomp->dev, port, pipe, rate); } EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate); @@ -220,6 +223,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate); * snd_hdac_acomp_get_eld - Get the audio state and ELD via component * @codec: HDA codec * @nid: the pin widget NID + * @dev_id: device identifier * @audio_enabled: the pointer to store the current audio state * @buffer: the buffer pointer to store ELD bytes * @max_bytes: the max bytes to be stored on @buffer @@ -236,12 +240,12 @@ EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate); * thus it may be over @max_bytes. If it's over @max_bytes, it implies * that only a part of ELD bytes have been fetched. */ -int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid, +int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid, int dev_id, bool *audio_enabled, char *buffer, int max_bytes) { struct hdac_bus *bus = codec->bus; struct i915_audio_component *acomp = bus->audio_component; - int port; + int port, pipe; if (!acomp || !acomp->ops || !acomp->ops->get_eld) return -ENODEV; @@ -249,7 +253,9 @@ int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid, port = pin2port(codec, nid); if (port < 0) return -EINVAL; - return acomp->ops->get_eld(acomp->dev, port, audio_enabled, + + pipe = dev_id; + return acomp->ops->get_eld(acomp->dev, port, pipe, audio_enabled, buffer, max_bytes); } EXPORT_SYMBOL_GPL(snd_hdac_acomp_get_eld); diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 38990a77d7b7..c6994ebb4567 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -465,7 +465,7 @@ int snd_hdac_stream_set_params(struct hdac_stream *azx_dev, } EXPORT_SYMBOL_GPL(snd_hdac_stream_set_params); -static cycle_t azx_cc_read(const struct cyclecounter *cc) +static u64 azx_cc_read(const struct cyclecounter *cc) { struct hdac_stream *azx_dev = container_of(cc, struct hdac_stream, cc); @@ -473,7 +473,7 @@ static cycle_t azx_cc_read(const struct cyclecounter *cc) } static void azx_timecounter_init(struct hdac_stream *azx_dev, - bool force, cycle_t last) + bool force, u64 last) { struct timecounter *tc = &azx_dev->tc; struct cyclecounter *cc = &azx_dev->cc; @@ -523,7 +523,7 @@ void snd_hdac_stream_timecounter_init(struct hdac_stream *azx_dev, struct snd_pcm_runtime *runtime = azx_dev->substream->runtime; struct hdac_stream *s; bool inited = false; - cycle_t cycle_last = 0; + u64 cycle_last = 0; int i = 0; list_for_each_entry(s, &bus->stream_list, list) { diff --git a/sound/oss/dmasound/dmasound_atari.c b/sound/oss/dmasound/dmasound_atari.c index 1c56bf58eff9..a1a2979c0bb1 100644 --- a/sound/oss/dmasound/dmasound_atari.c +++ b/sound/oss/dmasound/dmasound_atari.c @@ -22,7 +22,7 @@ #include <linux/spinlock.h> #include <linux/interrupt.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <asm/atariints.h> #include <asm/atari_stram.h> diff --git a/sound/oss/dmasound/dmasound_core.c b/sound/oss/dmasound/dmasound_core.c index f4ee85a4c42f..5f248fb41bea 100644 --- a/sound/oss/dmasound/dmasound_core.c +++ b/sound/oss/dmasound/dmasound_core.c @@ -183,7 +183,7 @@ #include <linux/poll.h> #include <linux/mutex.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include "dmasound.h" diff --git a/sound/oss/dmasound/dmasound_paula.c b/sound/oss/dmasound/dmasound_paula.c index 3f653618614d..81eb82c4675a 100644 --- a/sound/oss/dmasound/dmasound_paula.c +++ b/sound/oss/dmasound/dmasound_paula.c @@ -23,7 +23,7 @@ #include <linux/interrupt.h> #include <linux/platform_device.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <asm/setup.h> #include <asm/amigahw.h> #include <asm/amigaints.h> diff --git a/sound/oss/dmasound/dmasound_q40.c b/sound/oss/dmasound/dmasound_q40.c index 99bcb21c2281..be4fe15cfd6b 100644 --- a/sound/oss/dmasound/dmasound_q40.c +++ b/sound/oss/dmasound/dmasound_q40.c @@ -20,7 +20,7 @@ #include <linux/soundcard.h> #include <linux/interrupt.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <asm/q40ints.h> #include <asm/q40_master.h> diff --git a/sound/oss/msnd.c b/sound/oss/msnd.c index c0cc951ba97d..b63010ad22f1 100644 --- a/sound/oss/msnd.c +++ b/sound/oss/msnd.c @@ -32,7 +32,7 @@ #include <linux/interrupt.h> #include <asm/io.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/spinlock.h> #include <asm/irq.h> #include "msnd.h" diff --git a/sound/oss/os.h b/sound/oss/os.h index 75ad0cd0c0ab..0bf89e1d679c 100644 --- a/sound/oss/os.h +++ b/sound/oss/os.h @@ -17,7 +17,7 @@ #include <linux/ioport.h> #include <asm/page.h> #include <linux/vmalloc.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/poll.h> #include <linux/pci.h> #endif diff --git a/sound/oss/swarm_cs4297a.c b/sound/oss/swarm_cs4297a.c index 213a416b6e0b..f3af63e58b36 100644 --- a/sound/oss/swarm_cs4297a.c +++ b/sound/oss/swarm_cs4297a.c @@ -80,7 +80,7 @@ #include <asm/byteorder.h> #include <asm/dma.h> #include <asm/io.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <asm/sibyte/sb1250_regs.h> #include <asm/sibyte/sb1250_int.h> diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 82259ca61e64..1ef7cdf1d3e8 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -1907,7 +1907,7 @@ static int ac97_reset_wait(struct snd_ac97 *ac97, int timeout, int with_modem) * write). The other callbacks, wait and reset, are not mandatory. * * The clock is set to 48000. If another clock is needed, set - * (*rbus)->clock manually. + * ``(*rbus)->clock`` manually. * * The AC97 bus instance is registered as a low-level device, so you don't * have to release it manually. diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c index 1fc6d8bc09e5..8c36990e26f6 100644 --- a/sound/pci/ad1889.c +++ b/sound/pci/ad1889.c @@ -571,7 +571,7 @@ snd_ad1889_capture_pointer(struct snd_pcm_substream *ss) return bytes_to_frames(ss->runtime, ptr); } -static struct snd_pcm_ops snd_ad1889_playback_ops = { +static const struct snd_pcm_ops snd_ad1889_playback_ops = { .open = snd_ad1889_playback_open, .close = snd_ad1889_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -582,7 +582,7 @@ static struct snd_pcm_ops snd_ad1889_playback_ops = { .pointer = snd_ad1889_playback_pointer, }; -static struct snd_pcm_ops snd_ad1889_capture_ops = { +static const struct snd_pcm_ops snd_ad1889_capture_ops = { .open = snd_ad1889_capture_open, .close = snd_ad1889_capture_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index 36470af7eda7..92b819e4f729 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -1408,6 +1408,7 @@ snd_ali_playback_pointer(struct snd_pcm_substream *substream) spin_unlock(&codec->reg_lock); dev_dbg(codec->card->dev, "playback pointer returned cso=%xh.\n", cso); + cso %= runtime->buffer_size; return cso; } @@ -1428,6 +1429,7 @@ static snd_pcm_uframes_t snd_ali_pointer(struct snd_pcm_substream *substream) cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2)); spin_unlock(&codec->reg_lock); + cso %= runtime->buffer_size; return cso; } diff --git a/sound/pci/als300.c b/sound/pci/als300.c index add3176398d3..ab75601d7c2c 100644 --- a/sound/pci/als300.c +++ b/sound/pci/als300.c @@ -563,7 +563,7 @@ static snd_pcm_uframes_t snd_als300_pointer(struct snd_pcm_substream *substream) return bytes_to_frames(substream->runtime, current_ptr); } -static struct snd_pcm_ops snd_als300_playback_ops = { +static const struct snd_pcm_ops snd_als300_playback_ops = { .open = snd_als300_playback_open, .close = snd_als300_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -574,7 +574,7 @@ static struct snd_pcm_ops snd_als300_playback_ops = { .pointer = snd_als300_pointer, }; -static struct snd_pcm_ops snd_als300_capture_ops = { +static const struct snd_pcm_ops snd_als300_capture_ops = { .open = snd_als300_capture_open, .close = snd_als300_capture_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c index ff39a0c7277b..92bc06d01288 100644 --- a/sound/pci/als4000.c +++ b/sound/pci/als4000.c @@ -84,7 +84,7 @@ MODULE_DESCRIPTION("Avance Logic ALS4000"); MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("{{Avance Logic,ALS4000}}"); -#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#if IS_REACHABLE(CONFIG_GAMEPORT) #define SUPPORT_JOYSTICK 1 #endif @@ -672,7 +672,7 @@ static int snd_als4000_capture_close(struct snd_pcm_substream *substream) /******************************************************************/ -static struct snd_pcm_ops snd_als4000_playback_ops = { +static const struct snd_pcm_ops snd_als4000_playback_ops = { .open = snd_als4000_playback_open, .close = snd_als4000_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -683,7 +683,7 @@ static struct snd_pcm_ops snd_als4000_playback_ops = { .pointer = snd_als4000_playback_pointer }; -static struct snd_pcm_ops snd_als4000_capture_ops = { +static const struct snd_pcm_ops snd_als4000_capture_ops = { .open = snd_als4000_capture_open, .close = snd_als4000_capture_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index 1039eccbb895..976a3d23557e 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -1138,7 +1138,7 @@ static int snd_card_asihpi_playback_close(struct snd_pcm_substream *substream) return 0; } -static struct snd_pcm_ops snd_card_asihpi_playback_mmap_ops = { +static const struct snd_pcm_ops snd_card_asihpi_playback_mmap_ops = { .open = snd_card_asihpi_playback_open, .close = snd_card_asihpi_playback_close, .ioctl = snd_card_asihpi_playback_ioctl, @@ -1305,7 +1305,7 @@ static int snd_card_asihpi_capture_close(struct snd_pcm_substream *substream) return 0; } -static struct snd_pcm_ops snd_card_asihpi_capture_mmap_ops = { +static const struct snd_pcm_ops snd_card_asihpi_capture_mmap_ops = { .open = snd_card_asihpi_capture_open, .close = snd_card_asihpi_capture_close, .ioctl = snd_card_asihpi_capture_ioctl, diff --git a/sound/pci/asihpi/hpifunc.c b/sound/pci/asihpi/hpifunc.c index 510e56cffd31..f9b57647b319 100644 --- a/sound/pci/asihpi/hpifunc.c +++ b/sound/pci/asihpi/hpifunc.c @@ -2323,11 +2323,8 @@ u16 hpi_sample_clock_get_source_index(u32 h_control, u16 *pw_source_index) u16 hpi_sample_clock_query_local_rate(const u32 h_clock, const u32 index, u32 *prate) { - u16 err; - err = hpi_control_query(h_clock, HPI_SAMPLECLOCK_LOCAL_SAMPLERATE, - index, 0, prate); - - return err; + return hpi_control_query(h_clock, HPI_SAMPLECLOCK_LOCAL_SAMPLERATE, + index, 0, prate); } u16 hpi_sample_clock_set_local_rate(u32 h_control, u32 sample_rate) diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c index d17937b92331..7e3aa50b21f9 100644 --- a/sound/pci/asihpi/hpioctl.c +++ b/sound/pci/asihpi/hpioctl.c @@ -111,7 +111,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return -EINVAL; hm = kmalloc(sizeof(*hm), GFP_KERNEL); - hr = kmalloc(sizeof(*hr), GFP_KERNEL); + hr = kzalloc(sizeof(*hr), GFP_KERNEL); if (!hm || !hr) { err = -ENOMEM; goto out; diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 2ce0022dbc46..a40c918c8dff 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -1148,7 +1148,7 @@ static int snd_atiixp_spdif_close(struct snd_pcm_substream *substream) } /* AC97 playback */ -static struct snd_pcm_ops snd_atiixp_playback_ops = { +static const struct snd_pcm_ops snd_atiixp_playback_ops = { .open = snd_atiixp_playback_open, .close = snd_atiixp_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1160,7 +1160,7 @@ static struct snd_pcm_ops snd_atiixp_playback_ops = { }; /* AC97 capture */ -static struct snd_pcm_ops snd_atiixp_capture_ops = { +static const struct snd_pcm_ops snd_atiixp_capture_ops = { .open = snd_atiixp_capture_open, .close = snd_atiixp_capture_close, .ioctl = snd_pcm_lib_ioctl, @@ -1172,7 +1172,7 @@ static struct snd_pcm_ops snd_atiixp_capture_ops = { }; /* SPDIF playback */ -static struct snd_pcm_ops snd_atiixp_spdif_ops = { +static const struct snd_pcm_ops snd_atiixp_spdif_ops = { .open = snd_atiixp_spdif_open, .close = snd_atiixp_spdif_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index c534552963e7..40152feef1e7 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -947,7 +947,7 @@ static int snd_atiixp_capture_close(struct snd_pcm_substream *substream) /* AC97 playback */ -static struct snd_pcm_ops snd_atiixp_playback_ops = { +static const struct snd_pcm_ops snd_atiixp_playback_ops = { .open = snd_atiixp_playback_open, .close = snd_atiixp_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -959,7 +959,7 @@ static struct snd_pcm_ops snd_atiixp_playback_ops = { }; /* AC97 capture */ -static struct snd_pcm_ops snd_atiixp_capture_ops = { +static const struct snd_pcm_ops snd_atiixp_capture_ops = { .open = snd_atiixp_capture_open, .close = snd_atiixp_capture_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c index d3125c169684..e1af24f87566 100644 --- a/sound/pci/au88x0/au88x0_core.c +++ b/sound/pci/au88x0/au88x0_core.c @@ -1043,7 +1043,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)) - dev_err(vortex->card->dev, "bad adb fifo reset!"); + dev_err(vortex->card->dev, "bad adb fifo reset!\n"); vortex_fifo_clearadbdata(vortex, x, FIFO_SIZE); addr -= 4; } diff --git a/sound/pci/au88x0/au88x0_game.c b/sound/pci/au88x0/au88x0_game.c index 151815b857a0..53abcd3eccbd 100644 --- a/sound/pci/au88x0/au88x0_game.c +++ b/sound/pci/au88x0/au88x0_game.c @@ -36,7 +36,7 @@ #include <linux/gameport.h> #include <linux/export.h> -#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#if IS_REACHABLE(CONFIG_GAMEPORT) #define VORTEX_GAME_DWAIT 20 /* 20 ms */ diff --git a/sound/pci/aw2/aw2-alsa.c b/sound/pci/aw2/aw2-alsa.c index 167714303070..57bbb87d0c62 100644 --- a/sound/pci/aw2/aw2-alsa.c +++ b/sound/pci/aw2/aw2-alsa.c @@ -179,7 +179,7 @@ static struct pci_driver aw2_driver = { module_pci_driver(aw2_driver); /* operators for playback PCM alsa interface */ -static struct snd_pcm_ops snd_aw2_playback_ops = { +static const struct snd_pcm_ops snd_aw2_playback_ops = { .open = snd_aw2_pcm_playback_open, .close = snd_aw2_pcm_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -191,7 +191,7 @@ static struct snd_pcm_ops snd_aw2_playback_ops = { }; /* operators for capture PCM alsa interface */ -static struct snd_pcm_ops snd_aw2_capture_ops = { +static const struct snd_pcm_ops snd_aw2_capture_ops = { .open = snd_aw2_pcm_capture_open, .close = snd_aw2_pcm_capture_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 5e2ef0bb7057..79b2e6b7d88b 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -212,7 +212,7 @@ MODULE_DESCRIPTION("Aztech AZF3328 (PCI168)"); MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}"); -#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#if IS_REACHABLE(CONFIG_GAMEPORT) #define SUPPORT_GAMEPORT 1 #endif @@ -2090,7 +2090,7 @@ snd_azf3328_pcm_close(struct snd_pcm_substream *substream /******************************************************************/ -static struct snd_pcm_ops snd_azf3328_playback_ops = { +static const struct snd_pcm_ops snd_azf3328_playback_ops = { .open = snd_azf3328_pcm_playback_open, .close = snd_azf3328_pcm_close, .ioctl = snd_pcm_lib_ioctl, @@ -2101,7 +2101,7 @@ static struct snd_pcm_ops snd_azf3328_playback_ops = { .pointer = snd_azf3328_pcm_pointer }; -static struct snd_pcm_ops snd_azf3328_capture_ops = { +static const struct snd_pcm_ops snd_azf3328_capture_ops = { .open = snd_azf3328_pcm_capture_open, .close = snd_azf3328_pcm_close, .ioctl = snd_pcm_lib_ioctl, @@ -2112,7 +2112,7 @@ static struct snd_pcm_ops snd_azf3328_capture_ops = { .pointer = snd_azf3328_pcm_pointer }; -static struct snd_pcm_ops snd_azf3328_i2s_out_ops = { +static const struct snd_pcm_ops snd_azf3328_i2s_out_ops = { .open = snd_azf3328_pcm_i2s_out_open, .close = snd_azf3328_pcm_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c index 5925b7170e25..f2c0709d7441 100644 --- a/sound/pci/bt87x.c +++ b/sound/pci/bt87x.c @@ -550,7 +550,7 @@ static snd_pcm_uframes_t snd_bt87x_pointer(struct snd_pcm_substream *substream) return (snd_pcm_uframes_t)bytes_to_frames(runtime, chip->current_line * chip->line_bytes); } -static struct snd_pcm_ops snd_bt87x_pcm_ops = { +static const struct snd_pcm_ops snd_bt87x_pcm_ops = { .open = snd_bt87x_pcm_open, .close = snd_bt87x_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index d3cd95633ee2..6165a57a94ae 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -1109,7 +1109,7 @@ snd_ca0106_pcm_pointer_capture(struct snd_pcm_substream *substream) } /* operators */ -static struct snd_pcm_ops snd_ca0106_playback_front_ops = { +static const struct snd_pcm_ops snd_ca0106_playback_front_ops = { .open = snd_ca0106_pcm_open_playback_front, .close = snd_ca0106_pcm_close_playback, .ioctl = snd_pcm_lib_ioctl, @@ -1120,7 +1120,7 @@ static struct snd_pcm_ops snd_ca0106_playback_front_ops = { .pointer = snd_ca0106_pcm_pointer_playback, }; -static struct snd_pcm_ops snd_ca0106_capture_0_ops = { +static const struct snd_pcm_ops snd_ca0106_capture_0_ops = { .open = snd_ca0106_pcm_open_0_capture, .close = snd_ca0106_pcm_close_capture, .ioctl = snd_pcm_lib_ioctl, @@ -1131,7 +1131,7 @@ static struct snd_pcm_ops snd_ca0106_capture_0_ops = { .pointer = snd_ca0106_pcm_pointer_capture, }; -static struct snd_pcm_ops snd_ca0106_capture_1_ops = { +static const struct snd_pcm_ops snd_ca0106_capture_1_ops = { .open = snd_ca0106_pcm_open_1_capture, .close = snd_ca0106_pcm_close_capture, .ioctl = snd_pcm_lib_ioctl, @@ -1142,7 +1142,7 @@ static struct snd_pcm_ops snd_ca0106_capture_1_ops = { .pointer = snd_ca0106_pcm_pointer_capture, }; -static struct snd_pcm_ops snd_ca0106_capture_2_ops = { +static const struct snd_pcm_ops snd_ca0106_capture_2_ops = { .open = snd_ca0106_pcm_open_2_capture, .close = snd_ca0106_pcm_close_capture, .ioctl = snd_pcm_lib_ioctl, @@ -1153,7 +1153,7 @@ static struct snd_pcm_ops snd_ca0106_capture_2_ops = { .pointer = snd_ca0106_pcm_pointer_capture, }; -static struct snd_pcm_ops snd_ca0106_capture_3_ops = { +static const struct snd_pcm_ops snd_ca0106_capture_3_ops = { .open = snd_ca0106_pcm_open_3_capture, .close = snd_ca0106_pcm_close_capture, .ioctl = snd_pcm_lib_ioctl, @@ -1164,7 +1164,7 @@ static struct snd_pcm_ops snd_ca0106_capture_3_ops = { .pointer = snd_ca0106_pcm_pointer_capture, }; -static struct snd_pcm_ops snd_ca0106_playback_center_lfe_ops = { +static const struct snd_pcm_ops snd_ca0106_playback_center_lfe_ops = { .open = snd_ca0106_pcm_open_playback_center_lfe, .close = snd_ca0106_pcm_close_playback, .ioctl = snd_pcm_lib_ioctl, @@ -1175,7 +1175,7 @@ static struct snd_pcm_ops snd_ca0106_playback_center_lfe_ops = { .pointer = snd_ca0106_pcm_pointer_playback, }; -static struct snd_pcm_ops snd_ca0106_playback_unknown_ops = { +static const struct snd_pcm_ops snd_ca0106_playback_unknown_ops = { .open = snd_ca0106_pcm_open_playback_unknown, .close = snd_ca0106_pcm_close_playback, .ioctl = snd_pcm_lib_ioctl, @@ -1186,7 +1186,7 @@ static struct snd_pcm_ops snd_ca0106_playback_unknown_ops = { .pointer = snd_ca0106_pcm_pointer_playback, }; -static struct snd_pcm_ops snd_ca0106_playback_rear_ops = { +static const struct snd_pcm_ops snd_ca0106_playback_rear_ops = { .open = snd_ca0106_pcm_open_playback_rear, .close = snd_ca0106_pcm_close_playback, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 24cdcba06d27..aeedc270ed9b 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -48,7 +48,7 @@ MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8738}," "{C-Media,CMI8338A}," "{C-Media,CMI8338B}}"); -#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#if IS_REACHABLE(CONFIG_GAMEPORT) #define SUPPORT_JOYSTICK 1 #endif @@ -1838,7 +1838,7 @@ static int snd_cmipci_capture_spdif_close(struct snd_pcm_substream *substream) /* */ -static struct snd_pcm_ops snd_cmipci_playback_ops = { +static const struct snd_pcm_ops snd_cmipci_playback_ops = { .open = snd_cmipci_playback_open, .close = snd_cmipci_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1849,7 +1849,7 @@ static struct snd_pcm_ops snd_cmipci_playback_ops = { .pointer = snd_cmipci_playback_pointer, }; -static struct snd_pcm_ops snd_cmipci_capture_ops = { +static const struct snd_pcm_ops snd_cmipci_capture_ops = { .open = snd_cmipci_capture_open, .close = snd_cmipci_capture_close, .ioctl = snd_pcm_lib_ioctl, @@ -1860,7 +1860,7 @@ static struct snd_pcm_ops snd_cmipci_capture_ops = { .pointer = snd_cmipci_capture_pointer, }; -static struct snd_pcm_ops snd_cmipci_playback2_ops = { +static const struct snd_pcm_ops snd_cmipci_playback2_ops = { .open = snd_cmipci_playback2_open, .close = snd_cmipci_playback2_close, .ioctl = snd_pcm_lib_ioctl, @@ -1871,7 +1871,7 @@ static struct snd_pcm_ops snd_cmipci_playback2_ops = { .pointer = snd_cmipci_capture_pointer, /* channel B */ }; -static struct snd_pcm_ops snd_cmipci_playback_spdif_ops = { +static const struct snd_pcm_ops snd_cmipci_playback_spdif_ops = { .open = snd_cmipci_playback_spdif_open, .close = snd_cmipci_playback_spdif_close, .ioctl = snd_pcm_lib_ioctl, @@ -1882,7 +1882,7 @@ static struct snd_pcm_ops snd_cmipci_playback_spdif_ops = { .pointer = snd_cmipci_playback_pointer, }; -static struct snd_pcm_ops snd_cmipci_capture_spdif_ops = { +static const struct snd_pcm_ops snd_cmipci_capture_spdif_ops = { .open = snd_cmipci_capture_spdif_open, .close = snd_cmipci_capture_spdif_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index c296fd0dbc9c..8f0f5f24e40e 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -951,7 +951,7 @@ static int snd_cs4281_capture_close(struct snd_pcm_substream *substream) return 0; } -static struct snd_pcm_ops snd_cs4281_playback_ops = { +static const struct snd_pcm_ops snd_cs4281_playback_ops = { .open = snd_cs4281_playback_open, .close = snd_cs4281_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -962,7 +962,7 @@ static struct snd_pcm_ops snd_cs4281_playback_ops = { .pointer = snd_cs4281_pointer, }; -static struct snd_pcm_ops snd_cs4281_capture_ops = { +static const struct snd_pcm_ops snd_cs4281_capture_ops = { .open = snd_cs4281_capture_open, .close = snd_cs4281_capture_close, .ioctl = snd_pcm_lib_ioctl, @@ -1194,7 +1194,7 @@ static void snd_cs4281_proc_init(struct cs4281 *chip) * joystick support */ -#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#if IS_REACHABLE(CONFIG_GAMEPORT) static void snd_cs4281_gameport_trigger(struct gameport *gameport) { diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 2706f271a83b..fde3cd48258c 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -2460,7 +2460,7 @@ static int cs46xx_detect_codec(struct snd_cs46xx *chip, int codec) udelay(10); if (snd_cs46xx_codec_read(chip, AC97_RESET, codec) & 0x8000) { dev_dbg(chip->card->dev, - "seconadry codec not present\n"); + "secondary codec not present\n"); return -ENXIO; } } @@ -2503,7 +2503,7 @@ int snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device) chip->nr_ac97_codecs = 1; #ifdef CONFIG_SND_CS46XX_NEW_DSP - dev_dbg(chip->card->dev, "detecting seconadry codec\n"); + dev_dbg(chip->card->dev, "detecting secondary codec\n"); /* try detect a secondary codec */ if (! cs46xx_detect_codec(chip, CS46XX_SECONDARY_CODEC_INDEX)) chip->nr_ac97_codecs = 2; @@ -2718,7 +2718,7 @@ int snd_cs46xx_midi(struct snd_cs46xx *chip, int device) * gameport interface */ -#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#if IS_REACHABLE(CONFIG_GAMEPORT) static void snd_cs46xx_gameport_trigger(struct gameport *gameport) { diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c index d2951ed4bf71..aa61615288ff 100644 --- a/sound/pci/cs46xx/dsp_spos.c +++ b/sound/pci/cs46xx/dsp_spos.c @@ -107,7 +107,8 @@ static int shadow_and_reallocate_code (struct snd_cs46xx * chip, u32 * data, u32 dev_dbg(chip->card->dev, "handle_wideop:[2] %05x:%05x addr %04x\n", - hival, loval, address); nreallocated++; + hival, loval, address); + nreallocated++; } /* wide_opcodes[j] == wide_op */ } /* for */ } /* mod_type == 0 ... */ @@ -1441,7 +1442,7 @@ int cs46xx_dsp_scb_and_task_init (struct snd_cs46xx *chip) if (chip->nr_ac97_codecs == 2) { /* create CODEC tasklet for rear Center/LFE output - slot 6 and 9 on seconadry CODEC */ + slot 6 and 9 on secondary CODEC */ clfe_codec_out_scb = cs46xx_dsp_create_codec_out_scb(chip,"CodecOutSCB_CLFE",0x0030,0x0030, CLFE_MIXER_SCB_ADDR, CLFE_CODEC_SCB_ADDR, diff --git a/sound/pci/cs5535audio/cs5535audio_pcm.c b/sound/pci/cs5535audio/cs5535audio_pcm.c index 27fa57da8dc4..c208c1d8dbb2 100644 --- a/sound/pci/cs5535audio/cs5535audio_pcm.c +++ b/sound/pci/cs5535audio/cs5535audio_pcm.c @@ -380,7 +380,7 @@ static int snd_cs5535audio_capture_prepare(struct snd_pcm_substream *substream) substream->runtime->rate); } -static struct snd_pcm_ops snd_cs5535audio_playback_ops = { +static const struct snd_pcm_ops snd_cs5535audio_playback_ops = { .open = snd_cs5535audio_playback_open, .close = snd_cs5535audio_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -391,7 +391,7 @@ static struct snd_pcm_ops snd_cs5535audio_playback_ops = { .pointer = snd_cs5535audio_pcm_pointer, }; -static struct snd_pcm_ops snd_cs5535audio_capture_ops = { +static const struct snd_pcm_ops snd_cs5535audio_capture_ops = { .open = snd_cs5535audio_capture_open, .close = snd_cs5535audio_capture_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 977a59855fa6..908658a00377 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -1623,7 +1623,7 @@ static int atc_resume(struct ct_atc *atc) } #endif -static struct ct_atc atc_preset = { +static const struct ct_atc atc_preset = { .map_audio_buffer = ct_map_audio_buffer, .unmap_audio_buffer = ct_unmap_audio_buffer, .pcm_playback_prepare = atc_pcm_playback_prepare, diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c index d86c474ca5b6..974978041558 100644 --- a/sound/pci/ctxfi/ctpcm.c +++ b/sound/pci/ctxfi/ctpcm.c @@ -372,7 +372,7 @@ ct_pcm_capture_pointer(struct snd_pcm_substream *substream) } /* PCM operators for playback */ -static struct snd_pcm_ops ct_pcm_playback_ops = { +static const struct snd_pcm_ops ct_pcm_playback_ops = { .open = ct_pcm_playback_open, .close = ct_pcm_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -385,7 +385,7 @@ static struct snd_pcm_ops ct_pcm_playback_ops = { }; /* PCM operators for capture */ -static struct snd_pcm_ops ct_pcm_capture_ops = { +static const struct snd_pcm_ops ct_pcm_capture_ops = { .open = ct_pcm_capture_open, .close = ct_pcm_capture_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c index 419306ef825f..520e19bc649f 100644 --- a/sound/pci/ctxfi/ctvmem.c +++ b/sound/pci/ctxfi/ctvmem.c @@ -166,11 +166,7 @@ static void ct_vm_unmap(struct ct_vm *vm, struct ct_vm_block *block) static dma_addr_t ct_get_ptp_phys(struct ct_vm *vm, int index) { - dma_addr_t addr; - - addr = (index >= CT_PTP_NUM) ? ~0UL : vm->ptp[index].addr; - - return addr; + return (index >= CT_PTP_NUM) ? ~0UL : vm->ptp[index].addr; } int ct_vm_create(struct ct_vm **rvm, struct pci_dev *pci) diff --git a/sound/pci/echoaudio/layla24_dsp.c b/sound/pci/echoaudio/layla24_dsp.c index df28e5117359..c02bc1dcc170 100644 --- a/sound/pci/echoaudio/layla24_dsp.c +++ b/sound/pci/echoaudio/layla24_dsp.c @@ -135,7 +135,7 @@ static int load_asic(struct echoaudio *chip) err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC, FW_LAYLA24_2S_ASIC); if (err < 0) - return false; + return err; /* Now give the external ASIC a little time to set up */ mdelay(10); diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index db7a2e5e4a14..6a0e49ac5273 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -37,7 +37,7 @@ MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("{{Creative Labs,SB Live!/PCI512/E-mu APS}," "{Creative Labs,SB Audigy}}"); -#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) +#if IS_REACHABLE(CONFIG_SND_SEQUENCER) #define ENABLE_SYNTH #include <sound/emu10k1_synth.h> #endif @@ -194,6 +194,9 @@ static int snd_card_emu10k1_probe(struct pci_dev *pci, if ((err = snd_card_register(card)) < 0) goto error; + if (emu->card_capabilities->emu_model) + schedule_delayed_work(&emu->emu1010.firmware_work, 0); + pci_set_drvdata(pci, card); dev++; return 0; @@ -219,6 +222,8 @@ static int snd_emu10k1_suspend(struct device *dev) emu->suspend = 1; + cancel_delayed_work_sync(&emu->emu1010.firmware_work); + snd_pcm_suspend_all(emu->pcm); snd_pcm_suspend_all(emu->pcm_mic); snd_pcm_suspend_all(emu->pcm_efx); @@ -252,6 +257,10 @@ static int snd_emu10k1_resume(struct device *dev) emu->suspend = 0; snd_power_change_state(card, SNDRV_CTL_POWER_D0); + + if (emu->card_capabilities->emu_model) + schedule_delayed_work(&emu->emu1010.firmware_work, 0); + return 0; } diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 891453451543..ccf4415a1c7b 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -32,7 +32,6 @@ */ #include <linux/sched.h> -#include <linux/kthread.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/module.h> @@ -662,7 +661,7 @@ static int snd_emu10k1_cardbus_init(struct snd_emu10k1 *emu) return 0; } -static int snd_emu1010_load_firmware(struct snd_emu10k1 *emu, +static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu, const struct firmware *fw_entry) { int n, i; @@ -708,98 +707,104 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 *emu, return 0; } -static int emu1010_firmware_thread(void *data) +/* firmware file names, per model, init-fw and dock-fw (optional) */ +static const char * const firmware_names[5][2] = { + [EMU_MODEL_EMU1010] = { + HANA_FILENAME, DOCK_FILENAME + }, + [EMU_MODEL_EMU1010B] = { + EMU1010B_FILENAME, MICRO_DOCK_FILENAME + }, + [EMU_MODEL_EMU1616] = { + EMU1010_NOTEBOOK_FILENAME, MICRO_DOCK_FILENAME + }, + [EMU_MODEL_EMU0404] = { + EMU0404_FILENAME, NULL + }, +}; + +static int snd_emu1010_load_firmware(struct snd_emu10k1 *emu, int dock, + const struct firmware **fw) { - struct snd_emu10k1 *emu = data; + const char *filename; + int err; + + if (!*fw) { + filename = firmware_names[emu->card_capabilities->emu_model][dock]; + if (!filename) + return 0; + err = request_firmware(fw, filename, &emu->pci->dev); + if (err) + return err; + } + + return snd_emu1010_load_firmware_entry(emu, *fw); +} + +static void emu1010_firmware_work(struct work_struct *work) +{ + struct snd_emu10k1 *emu; u32 tmp, tmp2, reg; - u32 last_reg = 0; int err; - for (;;) { - /* Delay to allow Audio Dock to settle */ - msleep_interruptible(1000); - if (kthread_should_stop()) - break; + emu = container_of(work, struct snd_emu10k1, + emu1010.firmware_work.work); + if (emu->card->shutdown) + return; #ifdef CONFIG_PM_SLEEP - if (emu->suspend) - continue; + if (emu->suspend) + return; #endif - snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp); /* IRQ Status */ - snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, ®); /* OPTIONS: Which cards are attached to the EMU */ - if (reg & EMU_HANA_OPTION_DOCK_OFFLINE) { - /* Audio Dock attached */ - /* Return to Audio Dock programming mode */ - dev_info(emu->card->dev, - "emu1010: Loading Audio Dock Firmware\n"); - snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_AUDIODOCK); - - if (!emu->dock_fw) { - const char *filename = NULL; - switch (emu->card_capabilities->emu_model) { - case EMU_MODEL_EMU1010: - filename = DOCK_FILENAME; - break; - case EMU_MODEL_EMU1010B: - filename = MICRO_DOCK_FILENAME; - break; - case EMU_MODEL_EMU1616: - filename = MICRO_DOCK_FILENAME; - break; - } - if (filename) { - err = request_firmware(&emu->dock_fw, - filename, - &emu->pci->dev); - if (err) - continue; - } - } - - if (emu->dock_fw) { - err = snd_emu1010_load_firmware(emu, emu->dock_fw); - if (err) - continue; - } + snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp); /* IRQ Status */ + snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, ®); /* OPTIONS: Which cards are attached to the EMU */ + if (reg & EMU_HANA_OPTION_DOCK_OFFLINE) { + /* Audio Dock attached */ + /* Return to Audio Dock programming mode */ + dev_info(emu->card->dev, + "emu1010: Loading Audio Dock Firmware\n"); + snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, + EMU_HANA_FPGA_CONFIG_AUDIODOCK); + err = snd_emu1010_load_firmware(emu, 1, &emu->dock_fw); + if (err < 0) + goto next; - snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0); - snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, ®); - dev_info(emu->card->dev, - "emu1010: EMU_HANA+DOCK_IRQ_STATUS = 0x%x\n", - reg); - /* ID, should read & 0x7f = 0x55 when FPGA programmed. */ - snd_emu1010_fpga_read(emu, EMU_HANA_ID, ®); - dev_info(emu->card->dev, - "emu1010: EMU_HANA+DOCK_ID = 0x%x\n", reg); - if ((reg & 0x1f) != 0x15) { - /* FPGA failed to be programmed */ - dev_info(emu->card->dev, - "emu1010: Loading Audio Dock Firmware file failed, reg = 0x%x\n", - reg); - continue; - } - dev_info(emu->card->dev, - "emu1010: Audio Dock Firmware loaded\n"); - snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp); - snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2); - dev_info(emu->card->dev, "Audio Dock ver: %u.%u\n", - tmp, tmp2); - /* Sync clocking between 1010 and Dock */ - /* Allow DLL to settle */ - msleep(10); - /* Unmute all. Default is muted after a firmware load */ - snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE); - } else if (!reg && last_reg) { - /* Audio Dock removed */ + snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0); + snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp); + dev_info(emu->card->dev, + "emu1010: EMU_HANA+DOCK_IRQ_STATUS = 0x%x\n", tmp); + /* ID, should read & 0x7f = 0x55 when FPGA programmed. */ + snd_emu1010_fpga_read(emu, EMU_HANA_ID, &tmp); + dev_info(emu->card->dev, + "emu1010: EMU_HANA+DOCK_ID = 0x%x\n", tmp); + if ((tmp & 0x1f) != 0x15) { + /* FPGA failed to be programmed */ dev_info(emu->card->dev, - "emu1010: Audio Dock detached\n"); - /* Unmute all */ - snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE); + "emu1010: Loading Audio Dock Firmware file failed, reg = 0x%x\n", + tmp); + goto next; } - - last_reg = reg; + dev_info(emu->card->dev, + "emu1010: Audio Dock Firmware loaded\n"); + snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp); + snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2); + dev_info(emu->card->dev, "Audio Dock ver: %u.%u\n", tmp, tmp2); + /* Sync clocking between 1010 and Dock */ + /* Allow DLL to settle */ + msleep(10); + /* Unmute all. Default is muted after a firmware load */ + snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE); + } else if (!reg && emu->emu1010.last_reg) { + /* Audio Dock removed */ + dev_info(emu->card->dev, "emu1010: Audio Dock detached\n"); + /* Unmute all */ + snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE); } - dev_info(emu->card->dev, "emu1010: firmware thread stopping\n"); - return 0; + + next: + emu->emu1010.last_reg = reg; + if (!emu->card->shutdown) + schedule_delayed_work(&emu->emu1010.firmware_work, + msecs_to_jiffies(1000)); } /* @@ -881,39 +886,8 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu) } dev_info(emu->card->dev, "emu1010: EMU_HANA_ID = 0x%x\n", reg); - if (!emu->firmware) { - const char *filename; - switch (emu->card_capabilities->emu_model) { - case EMU_MODEL_EMU1010: - filename = HANA_FILENAME; - break; - case EMU_MODEL_EMU1010B: - filename = EMU1010B_FILENAME; - break; - case EMU_MODEL_EMU1616: - filename = EMU1010_NOTEBOOK_FILENAME; - break; - case EMU_MODEL_EMU0404: - filename = EMU0404_FILENAME; - break; - default: - return -ENODEV; - } - - err = request_firmware(&emu->firmware, filename, &emu->pci->dev); - if (err != 0) { - dev_info(emu->card->dev, - "emu1010: firmware: %s not found. Err = %d\n", - filename, err); - return err; - } - dev_info(emu->card->dev, - "emu1010: firmware file = %s, size = 0x%zx\n", - filename, emu->firmware->size); - } - - err = snd_emu1010_load_firmware(emu, emu->firmware); - if (err != 0) { + err = snd_emu1010_load_firmware(emu, 0, &emu->firmware); + if (err < 0) { dev_info(emu->card->dev, "emu1010: Loading Firmware failed\n"); return err; } @@ -1136,22 +1110,6 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu) snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp); snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10); /* SPDIF Format spdif (or 0x11 for aes/ebu) */ - /* Start Micro/Audio Dock firmware loader thread */ - if (!emu->emu1010.firmware_thread) { - emu->emu1010.firmware_thread = - kthread_create(emu1010_firmware_thread, emu, - "emu1010_firmware"); - if (IS_ERR(emu->emu1010.firmware_thread)) { - err = PTR_ERR(emu->emu1010.firmware_thread); - emu->emu1010.firmware_thread = NULL; - dev_info(emu->card->dev, - "emu1010: Creating thread failed\n"); - return err; - } - - wake_up_process(emu->emu1010.firmware_thread); - } - #if 0 snd_emu1010_fpga_link_dst_src_write(emu, EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32B + 2); /* ALICE2 bus 0xa2 */ @@ -1309,8 +1267,7 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu) /* Disable 48Volt power to Audio Dock */ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0); } - if (emu->emu1010.firmware_thread) - kthread_stop(emu->emu1010.firmware_thread); + cancel_delayed_work_sync(&emu->emu1010.firmware_work); release_firmware(emu->firmware); release_firmware(emu->dock_fw); if (emu->irq >= 0) @@ -1852,6 +1809,7 @@ int snd_emu10k1_create(struct snd_card *card, emu->irq = -1; emu->synth = NULL; emu->get_synth_voice = NULL; + INIT_DELAYED_WORK(&emu->emu1010.firmware_work, emu1010_firmware_work); /* read revision & serial */ emu->revision = pci->revision; pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial); diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index 6d1b98d14327..921037ed8468 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -548,7 +548,7 @@ snd_emu10k1x_pcm_pointer(struct snd_pcm_substream *substream) } /* operators */ -static struct snd_pcm_ops snd_emu10k1x_playback_ops = { +static const struct snd_pcm_ops snd_emu10k1x_playback_ops = { .open = snd_emu10k1x_playback_open, .close = snd_emu10k1x_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -694,7 +694,7 @@ snd_emu10k1x_pcm_pointer_capture(struct snd_pcm_substream *substream) return ptr; } -static struct snd_pcm_ops snd_emu10k1x_capture_ops = { +static const struct snd_pcm_ops snd_emu10k1x_capture_ops = { .open = snd_emu10k1x_pcm_open_capture, .close = snd_emu10k1x_pcm_close_capture, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index 14a305bd8a98..37be1e14d756 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -1364,7 +1364,7 @@ static int snd_emu10k1_capture_efx_close(struct snd_pcm_substream *substream) return 0; } -static struct snd_pcm_ops snd_emu10k1_playback_ops = { +static const struct snd_pcm_ops snd_emu10k1_playback_ops = { .open = snd_emu10k1_playback_open, .close = snd_emu10k1_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1376,7 +1376,7 @@ static struct snd_pcm_ops snd_emu10k1_playback_ops = { .page = snd_pcm_sgbuf_ops_page, }; -static struct snd_pcm_ops snd_emu10k1_capture_ops = { +static const struct snd_pcm_ops snd_emu10k1_capture_ops = { .open = snd_emu10k1_capture_open, .close = snd_emu10k1_capture_close, .ioctl = snd_pcm_lib_ioctl, @@ -1388,7 +1388,7 @@ static struct snd_pcm_ops snd_emu10k1_capture_ops = { }; /* EFX playback */ -static struct snd_pcm_ops snd_emu10k1_efx_playback_ops = { +static const struct snd_pcm_ops snd_emu10k1_efx_playback_ops = { .open = snd_emu10k1_efx_playback_open, .close = snd_emu10k1_efx_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1455,7 +1455,7 @@ int snd_emu10k1_pcm_multi(struct snd_emu10k1 *emu, int device) } -static struct snd_pcm_ops snd_emu10k1_capture_mic_ops = { +static const struct snd_pcm_ops snd_emu10k1_capture_mic_ops = { .open = snd_emu10k1_capture_mic_open, .close = snd_emu10k1_capture_mic_close, .ioctl = snd_pcm_lib_ioctl, @@ -1550,7 +1550,7 @@ static struct snd_kcontrol_new snd_emu10k1_pcm_efx_voices_mask = { .put = snd_emu10k1_pcm_efx_voices_mask_put }; -static struct snd_pcm_ops snd_emu10k1_capture_efx_ops = { +static const struct snd_pcm_ops snd_emu10k1_capture_efx_ops = { .open = snd_emu10k1_capture_efx_open, .close = snd_emu10k1_capture_efx_close, .ioctl = snd_pcm_lib_ioctl, @@ -1791,7 +1791,7 @@ static int snd_emu10k1_fx8010_playback_close(struct snd_pcm_substream *substream return 0; } -static struct snd_pcm_ops snd_emu10k1_fx8010_playback_ops = { +static const struct snd_pcm_ops snd_emu10k1_fx8010_playback_ops = { .open = snd_emu10k1_fx8010_playback_open, .close = snd_emu10k1_fx8010_playback_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c index 3c60b433de9f..c11f1a29f35d 100644 --- a/sound/pci/emu10k1/p16v.c +++ b/sound/pci/emu10k1/p16v.c @@ -300,37 +300,29 @@ static int snd_p16v_pcm_open_capture(struct snd_pcm_substream *substream) static int snd_p16v_pcm_hw_params_playback(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { - int result; - result = snd_pcm_lib_malloc_pages(substream, + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); - return result; } /* hw_params callback */ static int snd_p16v_pcm_hw_params_capture(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { - int result; - result = snd_pcm_lib_malloc_pages(substream, + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); - return result; } /* hw_free callback */ static int snd_p16v_pcm_hw_free_playback(struct snd_pcm_substream *substream) { - int result; - result = snd_pcm_lib_free_pages(substream); - return result; + return snd_pcm_lib_free_pages(substream); } /* hw_free callback */ static int snd_p16v_pcm_hw_free_capture(struct snd_pcm_substream *substream) { - int result; - result = snd_pcm_lib_free_pages(substream); - return result; + return snd_pcm_lib_free_pages(substream); } @@ -601,7 +593,7 @@ snd_p16v_pcm_pointer_capture(struct snd_pcm_substream *substream) } /* operators */ -static struct snd_pcm_ops snd_p16v_playback_front_ops = { +static const struct snd_pcm_ops snd_p16v_playback_front_ops = { .open = snd_p16v_pcm_open_playback_front, .close = snd_p16v_pcm_close_playback, .ioctl = snd_pcm_lib_ioctl, @@ -612,7 +604,7 @@ static struct snd_pcm_ops snd_p16v_playback_front_ops = { .pointer = snd_p16v_pcm_pointer_playback, }; -static struct snd_pcm_ops snd_p16v_capture_ops = { +static const struct snd_pcm_ops snd_p16v_capture_ops = { .open = snd_p16v_pcm_open_capture, .close = snd_p16v_pcm_close_capture, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 626cd2167d29..51736c2b5a00 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -79,7 +79,7 @@ MODULE_SUPPORTED_DEVICE("{{Ensoniq,AudioPCI ES1371/73}," "{Ectiva,EV1938}}"); #endif -#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#if IS_REACHABLE(CONFIG_GAMEPORT) #define SUPPORT_JOYSTICK #endif @@ -1227,7 +1227,7 @@ static int snd_ensoniq_capture_close(struct snd_pcm_substream *substream) return 0; } -static struct snd_pcm_ops snd_ensoniq_playback1_ops = { +static const struct snd_pcm_ops snd_ensoniq_playback1_ops = { .open = snd_ensoniq_playback1_open, .close = snd_ensoniq_playback1_close, .ioctl = snd_pcm_lib_ioctl, @@ -1238,7 +1238,7 @@ static struct snd_pcm_ops snd_ensoniq_playback1_ops = { .pointer = snd_ensoniq_playback1_pointer, }; -static struct snd_pcm_ops snd_ensoniq_playback2_ops = { +static const struct snd_pcm_ops snd_ensoniq_playback2_ops = { .open = snd_ensoniq_playback2_open, .close = snd_ensoniq_playback2_close, .ioctl = snd_pcm_lib_ioctl, @@ -1249,7 +1249,7 @@ static struct snd_pcm_ops snd_ensoniq_playback2_ops = { .pointer = snd_ensoniq_playback2_pointer, }; -static struct snd_pcm_ops snd_ensoniq_capture_ops = { +static const struct snd_pcm_ops snd_ensoniq_capture_ops = { .open = snd_ensoniq_capture_open, .close = snd_ensoniq_capture_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index 8963d7688fb0..e8d943071a8c 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -72,7 +72,7 @@ MODULE_SUPPORTED_DEVICE("{{ESS,ES1938}," "{ESS,ES1969}," "{TerraTec,128i PCI}}"); -#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#if IS_REACHABLE(CONFIG_GAMEPORT) #define SUPPORT_JOYSTICK 1 #endif @@ -992,7 +992,7 @@ static int snd_es1938_playback_close(struct snd_pcm_substream *substream) return 0; } -static struct snd_pcm_ops snd_es1938_playback_ops = { +static const struct snd_pcm_ops snd_es1938_playback_ops = { .open = snd_es1938_playback_open, .close = snd_es1938_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1003,7 +1003,7 @@ static struct snd_pcm_ops snd_es1938_playback_ops = { .pointer = snd_es1938_playback_pointer, }; -static struct snd_pcm_ops snd_es1938_capture_ops = { +static const struct snd_pcm_ops snd_es1938_capture_ops = { .open = snd_es1938_capture_open, .close = snd_es1938_capture_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 514f2604086e..2ec2b1ce0af6 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -126,7 +126,7 @@ MODULE_SUPPORTED_DEVICE("{{ESS,Maestro 2e}," "{ESS,Maestro 1}," "{TerraTec,DMX}}"); -#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#if IS_REACHABLE(CONFIG_GAMEPORT) #define SUPPORT_JOYSTICK 1 #endif @@ -1677,7 +1677,7 @@ static int snd_es1968_capture_close(struct snd_pcm_substream *substream) return 0; } -static struct snd_pcm_ops snd_es1968_playback_ops = { +static const struct snd_pcm_ops snd_es1968_playback_ops = { .open = snd_es1968_playback_open, .close = snd_es1968_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1688,7 +1688,7 @@ static struct snd_pcm_ops snd_es1968_playback_ops = { .pointer = snd_es1968_pcm_pointer, }; -static struct snd_pcm_ops snd_es1968_capture_ops = { +static const struct snd_pcm_ops snd_es1968_capture_ops = { .open = snd_es1968_capture_open, .close = snd_es1968_capture_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index bade9b907b92..c47287d79306 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -691,7 +691,7 @@ static int snd_fm801_capture_close(struct snd_pcm_substream *substream) return 0; } -static struct snd_pcm_ops snd_fm801_playback_ops = { +static const struct snd_pcm_ops snd_fm801_playback_ops = { .open = snd_fm801_playback_open, .close = snd_fm801_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -702,7 +702,7 @@ static struct snd_pcm_ops snd_fm801_playback_ops = { .pointer = snd_fm801_playback_pointer, }; -static struct snd_pcm_ops snd_fm801_capture_ops = { +static const struct snd_pcm_ops snd_fm801_capture_ops = { .open = snd_fm801_capture_open, .close = snd_fm801_capture_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/hda/dell_wmi_helper.c b/sound/pci/hda/dell_wmi_helper.c index 9c22f95838ef..19d41da79f93 100644 --- a/sound/pci/hda/dell_wmi_helper.c +++ b/sound/pci/hda/dell_wmi_helper.c @@ -49,7 +49,7 @@ static void alc_fixup_dell_wmi(struct hda_codec *codec, removefunc = true; if (dell_led_set_func(DELL_LED_MICMUTE, false) >= 0) { dell_led_value = 0; - if (spec->gen.num_adc_nids > 1) + if (spec->gen.num_adc_nids > 1 && !spec->gen.dyn_adc_switch) codec_dbg(codec, "Skipping micmute LED control due to several ADCs"); else { dell_old_cap_hook = spec->gen.cap_sync_hook; diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 7f57a145a47e..a03cf68d0bcd 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -884,6 +884,8 @@ void snd_hda_apply_fixup(struct hda_codec *codec, int action) } EXPORT_SYMBOL_GPL(snd_hda_apply_fixup); +#define IGNORE_SEQ_ASSOC (~(AC_DEFCFG_SEQUENCE | AC_DEFCFG_DEF_ASSOC)) + static bool pin_config_match(struct hda_codec *codec, const struct hda_pintbl *pins) { @@ -901,7 +903,7 @@ static bool pin_config_match(struct hda_codec *codec, for (; t_pins->nid; t_pins++) { if (t_pins->nid == nid) { found = 1; - if (t_pins->val == cfg) + if ((t_pins->val & IGNORE_SEQ_ASSOC) == (cfg & IGNORE_SEQ_ASSOC)) break; else if ((cfg & 0xf0000000) == 0x40000000 && (t_pins->val & 0xf0000000) == 0x40000000) break; diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 27de8015717d..500878556578 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -27,6 +27,12 @@ #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/slab.h> + +#ifdef CONFIG_X86 +/* for art-tsc conversion */ +#include <asm/tsc.h> +#endif + #include <sound/core.h> #include <sound/initval.h> #include "hda_controller.h" @@ -337,12 +343,173 @@ static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream) azx_get_position(chip, azx_dev)); } +/* + * azx_scale64: Scale base by mult/div while not overflowing sanely + * + * Derived from scale64_check_overflow in kernel/time/timekeeping.c + * + * The tmestamps for a 48Khz stream can overflow after (2^64/10^9)/48K which + * is about 384307 ie ~4.5 days. + * + * This scales the calculation so that overflow will happen but after 2^64 / + * 48000 secs, which is pretty large! + * + * In caln below: + * base may overflow, but since there isn’t any additional division + * performed on base it’s OK + * rem can’t overflow because both are 32-bit values + */ + +#ifdef CONFIG_X86 +static u64 azx_scale64(u64 base, u32 num, u32 den) +{ + u64 rem; + + rem = do_div(base, den); + + base *= num; + rem *= num; + + do_div(rem, den); + + return base + rem; +} + +static int azx_get_sync_time(ktime_t *device, + struct system_counterval_t *system, void *ctx) +{ + struct snd_pcm_substream *substream = ctx; + struct azx_dev *azx_dev = get_azx_dev(substream); + struct azx_pcm *apcm = snd_pcm_substream_chip(substream); + struct azx *chip = apcm->chip; + struct snd_pcm_runtime *runtime; + u64 ll_counter, ll_counter_l, ll_counter_h; + u64 tsc_counter, tsc_counter_l, tsc_counter_h; + u32 wallclk_ctr, wallclk_cycles; + bool direction; + u32 dma_select; + u32 timeout = 200; + u32 retry_count = 0; + + runtime = substream->runtime; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + direction = 1; + else + direction = 0; + + /* 0th stream tag is not used, so DMA ch 0 is for 1st stream tag */ + do { + timeout = 100; + dma_select = (direction << GTSCC_CDMAS_DMA_DIR_SHIFT) | + (azx_dev->core.stream_tag - 1); + snd_hdac_chip_writel(azx_bus(chip), GTSCC, dma_select); + + /* Enable the capture */ + snd_hdac_chip_updatel(azx_bus(chip), GTSCC, 0, GTSCC_TSCCI_MASK); + + while (timeout) { + if (snd_hdac_chip_readl(azx_bus(chip), GTSCC) & + GTSCC_TSCCD_MASK) + break; + + timeout--; + } + + if (!timeout) { + dev_err(chip->card->dev, "GTSCC capture Timedout!\n"); + return -EIO; + } + + /* Read wall clock counter */ + wallclk_ctr = snd_hdac_chip_readl(azx_bus(chip), WALFCC); + + /* Read TSC counter */ + tsc_counter_l = snd_hdac_chip_readl(azx_bus(chip), TSCCL); + tsc_counter_h = snd_hdac_chip_readl(azx_bus(chip), TSCCU); + + /* Read Link counter */ + ll_counter_l = snd_hdac_chip_readl(azx_bus(chip), LLPCL); + ll_counter_h = snd_hdac_chip_readl(azx_bus(chip), LLPCU); + + /* Ack: registers read done */ + snd_hdac_chip_writel(azx_bus(chip), GTSCC, GTSCC_TSCCD_SHIFT); + + tsc_counter = (tsc_counter_h << TSCCU_CCU_SHIFT) | + tsc_counter_l; + + ll_counter = (ll_counter_h << LLPC_CCU_SHIFT) | ll_counter_l; + wallclk_cycles = wallclk_ctr & WALFCC_CIF_MASK; + + /* + * An error occurs near frame "rollover". The clocks in + * frame value indicates whether this error may have + * occurred. Here we use the value of 10 i.e., + * HDA_MAX_CYCLE_OFFSET + */ + if (wallclk_cycles < HDA_MAX_CYCLE_VALUE - HDA_MAX_CYCLE_OFFSET + && wallclk_cycles > HDA_MAX_CYCLE_OFFSET) + break; + + /* + * Sleep before we read again, else we may again get + * value near to MAX_CYCLE. Try to sleep for different + * amount of time so we dont hit the same number again + */ + udelay(retry_count++); + + } while (retry_count != HDA_MAX_CYCLE_READ_RETRY); + + if (retry_count == HDA_MAX_CYCLE_READ_RETRY) { + dev_err_ratelimited(chip->card->dev, + "Error in WALFCC cycle count\n"); + return -EIO; + } + + *device = ns_to_ktime(azx_scale64(ll_counter, + NSEC_PER_SEC, runtime->rate)); + *device = ktime_add_ns(*device, (wallclk_cycles * NSEC_PER_SEC) / + ((HDA_MAX_CYCLE_VALUE + 1) * runtime->rate)); + + *system = convert_art_to_tsc(tsc_counter); + + return 0; +} + +#else +static int azx_get_sync_time(ktime_t *device, + struct system_counterval_t *system, void *ctx) +{ + return -ENXIO; +} +#endif + +static int azx_get_crosststamp(struct snd_pcm_substream *substream, + struct system_device_crosststamp *xtstamp) +{ + return get_device_system_crosststamp(azx_get_sync_time, + substream, NULL, xtstamp); +} + +static inline bool is_link_time_supported(struct snd_pcm_runtime *runtime, + struct snd_pcm_audio_tstamp_config *ts) +{ + if (runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME) + if (ts->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED) + return true; + + return false; +} + static int azx_get_time_info(struct snd_pcm_substream *substream, struct timespec *system_ts, struct timespec *audio_ts, struct snd_pcm_audio_tstamp_config *audio_tstamp_config, struct snd_pcm_audio_tstamp_report *audio_tstamp_report) { struct azx_dev *azx_dev = get_azx_dev(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct system_device_crosststamp xtstamp; + int ret; u64 nsec; if ((substream->runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_ATIME) && @@ -361,8 +528,37 @@ static int azx_get_time_info(struct snd_pcm_substream *substream, audio_tstamp_report->accuracy_report = 1; /* rest of structure is valid */ audio_tstamp_report->accuracy = 42; /* 24 MHz WallClock == 42ns resolution */ - } else + } else if (is_link_time_supported(runtime, audio_tstamp_config)) { + + ret = azx_get_crosststamp(substream, &xtstamp); + if (ret) + return ret; + + switch (runtime->tstamp_type) { + case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC: + return -EINVAL; + + case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC_RAW: + *system_ts = ktime_to_timespec(xtstamp.sys_monoraw); + break; + + default: + *system_ts = ktime_to_timespec(xtstamp.sys_realtime); + break; + + } + + *audio_ts = ktime_to_timespec(xtstamp.device); + + audio_tstamp_report->actual_type = + SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED; + audio_tstamp_report->accuracy_report = 1; + /* 24 MHz WallClock == 42ns resolution */ + audio_tstamp_report->accuracy = 42; + + } else { audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT; + } return 0; } @@ -412,6 +608,11 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) goto unlock; } runtime->private_data = azx_dev; + + if (chip->gts_present) + azx_pcm_hw.info = azx_pcm_hw.info | + SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME; + runtime->hw = azx_pcm_hw; runtime->hw.channels_min = hinfo->channels_min; runtime->hw.channels_max = hinfo->channels_max; @@ -495,7 +696,7 @@ static int azx_pcm_mmap(struct snd_pcm_substream *substream, return snd_pcm_lib_default_mmap(substream, area); } -static struct snd_pcm_ops azx_pcm_ops = { +static const struct snd_pcm_ops azx_pcm_ops = { .open = azx_pcm_open, .close = azx_pcm_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h index ec63bbf1ec6d..a50e0532622a 100644 --- a/sound/pci/hda/hda_controller.h +++ b/sound/pci/hda/hda_controller.h @@ -159,6 +159,9 @@ struct azx { unsigned int region_requested:1; unsigned int disabled:1; /* disabled by vga_switcheroo */ + /* GTS present */ + unsigned int gts_present:1; + #ifdef CONFIG_SND_HDA_DSP_LOADER struct azx_dev saved_azx_dev; #endif diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 160c7f713722..c64d986009a9 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -54,6 +54,7 @@ /* for snoop control */ #include <asm/pgtable.h> #include <asm/cacheflush.h> +#include <asm/cpufeature.h> #endif #include <sound/core.h> #include <sound/initval.h> @@ -340,8 +341,7 @@ enum { /* quirks for Nvidia */ #define AZX_DCAPS_PRESET_NVIDIA \ - (AZX_DCAPS_NO_MSI | /*AZX_DCAPS_ALIGN_BUFSIZE |*/ \ - AZX_DCAPS_NO_64BIT | AZX_DCAPS_CORBRP_SELF_CLEAR |\ + (AZX_DCAPS_NO_MSI | AZX_DCAPS_CORBRP_SELF_CLEAR |\ AZX_DCAPS_SNOOP_TYPE(NVIDIA)) #define AZX_DCAPS_PRESET_CTHDA \ @@ -1663,6 +1663,22 @@ static int azx_first_init(struct azx *chip) return -ENXIO; } + if (IS_SKL_PLUS(pci)) + snd_hdac_bus_parse_capabilities(bus); + + /* + * Some Intel CPUs has always running timer (ART) feature and + * controller may have Global time sync reporting capability, so + * check both of these before declaring synchronized time reporting + * capability SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME + */ + chip->gts_present = false; + +#ifdef CONFIG_X86 + if (bus->ppcap && boot_cpu_has(X86_FEATURE_ART)) + chip->gts_present = true; +#endif + if (chip->msi) { if (chip->driver_caps & AZX_DCAPS_NO_MSI64) { dev_dbg(card->dev, "Disabling 64bit MSI\n"); @@ -1699,6 +1715,10 @@ static int azx_first_init(struct azx *chip) } } + /* NVidia hardware normally only supports up to 40 bits of DMA */ + if (chip->pci->vendor == PCI_VENDOR_ID_NVIDIA) + dma_bits = 40; + /* disable 64bit DMA address on some devices */ if (chip->driver_caps & AZX_DCAPS_NO_64BIT) { dev_dbg(card->dev, "Disabling 64bit DMA\n"); diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 9ceb2bc36e68..11b9b2f17a2e 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -780,6 +780,7 @@ static const struct hda_pintbl alienware_pincfgs[] = { static const struct snd_pci_quirk ca0132_quirks[] = { SND_PCI_QUIRK(0x1028, 0x0685, "Alienware 15 2015", QUIRK_ALIENWARE), SND_PCI_QUIRK(0x1028, 0x0688, "Alienware 17 2015", QUIRK_ALIENWARE), + SND_PCI_QUIRK(0x1028, 0x0708, "Alienware 15 R2 2016", QUIRK_ALIENWARE), {} }; @@ -4018,7 +4019,7 @@ static int ca0132_build_controls(struct hda_codec *codec) /* * PCM */ -static struct hda_pcm_stream ca0132_pcm_analog_playback = { +static const struct hda_pcm_stream ca0132_pcm_analog_playback = { .substreams = 1, .channels_min = 2, .channels_max = 6, @@ -4029,7 +4030,7 @@ static struct hda_pcm_stream ca0132_pcm_analog_playback = { }, }; -static struct hda_pcm_stream ca0132_pcm_analog_capture = { +static const struct hda_pcm_stream ca0132_pcm_analog_capture = { .substreams = 1, .channels_min = 2, .channels_max = 2, @@ -4040,7 +4041,7 @@ static struct hda_pcm_stream ca0132_pcm_analog_capture = { }, }; -static struct hda_pcm_stream ca0132_pcm_digital_playback = { +static const struct hda_pcm_stream ca0132_pcm_digital_playback = { .substreams = 1, .channels_min = 2, .channels_max = 2, @@ -4052,7 +4053,7 @@ static struct hda_pcm_stream ca0132_pcm_digital_playback = { }, }; -static struct hda_pcm_stream ca0132_pcm_digital_capture = { +static const struct hda_pcm_stream ca0132_pcm_digital_capture = { .substreams = 1, .channels_min = 2, .channels_max = 2, @@ -4614,7 +4615,7 @@ static void ca0132_free(struct hda_codec *codec) kfree(codec->spec); } -static struct hda_codec_ops ca0132_patch_ops = { +static const struct hda_codec_ops ca0132_patch_ops = { .build_controls = ca0132_build_controls, .build_pcms = ca0132_build_pcms, .init = ca0132_init, diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 56fefbd85782..c15c51bea26d 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -261,6 +261,8 @@ enum { CXT_FIXUP_HP_530, CXT_FIXUP_CAP_MIX_AMP_5047, CXT_FIXUP_MUTE_LED_EAPD, + CXT_FIXUP_HP_SPECTRE, + CXT_FIXUP_HP_GATE_MIC, }; /* for hda_fixup_thinkpad_acpi() */ @@ -632,6 +634,17 @@ static void cxt_fixup_cap_mix_amp_5047(struct hda_codec *codec, (1 << AC_AMPCAP_MUTE_SHIFT)); } +static void cxt_fixup_hp_gate_mic_jack(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + /* the mic pin (0x19) doesn't give an unsolicited event; + * probe the mic pin together with the headphone pin (0x16) + */ + if (action == HDA_FIXUP_ACT_PROBE) + snd_hda_jack_set_gating_jack(codec, 0x19, 0x16); +} + /* ThinkPad X200 & co with cxt5051 */ static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = { { 0x16, 0x042140ff }, /* HP (seq# overridden) */ @@ -765,6 +778,18 @@ static const struct hda_fixup cxt_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = cxt_fixup_mute_led_eapd, }, + [CXT_FIXUP_HP_SPECTRE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + /* enable NID 0x1d for the speaker on top */ + { 0x1d, 0x91170111 }, + { } + } + }, + [CXT_FIXUP_HP_GATE_MIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = cxt_fixup_hp_gate_mic_jack, + }, }; static const struct snd_pci_quirk cxt5045_fixups[] = { @@ -814,6 +839,8 @@ 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(0x103c, 0x8174, "HP Spectre x360", CXT_FIXUP_HP_SPECTRE), + SND_PCI_QUIRK(0x103c, 0x8115, "HP Z1 Gen3", CXT_FIXUP_HP_GATE_MIC), 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), diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 56e5204ac9c1..cf9bc042fe96 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1485,7 +1485,7 @@ static void sync_eld_via_acomp(struct hda_codec *codec, mutex_lock(&per_pin->lock); eld->monitor_present = false; - size = snd_hdac_acomp_get_eld(&codec->core, per_pin->pin_nid, + size = snd_hdac_acomp_get_eld(&codec->core, per_pin->pin_nid, -1, &eld->monitor_present, eld->eld_buffer, ELD_MAX_SIZE); if (size > 0) { @@ -1744,7 +1744,8 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, /* Call sync_audio_rate to set the N/CTS/M manually if necessary */ /* Todo: add DP1.2 MST audio support later */ if (codec_has_acomp(codec)) - snd_hdac_sync_audio_rate(&codec->core, pin_nid, runtime->rate); + snd_hdac_sync_audio_rate(&codec->core, pin_nid, -1, + runtime->rate); non_pcm = check_non_pcm_per_cvt(codec, cvt_nid); mutex_lock(&per_pin->lock); @@ -2290,7 +2291,7 @@ static void haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg, snd_hda_codec_set_power_to_all(codec, fg, power_state); } -static void intel_pin_eld_notify(void *audio_ptr, int port) +static void intel_pin_eld_notify(void *audio_ptr, int port, int pipe) { struct hda_codec *codec = audio_ptr; int pin_nid; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 575cefd8cc4a..7d660ee1d5e8 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -420,7 +420,7 @@ static void alc_auto_setup_eapd(struct hda_codec *codec, bool on) } /* generic shutup callback; - * just turning off EPAD and a little pause for avoiding pop-noise + * just turning off EAPD and a little pause for avoiding pop-noise */ static void alc_eapd_shutup(struct hda_codec *codec) { @@ -2230,6 +2230,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_FIXUP_ASUS_W2JC), SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_FIXUP_EEE1601), SND_PCI_QUIRK(0x1043, 0x84bc, "ASUS ET2700", ALC887_FIXUP_ASUS_BASS), + SND_PCI_QUIRK(0x1043, 0x8691, "ASUS ROG Ranger VIII", ALC882_FIXUP_GPIO3), SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC889_FIXUP_VAIO_TT), SND_PCI_QUIRK(0x104d, 0x905a, "Sony Vaio Z", ALC882_FIXUP_NO_PRIMARY_HP), SND_PCI_QUIRK(0x104d, 0x9043, "Sony Vaio VGC-LN51JGB", ALC882_FIXUP_NO_PRIMARY_HP), @@ -5264,6 +5265,8 @@ static const struct hda_fixup alc269_fixups[] = { [ALC269_FIXUP_THINKPAD_ACPI] = { .type = HDA_FIXUP_FUNC, .v.func = hda_fixup_thinkpad_acpi, + .chained = true, + .chain_id = ALC269_FIXUP_SKU_IGNORE, }, [ALC269_FIXUP_DMIC_THINKPAD_ACPI] = { .type = HDA_FIXUP_FUNC, @@ -5806,6 +5809,11 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {0x14, 0x90170110}, \ {0x15, 0x0221401f} +#define ALC295_STANDARD_PINS \ + {0x12, 0xb7a60130}, \ + {0x14, 0x90170110}, \ + {0x21, 0x04211020} + #define ALC298_STANDARD_PINS \ {0x12, 0x90a60130}, \ {0x21, 0x03211020} @@ -5846,11 +5854,23 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x14, 0x90170120}, {0x21, 0x02211030}), SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x14, 0x90170110}, + {0x1b, 0x02011020}, + {0x21, 0x0221101f}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x14, 0x90170110}, + {0x1b, 0x01011020}, + {0x21, 0x0221101f}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, {0x14, 0x90170130}, {0x1b, 0x01014020}, {0x21, 0x0221103f}), SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, {0x14, 0x90170130}, + {0x1b, 0x01011020}, + {0x21, 0x0221103f}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x14, 0x90170130}, {0x1b, 0x02011020}, {0x21, 0x0221103f}), SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, @@ -5898,6 +5918,9 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x12, 0x90a60180}, {0x14, 0x90170120}, {0x21, 0x02211030}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x1b, 0x01011020}, + {0x21, 0x02211010}), SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, {0x12, 0x90a60160}, {0x14, 0x90170120}, @@ -5911,6 +5934,10 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x14, 0x90170120}, {0x21, 0x02211030}), SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x12, 0xb7a60130}, + {0x14, 0x90170110}, + {0x21, 0x02211020}), + SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, ALC256_STANDARD_PINS), SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC280_FIXUP_HP_GPIO4, {0x12, 0x90a60130}, @@ -6021,6 +6048,14 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, ALC292_STANDARD_PINS, {0x13, 0x90a60140}), + SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC295_STANDARD_PINS, + {0x17, 0x21014020}, + {0x18, 0x21a19030}), + SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC295_STANDARD_PINS, + {0x17, 0x21014040}, + {0x18, 0x21a19050}), SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, ALC298_STANDARD_PINS, {0x17, 0x90170110}), @@ -6530,6 +6565,30 @@ static void alc662_fixup_led_gpio1(struct hda_codec *codec, } } +static void alc662_usi_automute_hook(struct hda_codec *codec, + struct hda_jack_callback *jack) +{ + struct alc_spec *spec = codec->spec; + int vref; + msleep(200); + snd_hda_gen_hp_automute(codec, jack); + + vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0; + msleep(100); + snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + vref); +} + +static void alc662_fixup_usi_headset_mic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; + spec->gen.hp_automute_hook = alc662_usi_automute_hook; + } +} + static struct coef_fw alc668_coefs[] = { WRITE_COEF(0x01, 0xbebe), WRITE_COEF(0x02, 0xaaaa), WRITE_COEF(0x03, 0x0), WRITE_COEF(0x04, 0x0180), WRITE_COEF(0x06, 0x0), WRITE_COEF(0x07, 0x0f80), @@ -6594,6 +6653,9 @@ enum { ALC891_FIXUP_HEADSET_MODE, ALC891_FIXUP_DELL_MIC_NO_PRESENCE, ALC662_FIXUP_ACER_VERITON, + ALC892_FIXUP_ASROCK_MOBO, + ALC662_FIXUP_USI_FUNC, + ALC662_FIXUP_USI_HEADSET_MODE, }; static const struct hda_fixup alc662_fixups[] = { @@ -6870,6 +6932,28 @@ static const struct hda_fixup alc662_fixups[] = { { } } }, + [ALC892_FIXUP_ASROCK_MOBO] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x15, 0x40f000f0 }, /* disabled */ + { 0x16, 0x40f000f0 }, /* disabled */ + { } + } + }, + [ALC662_FIXUP_USI_FUNC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc662_fixup_usi_headset_mic, + }, + [ALC662_FIXUP_USI_HEADSET_MODE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x02a1913c }, /* use as headset mic, without its own jack detect */ + { 0x18, 0x01a1903d }, + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_USI_FUNC + }, }; static const struct snd_pci_quirk alc662_fixup_tbl[] = { @@ -6900,13 +6984,16 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x15a7, "ASUS UX51VZH", ALC662_FIXUP_BASS_16), SND_PCI_QUIRK(0x1043, 0x177d, "ASUS N551", ALC668_FIXUP_ASUS_Nx51), SND_PCI_QUIRK(0x1043, 0x17bd, "ASUS N751", ALC668_FIXUP_ASUS_Nx51), + SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71SL", ALC662_FIXUP_ASUS_MODE8), SND_PCI_QUIRK(0x1043, 0x1b73, "ASUS N55SF", ALC662_FIXUP_BASS_16), SND_PCI_QUIRK(0x1043, 0x1bf3, "ASUS N76VZ", ALC662_FIXUP_BASS_MODE4_CHMAP), SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT), SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_FIXUP_ASUS_MODE2), SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD), + SND_PCI_QUIRK(0x14cd, 0x5003, "USI", ALC662_FIXUP_USI_HEADSET_MODE), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD), + SND_PCI_QUIRK(0x1849, 0x5892, "ASRock B150M", ALC892_FIXUP_ASROCK_MOBO), SND_PCI_QUIRK(0x19da, 0xa130, "Zotac Z68", ALC662_FIXUP_ZOTAC_Z68), SND_PCI_QUIRK(0x1b0a, 0x01b8, "ACER Veriton", ALC662_FIXUP_ACER_VERITON), SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T), diff --git a/sound/pci/hda/thinkpad_helper.c b/sound/pci/hda/thinkpad_helper.c index f0955fd7a2e7..4d9d320a7971 100644 --- a/sound/pci/hda/thinkpad_helper.c +++ b/sound/pci/hda/thinkpad_helper.c @@ -13,7 +13,8 @@ static void (*old_vmaster_hook)(void *, int); static bool is_thinkpad(struct hda_codec *codec) { return (codec->core.subsystem_id >> 16 == 0x17aa) && - (acpi_dev_found("LEN0068") || acpi_dev_found("IBM0068")); + (acpi_dev_found("LEN0068") || acpi_dev_found("LEN0268") || + acpi_dev_found("IBM0068")); } static void update_tpacpi_mute_led(void *private_data, int enabled) @@ -62,7 +63,7 @@ static void hda_fixup_thinkpad_acpi(struct hda_codec *codec, removefunc = false; } if (led_set_func(TPACPI_LED_MICMUTE, false) >= 0) { - if (spec->num_adc_nids > 1) + if (spec->num_adc_nids > 1 && !spec->dyn_adc_switch) codec_dbg(codec, "Skipping micmute LED control due to several ADCs"); else { diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index 8ae3bb7975d1..b4aa4c1370a8 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -847,7 +847,7 @@ static int snd_ice1712_capture_close(struct snd_pcm_substream *substream) return 0; } -static struct snd_pcm_ops snd_ice1712_playback_ops = { +static const struct snd_pcm_ops snd_ice1712_playback_ops = { .open = snd_ice1712_playback_open, .close = snd_ice1712_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -858,7 +858,7 @@ static struct snd_pcm_ops snd_ice1712_playback_ops = { .pointer = snd_ice1712_playback_pointer, }; -static struct snd_pcm_ops snd_ice1712_playback_ds_ops = { +static const struct snd_pcm_ops snd_ice1712_playback_ds_ops = { .open = snd_ice1712_playback_ds_open, .close = snd_ice1712_playback_ds_close, .ioctl = snd_pcm_lib_ioctl, @@ -869,7 +869,7 @@ static struct snd_pcm_ops snd_ice1712_playback_ds_ops = { .pointer = snd_ice1712_playback_ds_pointer, }; -static struct snd_pcm_ops snd_ice1712_capture_ops = { +static const struct snd_pcm_ops snd_ice1712_capture_ops = { .open = snd_ice1712_capture_open, .close = snd_ice1712_capture_close, .ioctl = snd_pcm_lib_ioctl, @@ -1228,7 +1228,7 @@ static int snd_ice1712_capture_pro_close(struct snd_pcm_substream *substream) return 0; } -static struct snd_pcm_ops snd_ice1712_playback_pro_ops = { +static const struct snd_pcm_ops snd_ice1712_playback_pro_ops = { .open = snd_ice1712_playback_pro_open, .close = snd_ice1712_playback_pro_close, .ioctl = snd_pcm_lib_ioctl, @@ -1239,7 +1239,7 @@ static struct snd_pcm_ops snd_ice1712_playback_pro_ops = { .pointer = snd_ice1712_playback_pro_pointer, }; -static struct snd_pcm_ops snd_ice1712_capture_pro_ops = { +static const struct snd_pcm_ops snd_ice1712_capture_pro_ops = { .open = snd_ice1712_capture_pro_open, .close = snd_ice1712_capture_pro_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index 0b22c00642bb..e5c52ed9b674 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -620,9 +620,7 @@ static const unsigned int stdclock_rate_list[16] = { static unsigned int stdclock_get_rate(struct snd_ice1712 *ice) { - unsigned int rate; - rate = stdclock_rate_list[inb(ICEMT1724(ice, RATE)) & 15]; - return rate; + return stdclock_rate_list[inb(ICEMT1724(ice, RATE)) & 15]; } static void stdclock_set_rate(struct snd_ice1712 *ice, unsigned int rate) @@ -1113,7 +1111,7 @@ static int snd_vt1724_capture_pro_close(struct snd_pcm_substream *substream) return 0; } -static struct snd_pcm_ops snd_vt1724_playback_pro_ops = { +static const struct snd_pcm_ops snd_vt1724_playback_pro_ops = { .open = snd_vt1724_playback_pro_open, .close = snd_vt1724_playback_pro_close, .ioctl = snd_pcm_lib_ioctl, @@ -1124,7 +1122,7 @@ static struct snd_pcm_ops snd_vt1724_playback_pro_ops = { .pointer = snd_vt1724_playback_pro_pointer, }; -static struct snd_pcm_ops snd_vt1724_capture_pro_ops = { +static const struct snd_pcm_ops snd_vt1724_capture_pro_ops = { .open = snd_vt1724_capture_pro_open, .close = snd_vt1724_capture_pro_close, .ioctl = snd_pcm_lib_ioctl, @@ -1292,7 +1290,7 @@ static int snd_vt1724_capture_spdif_close(struct snd_pcm_substream *substream) return 0; } -static struct snd_pcm_ops snd_vt1724_playback_spdif_ops = { +static const struct snd_pcm_ops snd_vt1724_playback_spdif_ops = { .open = snd_vt1724_playback_spdif_open, .close = snd_vt1724_playback_spdif_close, .ioctl = snd_pcm_lib_ioctl, @@ -1303,7 +1301,7 @@ static struct snd_pcm_ops snd_vt1724_playback_spdif_ops = { .pointer = snd_vt1724_pcm_pointer, }; -static struct snd_pcm_ops snd_vt1724_capture_spdif_ops = { +static const struct snd_pcm_ops snd_vt1724_capture_spdif_ops = { .open = snd_vt1724_capture_spdif_open, .close = snd_vt1724_capture_spdif_close, .ioctl = snd_pcm_lib_ioctl, @@ -1437,7 +1435,7 @@ static int snd_vt1724_playback_indep_close(struct snd_pcm_substream *substream) return 0; } -static struct snd_pcm_ops snd_vt1724_playback_indep_ops = { +static const struct snd_pcm_ops snd_vt1724_playback_indep_ops = { .open = snd_vt1724_playback_indep_open, .close = snd_vt1724_playback_indep_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index 9e1ad119a3ce..565f7f55c3ca 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -1681,7 +1681,7 @@ static int snd_korg1212_capture_copy(struct snd_pcm_substream *substream, return snd_korg1212_copy_to(korg1212, dst, pos, count, 0, korg1212->channels * 2); } -static struct snd_pcm_ops snd_korg1212_playback_ops = { +static const struct snd_pcm_ops snd_korg1212_playback_ops = { .open = snd_korg1212_playback_open, .close = snd_korg1212_playback_close, .ioctl = snd_korg1212_ioctl, @@ -1693,7 +1693,7 @@ static struct snd_pcm_ops snd_korg1212_playback_ops = { .silence = snd_korg1212_playback_silence, }; -static struct snd_pcm_ops snd_korg1212_capture_ops = { +static const struct snd_pcm_ops snd_korg1212_capture_ops = { .open = snd_korg1212_capture_open, .close = snd_korg1212_capture_close, .ioctl = snd_korg1212_ioctl, diff --git a/sound/pci/lola/lola_pcm.c b/sound/pci/lola/lola_pcm.c index 3bd6985430e8..1268ba329016 100644 --- a/sound/pci/lola/lola_pcm.c +++ b/sound/pci/lola/lola_pcm.c @@ -586,7 +586,7 @@ void lola_pcm_update(struct lola *chip, struct lola_pcm *pcm, unsigned int bits) } } -static struct snd_pcm_ops lola_pcm_ops = { +static const struct snd_pcm_ops lola_pcm_ops = { .open = lola_pcm_open, .close = lola_pcm_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c index 8b8e2e54fba3..c0f0c349c3ec 100644 --- a/sound/pci/lx6464es/lx6464es.c +++ b/sound/pci/lx6464es/lx6464es.c @@ -804,7 +804,7 @@ mac_ready: return err; } -static struct snd_pcm_ops lx_ops_playback = { +static const struct snd_pcm_ops lx_ops_playback = { .open = lx_pcm_open, .close = lx_pcm_close, .ioctl = snd_pcm_lib_ioctl, @@ -815,7 +815,7 @@ static struct snd_pcm_ops lx_ops_playback = { .pointer = lx_pcm_stream_pointer, }; -static struct snd_pcm_ops lx_ops_capture = { +static const struct snd_pcm_ops lx_ops_capture = { .open = lx_pcm_open, .close = lx_pcm_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 17ae92613de4..cafea6dc5c01 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -1834,7 +1834,7 @@ snd_m3_capture_close(struct snd_pcm_substream *subs) * create pcm instance */ -static struct snd_pcm_ops snd_m3_playback_ops = { +static const struct snd_pcm_ops snd_m3_playback_ops = { .open = snd_m3_playback_open, .close = snd_m3_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1845,7 +1845,7 @@ static struct snd_pcm_ops snd_m3_playback_ops = { .pointer = snd_m3_pcm_pointer, }; -static struct snd_pcm_ops snd_m3_capture_ops = { +static const struct snd_pcm_ops snd_m3_capture_ops = { .open = snd_m3_capture_open, .close = snd_m3_capture_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c index 25c0ddd3a53b..80d439944cb5 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -922,7 +922,7 @@ static snd_pcm_uframes_t snd_mixart_stream_pointer(struct snd_pcm_substream *sub -static struct snd_pcm_ops snd_mixart_playback_ops = { +static const struct snd_pcm_ops snd_mixart_playback_ops = { .open = snd_mixart_playback_open, .close = snd_mixart_close, .ioctl = snd_pcm_lib_ioctl, @@ -933,7 +933,7 @@ static struct snd_pcm_ops snd_mixart_playback_ops = { .pointer = snd_mixart_stream_pointer, }; -static struct snd_pcm_ops snd_mixart_capture_ops = { +static const struct snd_pcm_ops snd_mixart_capture_ops = { .open = snd_mixart_capture_open, .close = snd_mixart_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 4735e27cc773..103fe311e5a9 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -902,7 +902,7 @@ snd_nm256_capture_close(struct snd_pcm_substream *substream) /* * create a pcm instance */ -static struct snd_pcm_ops snd_nm256_playback_ops = { +static const struct snd_pcm_ops snd_nm256_playback_ops = { .open = snd_nm256_playback_open, .close = snd_nm256_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -917,7 +917,7 @@ static struct snd_pcm_ops snd_nm256_playback_ops = { .mmap = snd_pcm_lib_mmap_iomem, }; -static struct snd_pcm_ops snd_nm256_capture_ops = { +static const struct snd_pcm_ops snd_nm256_capture_ops = { .open = snd_nm256_capture_open, .close = snd_nm256_capture_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c index aa2ebd1d6d12..042a2439fea5 100644 --- a/sound/pci/oxygen/oxygen_pcm.c +++ b/sound/pci/oxygen/oxygen_pcm.c @@ -631,7 +631,7 @@ static snd_pcm_uframes_t oxygen_pointer(struct snd_pcm_substream *substream) return bytes_to_frames(runtime, curr_addr - (u32)runtime->dma_addr); } -static struct snd_pcm_ops oxygen_rec_a_ops = { +static const struct snd_pcm_ops oxygen_rec_a_ops = { .open = oxygen_rec_a_open, .close = oxygen_close, .ioctl = snd_pcm_lib_ioctl, @@ -642,7 +642,7 @@ static struct snd_pcm_ops oxygen_rec_a_ops = { .pointer = oxygen_pointer, }; -static struct snd_pcm_ops oxygen_rec_b_ops = { +static const struct snd_pcm_ops oxygen_rec_b_ops = { .open = oxygen_rec_b_open, .close = oxygen_close, .ioctl = snd_pcm_lib_ioctl, @@ -653,7 +653,7 @@ static struct snd_pcm_ops oxygen_rec_b_ops = { .pointer = oxygen_pointer, }; -static struct snd_pcm_ops oxygen_rec_c_ops = { +static const struct snd_pcm_ops oxygen_rec_c_ops = { .open = oxygen_rec_c_open, .close = oxygen_close, .ioctl = snd_pcm_lib_ioctl, @@ -664,7 +664,7 @@ static struct snd_pcm_ops oxygen_rec_c_ops = { .pointer = oxygen_pointer, }; -static struct snd_pcm_ops oxygen_spdif_ops = { +static const struct snd_pcm_ops oxygen_spdif_ops = { .open = oxygen_spdif_open, .close = oxygen_close, .ioctl = snd_pcm_lib_ioctl, @@ -675,7 +675,7 @@ static struct snd_pcm_ops oxygen_spdif_ops = { .pointer = oxygen_pointer, }; -static struct snd_pcm_ops oxygen_multich_ops = { +static const struct snd_pcm_ops oxygen_multich_ops = { .open = oxygen_multich_open, .close = oxygen_close, .ioctl = snd_pcm_lib_ioctl, @@ -686,7 +686,7 @@ static struct snd_pcm_ops oxygen_multich_ops = { .pointer = oxygen_pointer, }; -static struct snd_pcm_ops oxygen_ac97_ops = { +static const struct snd_pcm_ops oxygen_ac97_ops = { .open = oxygen_ac97_open, .close = oxygen_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c index 9293235281dc..bb7eee9d0c2b 100644 --- a/sound/pci/pcxhr/pcxhr.c +++ b/sound/pci/pcxhr/pcxhr.c @@ -1146,7 +1146,7 @@ static snd_pcm_uframes_t pcxhr_stream_pointer(struct snd_pcm_substream *subs) } -static struct snd_pcm_ops pcxhr_ops = { +static const struct snd_pcm_ops pcxhr_ops = { .open = pcxhr_open, .close = pcxhr_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index 067a91207d8e..19c9df6b0f3d 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -110,7 +110,7 @@ #include <sound/opl3.h> #include <sound/initval.h> -#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#if IS_REACHABLE(CONFIG_GAMEPORT) #define SUPPORT_JOYSTICK 1 #endif @@ -644,7 +644,7 @@ static struct lbuspath lbus_play_paths[] = { .mono = lbus_play_mono3, }, }; -static struct lbuspath lbus_rec_path = { +static const struct lbuspath lbus_rec_path = { .noconv = lbus_rec_noconv1, .stereo = lbus_rec_stereo1, .mono = lbus_rec_mono1, @@ -1669,7 +1669,7 @@ static int snd_riptide_capture_close(struct snd_pcm_substream *substream) return 0; } -static struct snd_pcm_ops snd_riptide_playback_ops = { +static const struct snd_pcm_ops snd_riptide_playback_ops = { .open = snd_riptide_playback_open, .close = snd_riptide_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1680,7 +1680,7 @@ static struct snd_pcm_ops snd_riptide_playback_ops = { .trigger = snd_riptide_trigger, .pointer = snd_riptide_pointer, }; -static struct snd_pcm_ops snd_riptide_capture_ops = { +static const struct snd_pcm_ops snd_riptide_capture_ops = { .open = snd_riptide_capture_open, .close = snd_riptide_capture_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index cd94ac548ba3..96d15db65dfd 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -1196,7 +1196,7 @@ snd_rme32_capture_fd_pointer(struct snd_pcm_substream *substream) } /* for halfduplex mode */ -static struct snd_pcm_ops snd_rme32_playback_spdif_ops = { +static const struct snd_pcm_ops snd_rme32_playback_spdif_ops = { .open = snd_rme32_playback_spdif_open, .close = snd_rme32_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1210,7 +1210,7 @@ static struct snd_pcm_ops snd_rme32_playback_spdif_ops = { .mmap = snd_pcm_lib_mmap_iomem, }; -static struct snd_pcm_ops snd_rme32_capture_spdif_ops = { +static const struct snd_pcm_ops snd_rme32_capture_spdif_ops = { .open = snd_rme32_capture_spdif_open, .close = snd_rme32_capture_close, .ioctl = snd_pcm_lib_ioctl, @@ -1223,7 +1223,7 @@ static struct snd_pcm_ops snd_rme32_capture_spdif_ops = { .mmap = snd_pcm_lib_mmap_iomem, }; -static struct snd_pcm_ops snd_rme32_playback_adat_ops = { +static const struct snd_pcm_ops snd_rme32_playback_adat_ops = { .open = snd_rme32_playback_adat_open, .close = snd_rme32_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1236,7 +1236,7 @@ static struct snd_pcm_ops snd_rme32_playback_adat_ops = { .mmap = snd_pcm_lib_mmap_iomem, }; -static struct snd_pcm_ops snd_rme32_capture_adat_ops = { +static const struct snd_pcm_ops snd_rme32_capture_adat_ops = { .open = snd_rme32_capture_adat_open, .close = snd_rme32_capture_close, .ioctl = snd_pcm_lib_ioctl, @@ -1249,7 +1249,7 @@ static struct snd_pcm_ops snd_rme32_capture_adat_ops = { }; /* for fullduplex mode */ -static struct snd_pcm_ops snd_rme32_playback_spdif_fd_ops = { +static const struct snd_pcm_ops snd_rme32_playback_spdif_fd_ops = { .open = snd_rme32_playback_spdif_open, .close = snd_rme32_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1261,7 +1261,7 @@ static struct snd_pcm_ops snd_rme32_playback_spdif_fd_ops = { .ack = snd_rme32_playback_fd_ack, }; -static struct snd_pcm_ops snd_rme32_capture_spdif_fd_ops = { +static const struct snd_pcm_ops snd_rme32_capture_spdif_fd_ops = { .open = snd_rme32_capture_spdif_open, .close = snd_rme32_capture_close, .ioctl = snd_pcm_lib_ioctl, @@ -1273,7 +1273,7 @@ static struct snd_pcm_ops snd_rme32_capture_spdif_fd_ops = { .ack = snd_rme32_capture_fd_ack, }; -static struct snd_pcm_ops snd_rme32_playback_adat_fd_ops = { +static const struct snd_pcm_ops snd_rme32_playback_adat_fd_ops = { .open = snd_rme32_playback_adat_open, .close = snd_rme32_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1284,7 +1284,7 @@ static struct snd_pcm_ops snd_rme32_playback_adat_fd_ops = { .ack = snd_rme32_playback_fd_ack, }; -static struct snd_pcm_ops snd_rme32_capture_adat_fd_ops = { +static const struct snd_pcm_ops snd_rme32_capture_adat_fd_ops = { .open = snd_rme32_capture_adat_open, .close = snd_rme32_capture_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 41c31db65039..05b9da30990d 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -1505,7 +1505,7 @@ snd_rme96_capture_pointer(struct snd_pcm_substream *substream) return snd_rme96_capture_ptr(rme96); } -static struct snd_pcm_ops snd_rme96_playback_spdif_ops = { +static const struct snd_pcm_ops snd_rme96_playback_spdif_ops = { .open = snd_rme96_playback_spdif_open, .close = snd_rme96_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1518,7 +1518,7 @@ static struct snd_pcm_ops snd_rme96_playback_spdif_ops = { .mmap = snd_pcm_lib_mmap_iomem, }; -static struct snd_pcm_ops snd_rme96_capture_spdif_ops = { +static const struct snd_pcm_ops snd_rme96_capture_spdif_ops = { .open = snd_rme96_capture_spdif_open, .close = snd_rme96_capture_close, .ioctl = snd_pcm_lib_ioctl, @@ -1530,7 +1530,7 @@ static struct snd_pcm_ops snd_rme96_capture_spdif_ops = { .mmap = snd_pcm_lib_mmap_iomem, }; -static struct snd_pcm_ops snd_rme96_playback_adat_ops = { +static const struct snd_pcm_ops snd_rme96_playback_adat_ops = { .open = snd_rme96_playback_adat_open, .close = snd_rme96_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1543,7 +1543,7 @@ static struct snd_pcm_ops snd_rme96_playback_adat_ops = { .mmap = snd_pcm_lib_mmap_iomem, }; -static struct snd_pcm_ops snd_rme96_capture_adat_ops = { +static const struct snd_pcm_ops snd_rme96_capture_adat_ops = { .open = snd_rme96_capture_adat_open, .close = snd_rme96_capture_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 7c8941b8b2de..b94fc6357139 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -4861,7 +4861,7 @@ static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigne return 0; } -static struct snd_pcm_ops snd_hdsp_playback_ops = { +static const struct snd_pcm_ops snd_hdsp_playback_ops = { .open = snd_hdsp_playback_open, .close = snd_hdsp_playback_release, .ioctl = snd_hdsp_ioctl, @@ -4873,7 +4873,7 @@ static struct snd_pcm_ops snd_hdsp_playback_ops = { .silence = snd_hdsp_hw_silence, }; -static struct snd_pcm_ops snd_hdsp_capture_ops = { +static const struct snd_pcm_ops snd_hdsp_capture_ops = { .open = snd_hdsp_capture_open, .close = snd_hdsp_capture_release, .ioctl = snd_hdsp_ioctl, diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index a4a999a0317e..14bbf55c1ef9 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -1666,7 +1666,7 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally) HDSPM_AUTOSYNC_FROM_NONE) { dev_warn(hdspm->card->dev, - "Detected no Externel Sync\n"); + "Detected no External Sync\n"); not_set = 1; } else if (rate != external_freq) { @@ -6361,7 +6361,7 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, return 0; } -static struct snd_pcm_ops snd_hdspm_ops = { +static const struct snd_pcm_ops snd_hdspm_ops = { .open = snd_hdspm_open, .close = snd_hdspm_release, .ioctl = snd_hdspm_ioctl, diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index fdbc0aa2776a..55172c689991 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -2368,7 +2368,7 @@ static int snd_rme9652_capture_release(struct snd_pcm_substream *substream) return 0; } -static struct snd_pcm_ops snd_rme9652_playback_ops = { +static const struct snd_pcm_ops snd_rme9652_playback_ops = { .open = snd_rme9652_playback_open, .close = snd_rme9652_playback_release, .ioctl = snd_rme9652_ioctl, @@ -2380,7 +2380,7 @@ static struct snd_pcm_ops snd_rme9652_playback_ops = { .silence = snd_rme9652_hw_silence, }; -static struct snd_pcm_ops snd_rme9652_capture_ops = { +static const struct snd_pcm_ops snd_rme9652_capture_ops = { .open = snd_rme9652_capture_open, .close = snd_rme9652_capture_release, .ioctl = snd_rme9652_ioctl, diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c index 1b6fad7d4d56..a6aa48c5b969 100644 --- a/sound/pci/sonicvibes.c +++ b/sound/pci/sonicvibes.c @@ -45,7 +45,7 @@ MODULE_DESCRIPTION("S3 SonicVibes PCI"); MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("{{S3,SonicVibes PCI}}"); -#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#if IS_REACHABLE(CONFIG_GAMEPORT) #define SUPPORT_JOYSTICK 1 #endif @@ -857,7 +857,7 @@ static int snd_sonicvibes_capture_close(struct snd_pcm_substream *substream) return 0; } -static struct snd_pcm_ops snd_sonicvibes_playback_ops = { +static const struct snd_pcm_ops snd_sonicvibes_playback_ops = { .open = snd_sonicvibes_playback_open, .close = snd_sonicvibes_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -868,7 +868,7 @@ static struct snd_pcm_ops snd_sonicvibes_playback_ops = { .pointer = snd_sonicvibes_playback_pointer, }; -static struct snd_pcm_ops snd_sonicvibes_capture_ops = { +static const struct snd_pcm_ops snd_sonicvibes_capture_ops = { .open = snd_sonicvibes_capture_open, .close = snd_sonicvibes_capture_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index 599d2b7eb5b8..92ad2d7a6bf8 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -2070,7 +2070,7 @@ static int snd_trident_foldback_close(struct snd_pcm_substream *substream) PCM operations ---------------------------------------------------------------------------*/ -static struct snd_pcm_ops snd_trident_playback_ops = { +static const struct snd_pcm_ops snd_trident_playback_ops = { .open = snd_trident_playback_open, .close = snd_trident_playback_close, .ioctl = snd_trident_ioctl, @@ -2081,7 +2081,7 @@ static struct snd_pcm_ops snd_trident_playback_ops = { .pointer = snd_trident_playback_pointer, }; -static struct snd_pcm_ops snd_trident_nx_playback_ops = { +static const struct snd_pcm_ops snd_trident_nx_playback_ops = { .open = snd_trident_playback_open, .close = snd_trident_playback_close, .ioctl = snd_trident_ioctl, @@ -2115,7 +2115,7 @@ static struct snd_pcm_ops snd_trident_si7018_capture_ops = { .pointer = snd_trident_playback_pointer, }; -static struct snd_pcm_ops snd_trident_foldback_ops = { +static const struct snd_pcm_ops snd_trident_foldback_ops = { .open = snd_trident_foldback_open, .close = snd_trident_foldback_close, .ioctl = snd_trident_ioctl, @@ -2126,7 +2126,7 @@ static struct snd_pcm_ops snd_trident_foldback_ops = { .pointer = snd_trident_playback_pointer, }; -static struct snd_pcm_ops snd_trident_nx_foldback_ops = { +static const struct snd_pcm_ops snd_trident_nx_foldback_ops = { .open = snd_trident_foldback_open, .close = snd_trident_foldback_close, .ioctl = snd_trident_ioctl, @@ -2138,7 +2138,7 @@ static struct snd_pcm_ops snd_trident_nx_foldback_ops = { .page = snd_pcm_sgbuf_ops_page, }; -static struct snd_pcm_ops snd_trident_spdif_ops = { +static const struct snd_pcm_ops snd_trident_spdif_ops = { .open = snd_trident_spdif_open, .close = snd_trident_spdif_close, .ioctl = snd_trident_ioctl, @@ -2149,7 +2149,7 @@ static struct snd_pcm_ops snd_trident_spdif_ops = { .pointer = snd_trident_spdif_pointer, }; -static struct snd_pcm_ops snd_trident_spdif_7018_ops = { +static const struct snd_pcm_ops snd_trident_spdif_7018_ops = { .open = snd_trident_spdif_open, .close = snd_trident_spdif_close, .ioctl = snd_trident_ioctl, @@ -3120,7 +3120,7 @@ static int snd_trident_mixer(struct snd_trident *trident, int pcm_spdif_device) * gameport interface */ -#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#if IS_REACHABLE(CONFIG_GAMEPORT) static unsigned char snd_trident_gameport_read(struct gameport *gameport) { diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index 3dd038bdb204..2d8c14e3f8d2 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -72,7 +72,7 @@ MODULE_DESCRIPTION("VIA VT82xx audio"); MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("{{VIA,VT82C686A/B/C,pci},{VIA,VT8233A/C,8235}}"); -#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#if IS_REACHABLE(CONFIG_GAMEPORT) #define SUPPORT_JOYSTICK 1 #endif @@ -1366,7 +1366,7 @@ static int snd_via8233_playback_close(struct snd_pcm_substream *substream) /* via686 playback callbacks */ -static struct snd_pcm_ops snd_via686_playback_ops = { +static const struct snd_pcm_ops snd_via686_playback_ops = { .open = snd_via686_playback_open, .close = snd_via82xx_pcm_close, .ioctl = snd_pcm_lib_ioctl, @@ -1379,7 +1379,7 @@ static struct snd_pcm_ops snd_via686_playback_ops = { }; /* via686 capture callbacks */ -static struct snd_pcm_ops snd_via686_capture_ops = { +static const struct snd_pcm_ops snd_via686_capture_ops = { .open = snd_via82xx_capture_open, .close = snd_via82xx_pcm_close, .ioctl = snd_pcm_lib_ioctl, @@ -1392,7 +1392,7 @@ static struct snd_pcm_ops snd_via686_capture_ops = { }; /* via823x DSX playback callbacks */ -static struct snd_pcm_ops snd_via8233_playback_ops = { +static const struct snd_pcm_ops snd_via8233_playback_ops = { .open = snd_via8233_playback_open, .close = snd_via8233_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1405,7 +1405,7 @@ static struct snd_pcm_ops snd_via8233_playback_ops = { }; /* via823x multi-channel playback callbacks */ -static struct snd_pcm_ops snd_via8233_multi_ops = { +static const struct snd_pcm_ops snd_via8233_multi_ops = { .open = snd_via8233_multi_open, .close = snd_via82xx_pcm_close, .ioctl = snd_pcm_lib_ioctl, @@ -1418,7 +1418,7 @@ static struct snd_pcm_ops snd_via8233_multi_ops = { }; /* via823x capture callbacks */ -static struct snd_pcm_ops snd_via8233_capture_ops = { +static const struct snd_pcm_ops snd_via8233_capture_ops = { .open = snd_via82xx_capture_open, .close = snd_via82xx_pcm_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index 99b9137bc677..2f6d40f10618 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -804,7 +804,7 @@ static int snd_via82xx_pcm_close(struct snd_pcm_substream *substream) /* via686 playback callbacks */ -static struct snd_pcm_ops snd_via686_playback_ops = { +static const struct snd_pcm_ops snd_via686_playback_ops = { .open = snd_via82xx_playback_open, .close = snd_via82xx_pcm_close, .ioctl = snd_pcm_lib_ioctl, @@ -817,7 +817,7 @@ static struct snd_pcm_ops snd_via686_playback_ops = { }; /* via686 capture callbacks */ -static struct snd_pcm_ops snd_via686_capture_ops = { +static const struct snd_pcm_ops snd_via686_capture_ops = { .open = snd_via82xx_capture_open, .close = snd_via82xx_pcm_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/pci/ymfpci/ymfpci.h b/sound/pci/ymfpci/ymfpci.h index 149d4cb46998..aa9bb065f385 100644 --- a/sound/pci/ymfpci/ymfpci.h +++ b/sound/pci/ymfpci/ymfpci.h @@ -176,7 +176,7 @@ #define YMFPCI_LEGACY2_IMOD (1 << 15) /* legacy IRQ mode */ /* SIEN:IMOD 0:0 = legacy irq, 0:1 = INTA, 1:0 = serialized IRQ */ -#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#if IS_REACHABLE(CONFIG_GAMEPORT) #define SUPPORT_JOYSTICK #endif diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index 4c26076dbf78..ffee284898b3 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -1123,7 +1123,7 @@ static int snd_ymfpci_capture_close(struct snd_pcm_substream *substream) return 0; } -static struct snd_pcm_ops snd_ymfpci_playback_ops = { +static const struct snd_pcm_ops snd_ymfpci_playback_ops = { .open = snd_ymfpci_playback_open, .close = snd_ymfpci_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1134,7 +1134,7 @@ static struct snd_pcm_ops snd_ymfpci_playback_ops = { .pointer = snd_ymfpci_playback_pointer, }; -static struct snd_pcm_ops snd_ymfpci_capture_rec_ops = { +static const struct snd_pcm_ops snd_ymfpci_capture_rec_ops = { .open = snd_ymfpci_capture_rec_open, .close = snd_ymfpci_capture_close, .ioctl = snd_pcm_lib_ioctl, @@ -1169,7 +1169,7 @@ int snd_ymfpci_pcm(struct snd_ymfpci *chip, int device) snd_pcm_std_chmaps, 2, 0, NULL); } -static struct snd_pcm_ops snd_ymfpci_capture_ac97_ops = { +static const struct snd_pcm_ops snd_ymfpci_capture_ac97_ops = { .open = snd_ymfpci_capture_ac97_open, .close = snd_ymfpci_capture_close, .ioctl = snd_pcm_lib_ioctl, @@ -1203,7 +1203,7 @@ int snd_ymfpci_pcm2(struct snd_ymfpci *chip, int device) return 0; } -static struct snd_pcm_ops snd_ymfpci_playback_spdif_ops = { +static const struct snd_pcm_ops snd_ymfpci_playback_spdif_ops = { .open = snd_ymfpci_playback_spdif_open, .close = snd_ymfpci_playback_spdif_close, .ioctl = snd_pcm_lib_ioctl, @@ -1236,7 +1236,7 @@ int snd_ymfpci_pcm_spdif(struct snd_ymfpci *chip, int device) return 0; } -static struct snd_pcm_ops snd_ymfpci_playback_4ch_ops = { +static const struct snd_pcm_ops snd_ymfpci_playback_4ch_ops = { .open = snd_ymfpci_playback_4ch_open, .close = snd_ymfpci_playback_4ch_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c index 368242519279..b84d7d34f188 100644 --- a/sound/ppc/snd_ps3.c +++ b/sound/ppc/snd_ps3.c @@ -564,9 +564,7 @@ static int snd_ps3_pcm_hw_params(struct snd_pcm_substream *substream, static int snd_ps3_pcm_hw_free(struct snd_pcm_substream *substream) { - int ret; - ret = snd_pcm_lib_free_pages(substream); - return ret; + return snd_pcm_lib_free_pages(substream); }; static int snd_ps3_delay_to_bytes(struct snd_pcm_substream *substream, diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c index c8fafba218a5..58ee8089bbf9 100644 --- a/sound/ppc/tumbler.c +++ b/sound/ppc/tumbler.c @@ -1303,19 +1303,19 @@ static int tumbler_init(struct snd_pmac *chip) &mix->line_mute, 1); irq = tumbler_find_device("headphone-detect", NULL, &mix->hp_detect, 0); - if (irq <= NO_IRQ) + if (irq <= 0) irq = tumbler_find_device("headphone-detect", NULL, &mix->hp_detect, 1); - if (irq <= NO_IRQ) + if (irq <= 0) irq = tumbler_find_device("keywest-gpio15", NULL, &mix->hp_detect, 1); mix->headphone_irq = irq; irq = tumbler_find_device("line-output-detect", NULL, &mix->line_detect, 0); - if (irq <= NO_IRQ) + if (irq <= 0) irq = tumbler_find_device("line-output-detect", NULL, &mix->line_detect, 1); - if (IS_G4DA && irq <= NO_IRQ) + if (IS_G4DA && irq <= 0) irq = tumbler_find_device("keywest-gpio16", NULL, &mix->line_detect, 1); mix->lineout_irq = irq; diff --git a/sound/sh/sh_dac_audio.c b/sound/sh/sh_dac_audio.c index abf9c0cab1e2..461b310c7872 100644 --- a/sound/sh/sh_dac_audio.c +++ b/sound/sh/sh_dac_audio.c @@ -87,7 +87,7 @@ static void dac_audio_reset(struct snd_sh_dac *chip) static void dac_audio_set_rate(struct snd_sh_dac *chip) { - chip->wakeups_per_second = ktime_set(0, 1000000000 / chip->rate); + chip->wakeups_per_second = 1000000000 / chip->rate; } diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c index d1fb035f44db..504c7cd7f58a 100644 --- a/sound/soc/amd/acp-pcm-dma.c +++ b/sound/soc/amd/acp-pcm-dma.c @@ -897,7 +897,7 @@ static int acp_dma_close(struct snd_pcm_substream *substream) return 0; } -static struct snd_pcm_ops acp_dma_ops = { +static const struct snd_pcm_ops acp_dma_ops = { .open = acp_dma_open, .close = acp_dma_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index 22aec9a1e9a4..4a56f3dfba51 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -78,4 +78,14 @@ config SND_ATMEL_SOC_PDMIC help Say Y if you want to add support for Atmel ASoC driver for boards using PDMIC. + +config SND_ATMEL_SOC_TSE850_PCM5142 + tristate "ASoC driver for the Axentia TSE-850" + depends on ARCH_AT91 && OF + depends on ATMEL_SSC && I2C + select SND_ATMEL_SOC_SSC_DMA + select SND_SOC_PCM512x_I2C + help + Say Y if you want to add support for the ASoC driver for the + Axentia TSE-850 with a PCM5142 codec. endif diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile index a2b127bd9c87..67e10cbd4ed7 100644 --- a/sound/soc/atmel/Makefile +++ b/sound/soc/atmel/Makefile @@ -13,9 +13,11 @@ snd-atmel-soc-wm8904-objs := atmel_wm8904.o snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o snd-atmel-soc-classd-objs := atmel-classd.o snd-atmel-soc-pdmic-objs := atmel-pdmic.o +snd-atmel-soc-tse850-pcm5142-objs := tse850-pcm5142.o obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o obj-$(CONFIG_SND_ATMEL_SOC_CLASSD) += snd-atmel-soc-classd.o obj-$(CONFIG_SND_ATMEL_SOC_PDMIC) += snd-atmel-soc-pdmic.o +obj-$(CONFIG_SND_ATMEL_SOC_TSE850_PCM5142) += snd-atmel-soc-tse850-pcm5142.o diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c index 6d9b8b44e2da..89ac5f5a93eb 100644 --- a/sound/soc/atmel/atmel-classd.c +++ b/sound/soc/atmel/atmel-classd.c @@ -308,9 +308,11 @@ static struct regmap *atmel_classd_codec_get_remap(struct device *dev) static struct snd_soc_codec_driver soc_codec_dev_classd = { .probe = atmel_classd_codec_probe, - .controls = atmel_classd_snd_controls, - .num_controls = ARRAY_SIZE(atmel_classd_snd_controls), .get_regmap = atmel_classd_codec_get_remap, + .component_driver = { + .controls = atmel_classd_snd_controls, + .num_controls = ARRAY_SIZE(atmel_classd_snd_controls), + }, }; /* codec dai component */ diff --git a/sound/soc/atmel/atmel-pcm-pdc.c b/sound/soc/atmel/atmel-pcm-pdc.c index da861b44413f..91b7069c3499 100644 --- a/sound/soc/atmel/atmel-pcm-pdc.c +++ b/sound/soc/atmel/atmel-pcm-pdc.c @@ -381,7 +381,7 @@ static int atmel_pcm_close(struct snd_pcm_substream *substream) return 0; } -static struct snd_pcm_ops atmel_pcm_ops = { +static const struct snd_pcm_ops atmel_pcm_ops = { .open = atmel_pcm_open, .close = atmel_pcm_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/soc/atmel/atmel-pdmic.c b/sound/soc/atmel/atmel-pdmic.c index 5f56da60c92f..c917df7715d1 100644 --- a/sound/soc/atmel/atmel-pdmic.c +++ b/sound/soc/atmel/atmel-pdmic.c @@ -80,7 +80,7 @@ static struct atmel_pdmic_pdata *atmel_pdmic_dt_init(struct device *dev) if (pdata->mic_max_freq < pdata->mic_min_freq) { dev_err(dev, - "mic-max-freq should not less than mic-min-freq\n"); + "mic-max-freq should not be less than mic-min-freq\n"); return ERR_PTR(-EINVAL); } @@ -115,8 +115,10 @@ static int atmel_pdmic_cpu_dai_startup(struct snd_pcm_substream *substream, return ret; ret = clk_prepare_enable(dd->pclk); - if (ret) + if (ret) { + clk_disable_unprepare(dd->gclk); return ret; + } /* Clear all bits in the Control Register(PDMIC_CR) */ regmap_write(dd->regmap, PDMIC_CR, 0); @@ -283,7 +285,7 @@ static const DECLARE_TLV_DB_RANGE(mic_gain_tlv, 8, ARRAY_SIZE(mic_gain_table)-1, TLV_DB_SCALE_ITEM(-6500, 100, 0), ); -int pdmic_get_mic_volsw(struct snd_kcontrol *kcontrol, +static int pdmic_get_mic_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); @@ -357,8 +359,10 @@ static int atmel_pdmic_codec_probe(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_dev_pdmic = { .probe = atmel_pdmic_codec_probe, - .controls = atmel_pdmic_snd_controls, - .num_controls = ARRAY_SIZE(atmel_pdmic_snd_controls), + .component_driver = { + .controls = atmel_pdmic_snd_controls, + .num_controls = ARRAY_SIZE(atmel_pdmic_snd_controls), + }, }; /* codec dai component */ @@ -596,7 +600,7 @@ static int atmel_pdmic_probe(struct platform_device *pdev) dd->irq = platform_get_irq(pdev, 0); if (dd->irq < 0) { ret = dd->irq; - dev_err(dev, "failed to could not get irq: %d\n", ret); + dev_err(dev, "failed to get irq: %d\n", ret); return ret; } @@ -614,7 +618,7 @@ static int atmel_pdmic_probe(struct platform_device *pdev) return ret; } - /* The gclk clock frequency must always be tree times + /* The gclk clock frequency must always be three times * lower than the pclk clock frequency */ ret = clk_set_rate(dd->gclk, clk_get_rate(dd->pclk)/3); @@ -649,7 +653,7 @@ static int atmel_pdmic_probe(struct platform_device *pdev) return ret; } - /* Get the minimal and maximal sample rate that micphone supports */ + /* Get the minimal and maximal sample rate that the microphone supports */ atmel_pdmic_get_sample_rate(dd, &rate_min, &rate_max); /* register cpu dai */ diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 16e459aedffe..a1e2c5682dcd 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -380,6 +380,7 @@ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream, ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST)); /* Clear the SSC dividers */ ssc_p->cmr_div = ssc_p->tcmr_period = ssc_p->rcmr_period = 0; + ssc_p->forced_divider = 0; } spin_unlock_irq(&ssc_p->lock); @@ -426,14 +427,17 @@ static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, else if (div != ssc_p->cmr_div) return -EBUSY; + ssc_p->forced_divider |= BIT(ATMEL_SSC_CMR_DIV); break; case ATMEL_SSC_TCMR_PERIOD: ssc_p->tcmr_period = div; + ssc_p->forced_divider |= BIT(ATMEL_SSC_TCMR_PERIOD); break; case ATMEL_SSC_RCMR_PERIOD: ssc_p->rcmr_period = div; + ssc_p->forced_divider |= BIT(ATMEL_SSC_RCMR_PERIOD); break; default: @@ -443,6 +447,28 @@ static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, return 0; } +/* Is the cpu-dai master of the frame clock? */ +static int atmel_ssc_cfs(struct atmel_ssc_info *ssc_p) +{ + switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBS_CFS: + return 1; + } + return 0; +} + +/* Is the cpu-dai master of the bit clock? */ +static int atmel_ssc_cbs(struct atmel_ssc_info *ssc_p) +{ + switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBS_CFS: + return 1; + } + return 0; +} + /* * Configure the SSC. */ @@ -459,6 +485,9 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, u32 tfmr, rfmr, tcmr, rcmr; int ret; int fslen, fslen_ext; + u32 cmr_div; + u32 tcmr_period; + u32 rcmr_period; /* * Currently, there is only one set of dma params for @@ -470,6 +499,46 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, else dir = 1; + /* + * If the cpu dai should provide BCLK, but noone has provided the + * divider needed for that to work, fall back to something sensible. + */ + cmr_div = ssc_p->cmr_div; + if (!(ssc_p->forced_divider & BIT(ATMEL_SSC_CMR_DIV)) && + atmel_ssc_cbs(ssc_p)) { + int bclk_rate = snd_soc_params_to_bclk(params); + + if (bclk_rate < 0) { + dev_err(dai->dev, "unable to calculate cmr_div: %d\n", + bclk_rate); + return bclk_rate; + } + + cmr_div = DIV_ROUND_CLOSEST(ssc_p->mck_rate, 2 * bclk_rate); + } + + /* + * If the cpu dai should provide LRCLK, but noone has provided the + * dividers needed for that to work, fall back to something sensible. + */ + tcmr_period = ssc_p->tcmr_period; + rcmr_period = ssc_p->rcmr_period; + if (atmel_ssc_cfs(ssc_p)) { + int frame_size = snd_soc_params_to_frame_size(params); + + if (frame_size < 0) { + dev_err(dai->dev, + "unable to calculate tx/rx cmr_period: %d\n", + frame_size); + return frame_size; + } + + if (!(ssc_p->forced_divider & BIT(ATMEL_SSC_TCMR_PERIOD))) + tcmr_period = frame_size / 2 - 1; + if (!(ssc_p->forced_divider & BIT(ATMEL_SSC_RCMR_PERIOD))) + rcmr_period = frame_size / 2 - 1; + } + dma_params = ssc_p->dma_params[dir]; channels = params_channels(params); @@ -524,7 +593,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, fslen_ext = (bits - 1) / 16; fslen = (bits - 1) % 16; - rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) + rcmr = SSC_BF(RCMR_PERIOD, rcmr_period) | SSC_BF(RCMR_STTDLY, START_DELAY) | SSC_BF(RCMR_START, SSC_START_FALLING_RF) | SSC_BF(RCMR_CKI, SSC_CKI_RISING) @@ -540,7 +609,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, | SSC_BF(RFMR_LOOP, 0) | SSC_BF(RFMR_DATLEN, (bits - 1)); - tcmr = SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period) + tcmr = SSC_BF(TCMR_PERIOD, tcmr_period) | SSC_BF(TCMR_STTDLY, START_DELAY) | SSC_BF(TCMR_START, SSC_START_FALLING_RF) | SSC_BF(TCMR_CKI, SSC_CKI_FALLING) @@ -606,7 +675,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, fslen_ext = (bits - 1) / 16; fslen = (bits - 1) % 16; - rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) + rcmr = SSC_BF(RCMR_PERIOD, rcmr_period) | SSC_BF(RCMR_STTDLY, START_DELAY) | SSC_BF(RCMR_START, SSC_START_FALLING_RF) | SSC_BF(RCMR_CKI, SSC_CKI_RISING) @@ -623,7 +692,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, | SSC_BF(RFMR_LOOP, 0) | SSC_BF(RFMR_DATLEN, (bits - 1)); - tcmr = SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period) + tcmr = SSC_BF(TCMR_PERIOD, tcmr_period) | SSC_BF(TCMR_STTDLY, START_DELAY) | SSC_BF(TCMR_START, SSC_START_FALLING_RF) | SSC_BF(TCMR_CKI, SSC_CKI_FALLING) @@ -650,7 +719,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, * MCK divider, and the BCLK signal is output * on the SSC TK line. */ - rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) + rcmr = SSC_BF(RCMR_PERIOD, rcmr_period) | SSC_BF(RCMR_STTDLY, 1) | SSC_BF(RCMR_START, SSC_START_RISING_RF) | SSC_BF(RCMR_CKI, SSC_CKI_RISING) @@ -665,7 +734,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, | SSC_BF(RFMR_LOOP, 0) | SSC_BF(RFMR_DATLEN, (bits - 1)); - tcmr = SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period) + tcmr = SSC_BF(TCMR_PERIOD, tcmr_period) | SSC_BF(TCMR_STTDLY, 1) | SSC_BF(TCMR_START, SSC_START_RISING_RF) | SSC_BF(TCMR_CKI, SSC_CKI_FALLING) @@ -760,7 +829,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, } /* set SSC clock mode register */ - ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->cmr_div); + ssc_writel(ssc_p->ssc->regs, CMR, cmr_div); /* set receive clock mode and format */ ssc_writel(ssc_p->ssc->regs, RCMR, rcmr); diff --git a/sound/soc/atmel/atmel_ssc_dai.h b/sound/soc/atmel/atmel_ssc_dai.h index 80b153857a88..75194f582131 100644 --- a/sound/soc/atmel/atmel_ssc_dai.h +++ b/sound/soc/atmel/atmel_ssc_dai.h @@ -113,6 +113,7 @@ struct atmel_ssc_info { unsigned short cmr_div; unsigned short tcmr_period; unsigned short rcmr_period; + unsigned int forced_divider; struct atmel_pcm_dma_params *dma_params[2]; struct atmel_ssc_state ssc_state; unsigned long mck_rate; diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c index fdd28ed3e0b9..fbc10f61eb55 100644 --- a/sound/soc/atmel/atmel_wm8904.c +++ b/sound/soc/atmel/atmel_wm8904.c @@ -53,7 +53,7 @@ static int atmel_asoc_wm8904_hw_params(struct snd_pcm_substream *substream, return 0; } -static struct snd_soc_ops atmel_asoc_wm8904_ops = { +static const struct snd_soc_ops atmel_asoc_wm8904_ops = { .hw_params = atmel_asoc_wm8904_hw_params, }; diff --git a/sound/soc/atmel/tse850-pcm5142.c b/sound/soc/atmel/tse850-pcm5142.c new file mode 100644 index 000000000000..ac6a814c8ecf --- /dev/null +++ b/sound/soc/atmel/tse850-pcm5142.c @@ -0,0 +1,472 @@ +/* + * TSE-850 audio - ASoC driver for the Axentia TSE-850 with a PCM5142 codec + * + * Copyright (C) 2016 Axentia Technologies AB + * + * Author: Peter Rosin <peda@axentia.se> + * + * 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. + */ + +/* + * loop1 relays + * IN1 +---o +------------+ o---+ OUT1 + * \ / + * + + + * | / | + * +--o +--. | + * | add | | + * | V | + * | .---. | + * DAC +----------->|Sum|---+ + * | '---' | + * | | + * + + + * + * IN2 +---o--+------------+--o---+ OUT2 + * loop2 relays + * + * The 'loop1' gpio pin controlls two relays, which are either in loop + * position, meaning that input and output are directly connected, or + * they are in mixer position, meaning that the signal is passed through + * the 'Sum' mixer. Similarly for 'loop2'. + * + * In the above, the 'loop1' relays are inactive, thus feeding IN1 to the + * mixer (if 'add' is active) and feeding the mixer output to OUT1. The + * 'loop2' relays are active, short-cutting the TSE-850 from channel 2. + * IN1, IN2, OUT1 and OUT2 are TSE-850 connectors and DAC is the PCB name + * of the (filtered) output from the PCM5142 codec. + */ + +#include <linux/clk.h> +#include <linux/gpio.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/regulator/consumer.h> + +#include <sound/soc.h> +#include <sound/pcm_params.h> + +#include "atmel_ssc_dai.h" + +struct tse850_priv { + int ssc_id; + + struct gpio_desc *add; + struct gpio_desc *loop1; + struct gpio_desc *loop2; + + struct regulator *ana; + + int add_cache; + int loop1_cache; + int loop2_cache; +}; + +static int tse850_get_mux1(struct snd_kcontrol *kctrl, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl); + struct snd_soc_card *card = dapm->card; + struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card); + + ucontrol->value.enumerated.item[0] = tse850->loop1_cache; + + return 0; +} + +static int tse850_put_mux1(struct snd_kcontrol *kctrl, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl); + struct snd_soc_card *card = dapm->card; + struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card); + struct soc_enum *e = (struct soc_enum *)kctrl->private_value; + unsigned int val = ucontrol->value.enumerated.item[0]; + + if (val >= e->items) + return -EINVAL; + + gpiod_set_value_cansleep(tse850->loop1, val); + tse850->loop1_cache = val; + + return snd_soc_dapm_put_enum_double(kctrl, ucontrol); +} + +static int tse850_get_mux2(struct snd_kcontrol *kctrl, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl); + struct snd_soc_card *card = dapm->card; + struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card); + + ucontrol->value.enumerated.item[0] = tse850->loop2_cache; + + return 0; +} + +static int tse850_put_mux2(struct snd_kcontrol *kctrl, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl); + struct snd_soc_card *card = dapm->card; + struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card); + struct soc_enum *e = (struct soc_enum *)kctrl->private_value; + unsigned int val = ucontrol->value.enumerated.item[0]; + + if (val >= e->items) + return -EINVAL; + + gpiod_set_value_cansleep(tse850->loop2, val); + tse850->loop2_cache = val; + + return snd_soc_dapm_put_enum_double(kctrl, ucontrol); +} + +int tse850_get_mix(struct snd_kcontrol *kctrl, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl); + struct snd_soc_card *card = dapm->card; + struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card); + + ucontrol->value.enumerated.item[0] = tse850->add_cache; + + return 0; +} + +int tse850_put_mix(struct snd_kcontrol *kctrl, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl); + struct snd_soc_card *card = dapm->card; + struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card); + int connect = !!ucontrol->value.integer.value[0]; + + if (tse850->add_cache == connect) + return 0; + + /* + * Hmmm, this gpiod_set_value_cansleep call should probably happen + * inside snd_soc_dapm_mixer_update_power in the loop. + */ + gpiod_set_value_cansleep(tse850->add, connect); + tse850->add_cache = connect; + + snd_soc_dapm_mixer_update_power(dapm, kctrl, connect, NULL); + return 1; +} + +int tse850_get_ana(struct snd_kcontrol *kctrl, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl); + struct snd_soc_card *card = dapm->card; + struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card); + int ret; + + ret = regulator_get_voltage(tse850->ana); + if (ret < 0) + return ret; + + /* + * Map regulator output values like so: + * -11.5V to "Low" (enum 0) + * 11.5V-12.5V to "12V" (enum 1) + * 12.5V-13.5V to "13V" (enum 2) + * ... + * 18.5V-19.5V to "19V" (enum 8) + * 19.5V- to "20V" (enum 9) + */ + if (ret < 11000000) + ret = 11000000; + else if (ret > 20000000) + ret = 20000000; + ret -= 11000000; + ret = (ret + 500000) / 1000000; + + ucontrol->value.enumerated.item[0] = ret; + + return 0; +} + +int tse850_put_ana(struct snd_kcontrol *kctrl, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl); + struct snd_soc_card *card = dapm->card; + struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card); + struct soc_enum *e = (struct soc_enum *)kctrl->private_value; + unsigned int uV = ucontrol->value.enumerated.item[0]; + int ret; + + if (uV >= e->items) + return -EINVAL; + + /* + * Map enum zero (Low) to 2 volts on the regulator, do this since + * the ana regulator is supplied by the system 12V voltage and + * requesting anything below the system voltage causes the system + * voltage to be passed through the regulator. Also, the ana + * regulator induces noise when requesting voltages near the + * system voltage. So, by mapping Low to 2V, that noise is + * eliminated when all that is needed is 12V (the system voltage). + */ + if (uV) + uV = 11000000 + (1000000 * uV); + else + uV = 2000000; + + ret = regulator_set_voltage(tse850->ana, uV, uV); + if (ret < 0) + return ret; + + return snd_soc_dapm_put_enum_double(kctrl, ucontrol); +} + +static const char * const mux_text[] = { "Mixer", "Loop" }; + +static const struct soc_enum mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, mux_text); + +static const struct snd_kcontrol_new mux1 = + SOC_DAPM_ENUM_EXT("MUX1", mux_enum, tse850_get_mux1, tse850_put_mux1); + +static const struct snd_kcontrol_new mux2 = + SOC_DAPM_ENUM_EXT("MUX2", mux_enum, tse850_get_mux2, tse850_put_mux2); + +#define TSE850_DAPM_SINGLE_EXT(xname, reg, shift, max, invert, xget, xput) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_volsw, \ + .get = xget, \ + .put = xput, \ + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } + +static const struct snd_kcontrol_new mix[] = { + TSE850_DAPM_SINGLE_EXT("IN Switch", SND_SOC_NOPM, 0, 1, 0, + tse850_get_mix, tse850_put_mix), +}; + +static const char * const ana_text[] = { + "Low", "12V", "13V", "14V", "15V", "16V", "17V", "18V", "19V", "20V" +}; + +static const struct soc_enum ana_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 9, ana_text); + +static const struct snd_kcontrol_new out = + SOC_DAPM_ENUM_EXT("ANA", ana_enum, tse850_get_ana, tse850_put_ana); + +static const struct snd_soc_dapm_widget tse850_dapm_widgets[] = { + SND_SOC_DAPM_LINE("OUT1", NULL), + SND_SOC_DAPM_LINE("OUT2", NULL), + SND_SOC_DAPM_LINE("IN1", NULL), + SND_SOC_DAPM_LINE("IN2", NULL), + SND_SOC_DAPM_INPUT("DAC"), + SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0), + SOC_MIXER_ARRAY("MIX", SND_SOC_NOPM, 0, 0, mix), + SND_SOC_DAPM_MUX("MUX1", SND_SOC_NOPM, 0, 0, &mux1), + SND_SOC_DAPM_MUX("MUX2", SND_SOC_NOPM, 0, 0, &mux2), + SND_SOC_DAPM_OUT_DRV("OUT", SND_SOC_NOPM, 0, 0, &out, 1), +}; + +/* + * These connections are not entirely correct, since both IN1 and IN2 + * are always fed to MIX (if the "IN switch" is set so), i.e. without + * regard to the loop1 and loop2 relays that according to this only + * control MUX1 and MUX2 but in fact also control how the input signals + * are routed. + * But, 1) I don't know how to do it right, and 2) it doesn't seem to + * matter in practice since nothing is powered in those sections anyway. + */ +static const struct snd_soc_dapm_route tse850_intercon[] = { + { "OUT1", NULL, "MUX1" }, + { "OUT2", NULL, "MUX2" }, + + { "MUX1", "Loop", "IN1" }, + { "MUX1", "Mixer", "OUT" }, + + { "MUX2", "Loop", "IN2" }, + { "MUX2", "Mixer", "OUT" }, + + { "OUT", NULL, "MIX" }, + + { "MIX", NULL, "DAC" }, + { "MIX", "IN Switch", "IN1" }, + { "MIX", "IN Switch", "IN2" }, + + /* connect board input to the codec left channel output pin */ + { "DAC", NULL, "OUTL" }, +}; + +static struct snd_soc_dai_link tse850_dailink = { + .name = "TSE-850", + .stream_name = "TSE-850-PCM", + .codec_dai_name = "pcm512x-hifi", + .dai_fmt = SND_SOC_DAIFMT_I2S + | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFS, +}; + +static struct snd_soc_card tse850_card = { + .name = "TSE-850-ASoC", + .owner = THIS_MODULE, + .dai_link = &tse850_dailink, + .num_links = 1, + .dapm_widgets = tse850_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tse850_dapm_widgets), + .dapm_routes = tse850_intercon, + .num_dapm_routes = ARRAY_SIZE(tse850_intercon), + .fully_routed = true, +}; + +static int tse850_dt_init(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *codec_np, *cpu_np; + struct snd_soc_card *card = &tse850_card; + struct snd_soc_dai_link *dailink = &tse850_dailink; + struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card); + + if (!np) { + dev_err(&pdev->dev, "only device tree supported\n"); + return -EINVAL; + } + + cpu_np = of_parse_phandle(np, "axentia,ssc-controller", 0); + if (!cpu_np) { + dev_err(&pdev->dev, "failed to get dai and pcm info\n"); + return -EINVAL; + } + dailink->cpu_of_node = cpu_np; + dailink->platform_of_node = cpu_np; + tse850->ssc_id = of_alias_get_id(cpu_np, "ssc"); + of_node_put(cpu_np); + + codec_np = of_parse_phandle(np, "axentia,audio-codec", 0); + if (!codec_np) { + dev_err(&pdev->dev, "failed to get codec info\n"); + return -EINVAL; + } + dailink->codec_of_node = codec_np; + of_node_put(codec_np); + + return 0; +} + +static int tse850_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &tse850_card; + struct device *dev = card->dev = &pdev->dev; + struct tse850_priv *tse850; + int ret; + + tse850 = devm_kzalloc(dev, sizeof(*tse850), GFP_KERNEL); + if (!tse850) + return -ENOMEM; + + snd_soc_card_set_drvdata(card, tse850); + + ret = tse850_dt_init(pdev); + if (ret) { + dev_err(dev, "failed to init dt info\n"); + return ret; + } + + tse850->add = devm_gpiod_get(dev, "axentia,add", GPIOD_OUT_HIGH); + if (IS_ERR(tse850->add)) { + if (PTR_ERR(tse850->add) != -EPROBE_DEFER) + dev_err(dev, "failed to get 'add' gpio\n"); + return PTR_ERR(tse850->add); + } + tse850->add_cache = 1; + + tse850->loop1 = devm_gpiod_get(dev, "axentia,loop1", GPIOD_OUT_HIGH); + if (IS_ERR(tse850->loop1)) { + if (PTR_ERR(tse850->loop1) != -EPROBE_DEFER) + dev_err(dev, "failed to get 'loop1' gpio\n"); + return PTR_ERR(tse850->loop1); + } + tse850->loop1_cache = 1; + + tse850->loop2 = devm_gpiod_get(dev, "axentia,loop2", GPIOD_OUT_HIGH); + if (IS_ERR(tse850->loop2)) { + if (PTR_ERR(tse850->loop2) != -EPROBE_DEFER) + dev_err(dev, "failed to get 'loop2' gpio\n"); + return PTR_ERR(tse850->loop2); + } + tse850->loop2_cache = 1; + + tse850->ana = devm_regulator_get(dev, "axentia,ana"); + if (IS_ERR(tse850->ana)) { + if (PTR_ERR(tse850->ana) != -EPROBE_DEFER) + dev_err(dev, "failed to get 'ana' regulator\n"); + return PTR_ERR(tse850->ana); + } + + ret = regulator_enable(tse850->ana); + if (ret < 0) { + dev_err(dev, "failed to enable the 'ana' regulator\n"); + return ret; + } + + ret = atmel_ssc_set_audio(tse850->ssc_id); + if (ret != 0) { + dev_err(dev, + "failed to set SSC %d for audio\n", tse850->ssc_id); + goto err_disable_ana; + } + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(dev, "snd_soc_register_card failed\n"); + goto err_put_audio; + } + + return 0; + +err_put_audio: + atmel_ssc_put_audio(tse850->ssc_id); +err_disable_ana: + regulator_disable(tse850->ana); + return ret; +} + +static int tse850_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card); + + snd_soc_unregister_card(card); + atmel_ssc_put_audio(tse850->ssc_id); + regulator_disable(tse850->ana); + + return 0; +} + +static const struct of_device_id tse850_dt_ids[] = { + { .compatible = "axentia,tse850-pcm5142", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, tse850_dt_ids); + +static struct platform_driver tse850_driver = { + .driver = { + .name = "axentia-tse850-pcm5142", + .of_match_table = of_match_ptr(tse850_dt_ids), + }, + .probe = tse850_probe, + .remove = tse850_remove, +}; + +module_platform_driver(tse850_driver); + +/* Module information */ +MODULE_AUTHOR("Peter Rosin <peda@axentia.se>"); +MODULE_DESCRIPTION("ALSA SoC driver for TSE-850 with PCM5142 codec"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig index d528aaceaad9..edf367100ebd 100644 --- a/sound/soc/bcm/Kconfig +++ b/sound/soc/bcm/Kconfig @@ -11,6 +11,7 @@ config SND_BCM2835_SOC_I2S config SND_SOC_CYGNUS tristate "SoC platform audio for Broadcom Cygnus chips" depends on ARCH_BCM_CYGNUS || COMPILE_TEST + depends on HAS_DMA help Say Y if you want to add support for ASoC audio on Broadcom Cygnus chips (bcm958300, bcm958305, bcm911360) diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c index e8bed6b0c9db..b013a4c62b63 100644 --- a/sound/soc/codecs/88pm860x-codec.c +++ b/sound/soc/codecs/88pm860x-codec.c @@ -1361,12 +1361,14 @@ static struct snd_soc_codec_driver soc_codec_dev_pm860x = { .set_bias_level = pm860x_set_bias_level, .get_regmap = pm860x_get_regmap, - .controls = pm860x_snd_controls, - .num_controls = ARRAY_SIZE(pm860x_snd_controls), - .dapm_widgets = pm860x_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(pm860x_dapm_widgets), - .dapm_routes = pm860x_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(pm860x_dapm_routes), + .component_driver = { + .controls = pm860x_snd_controls, + .num_controls = ARRAY_SIZE(pm860x_snd_controls), + .dapm_widgets = pm860x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pm860x_dapm_widgets), + .dapm_routes = pm860x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(pm860x_dapm_routes), + }, }; static int pm860x_codec_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 1cd6ab344d67..9e1718a8cb1c 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -48,6 +48,8 @@ config SND_SOC_ALL_CODECS select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC select SND_SOC_CS35L32 if I2C select SND_SOC_CS35L33 if I2C + select SND_SOC_CS35L34 if I2C + select SND_SOC_CS42L42 if I2C select SND_SOC_CS42L51_I2C if I2C select SND_SOC_CS42L52 if I2C && INPUT select SND_SOC_CS42L56 if I2C && INPUT @@ -83,6 +85,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_MAX98095 if I2C select SND_SOC_MAX98357A if GPIOLIB select SND_SOC_MAX98371 if I2C + select SND_SOC_MAX98504 if I2C select SND_SOC_MAX9867 if I2C select SND_SOC_MAX98925 if I2C select SND_SOC_MAX98926 if I2C @@ -92,6 +95,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_MAX9877 if I2C select SND_SOC_MC13783 if MFD_MC13XXX select SND_SOC_ML26124 if I2C + select SND_SOC_NAU8810 if I2C select SND_SOC_NAU8825 if I2C select SND_SOC_HDMI_CODEC select SND_SOC_PCM1681 if I2C @@ -112,6 +116,9 @@ config SND_SOC_ALL_CODECS select SND_SOC_RT5645 if I2C select SND_SOC_RT5651 if I2C select SND_SOC_RT5659 if I2C + select SND_SOC_RT5660 if I2C + select SND_SOC_RT5665 if I2C + select SND_SOC_RT5663 if I2C select SND_SOC_RT5670 if I2C select SND_SOC_RT5677 if I2C && SPI_MASTER select SND_SOC_SGTL5000 if I2C @@ -396,6 +403,14 @@ config SND_SOC_CS35L33 tristate "Cirrus Logic CS35L33 CODEC" depends on I2C +config SND_SOC_CS35L34 + tristate "Cirrus Logic CS35L34 CODEC" + depends on I2C + +config SND_SOC_CS42L42 + tristate "Cirrus Logic CS42L42 CODEC" + depends on I2C + config SND_SOC_CS42L51 tristate @@ -578,6 +593,13 @@ config SND_SOC_MAX9860 depends on I2C select REGMAP_I2C +config SND_SOC_MSM8916_WCD_ANALOG + tristate "Qualcomm MSM8916 WCD Analog Codec" + depends on SPMI || COMPILE_TEST + +config SND_SOC_MSM8916_WCD_DIGITAL + tristate "Qualcomm MSM8916 WCD DIGITAL Codec" + config SND_SOC_PCM1681 tristate "Texas Instruments PCM1681 CODEC" depends on I2C @@ -645,6 +667,9 @@ config SND_SOC_RL6231 default y if SND_SOC_RT5645=y default y if SND_SOC_RT5651=y default y if SND_SOC_RT5659=y + default y if SND_SOC_RT5660=y + default y if SND_SOC_RT5665=y + default y if SND_SOC_RT5663=y default y if SND_SOC_RT5670=y default y if SND_SOC_RT5677=y default m if SND_SOC_RT5514=m @@ -653,6 +678,9 @@ config SND_SOC_RL6231 default m if SND_SOC_RT5645=m default m if SND_SOC_RT5651=m default m if SND_SOC_RT5659=m + default m if SND_SOC_RT5660=m + default m if SND_SOC_RT5665=m + default m if SND_SOC_RT5663=m default m if SND_SOC_RT5670=m default m if SND_SOC_RT5677=m @@ -697,6 +725,15 @@ config SND_SOC_RT5651 config SND_SOC_RT5659 tristate +config SND_SOC_RT5660 + tristate + +config SND_SOC_RT5665 + tristate + +config SND_SOC_RT5663 + tristate + config SND_SOC_RT5670 tristate @@ -860,6 +897,7 @@ config SND_SOC_UDA134X config SND_SOC_UDA1380 tristate + depends on I2C config SND_SOC_WL1273 tristate @@ -900,7 +938,7 @@ config SND_SOC_WM8523 depends on I2C config SND_SOC_WM8580 - tristate "Wolfson Microelectronics WM8523 CODEC" + tristate "Wolfson Microelectronics WM8580 and WM8581 CODECs" depends on I2C config SND_SOC_WM8711 @@ -1034,15 +1072,18 @@ config SND_SOC_WM8998 config SND_SOC_WM9081 tristate + depends on I2C config SND_SOC_WM9090 tristate config SND_SOC_WM9705 tristate + select REGMAP_AC97 config SND_SOC_WM9712 tristate + select REGMAP_AC97 config SND_SOC_WM9713 tristate @@ -1064,6 +1105,10 @@ config SND_SOC_MC13783 config SND_SOC_ML26124 tristate +config SND_SOC_NAU8810 + tristate "Nuvoton Technology Corporation NAU88C10 CODEC" + depends on I2C + config SND_SOC_NAU8825 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 58036af2c7d9..7e1dad79610b 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -38,6 +38,8 @@ snd-soc-bt-sco-objs := bt-sco.o snd-soc-cq93vc-objs := cq93vc.o snd-soc-cs35l32-objs := cs35l32.o snd-soc-cs35l33-objs := cs35l33.o +snd-soc-cs35l34-objs := cs35l34.o +snd-soc-cs42l42-objs := cs42l42.o snd-soc-cs42l51-objs := cs42l51.o snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o snd-soc-cs42l52-objs := cs42l52.o @@ -86,6 +88,9 @@ snd-soc-max9850-objs := max9850.o snd-soc-max9860-objs := max9860.o snd-soc-mc13783-objs := mc13783.o snd-soc-ml26124-objs := ml26124.o +snd-soc-msm8916-analog-objs := msm8916-wcd-analog.o +snd-soc-msm8916-digital-objs := msm8916-wcd-digital.o +snd-soc-nau8810-objs := nau8810.o snd-soc-nau8825-objs := nau8825.o snd-soc-hdmi-codec-objs := hdmi-codec.o snd-soc-pcm1681-objs := pcm1681.o @@ -112,6 +117,9 @@ snd-soc-rt5640-objs := rt5640.o snd-soc-rt5645-objs := rt5645.o snd-soc-rt5651-objs := rt5651.o snd-soc-rt5659-objs := rt5659.o +snd-soc-rt5660-objs := rt5660.o +snd-soc-rt5665-objs := rt5665.o +snd-soc-rt5663-objs := rt5663.o snd-soc-rt5670-objs := rt5670.o snd-soc-rt5677-objs := rt5677.o snd-soc-rt5677-spi-objs := rt5677-spi.o @@ -211,7 +219,6 @@ snd-soc-wm9705-objs := wm9705.o snd-soc-wm9712-objs := wm9712.o snd-soc-wm9713-objs := wm9713.o snd-soc-wm-hubs-objs := wm_hubs.o - # Amp snd-soc-max9877-objs := max9877.o snd-soc-max98504-objs := max98504.o @@ -260,6 +267,8 @@ obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o obj-$(CONFIG_SND_SOC_CS35L32) += snd-soc-cs35l32.o obj-$(CONFIG_SND_SOC_CS35L33) += snd-soc-cs35l33.o +obj-$(CONFIG_SND_SOC_CS35L34) += snd-soc-cs35l34.o +obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42.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 @@ -307,6 +316,9 @@ obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_MAX9860) += snd-soc-max9860.o obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o +obj-$(CONFIG_SND_SOC_MSM8916_WCD_ANALOG) +=snd-soc-msm8916-analog.o +obj-$(CONFIG_SND_SOC_MSM8916_WCD_DIGITAL) +=snd-soc-msm8916-digital.o +obj-$(CONFIG_SND_SOC_NAU8810) += snd-soc-nau8810.o obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o @@ -333,6 +345,9 @@ obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o obj-$(CONFIG_SND_SOC_RT5645) += snd-soc-rt5645.o obj-$(CONFIG_SND_SOC_RT5651) += snd-soc-rt5651.o obj-$(CONFIG_SND_SOC_RT5659) += snd-soc-rt5659.o +obj-$(CONFIG_SND_SOC_RT5660) += snd-soc-rt5660.o +obj-$(CONFIG_SND_SOC_RT5665) += snd-soc-rt5665.o +obj-$(CONFIG_SND_SOC_RT5663) += snd-soc-rt5663.o obj-$(CONFIG_SND_SOC_RT5670) += snd-soc-rt5670.o obj-$(CONFIG_SND_SOC_RT5677) += snd-soc-rt5677.o obj-$(CONFIG_SND_SOC_RT5677_SPI) += snd-soc-rt5677-spi.o diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index 2fc89155f14a..312b2a11abb6 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -2408,28 +2408,28 @@ static void ab8500_codec_of_probe(struct device *dev, struct device_node *np, { u32 value; - if (of_get_property(np, "stericsson,amic1-type-single-ended", NULL)) + if (of_property_read_bool(np, "stericsson,amic1-type-single-ended")) codec->amics.mic1_type = AMIC_TYPE_SINGLE_ENDED; else codec->amics.mic1_type = AMIC_TYPE_DIFFERENTIAL; - if (of_get_property(np, "stericsson,amic2-type-single-ended", NULL)) + if (of_property_read_bool(np, "stericsson,amic2-type-single-ended")) codec->amics.mic2_type = AMIC_TYPE_SINGLE_ENDED; else codec->amics.mic2_type = AMIC_TYPE_DIFFERENTIAL; /* Has a non-standard Vamic been requested? */ - if (of_get_property(np, "stericsson,amic1a-bias-vamic2", NULL)) + if (of_property_read_bool(np, "stericsson,amic1a-bias-vamic2")) codec->amics.mic1a_micbias = AMIC_MICBIAS_VAMIC2; else codec->amics.mic1a_micbias = AMIC_MICBIAS_VAMIC1; - if (of_get_property(np, "stericsson,amic1b-bias-vamic2", NULL)) + if (of_property_read_bool(np, "stericsson,amic1b-bias-vamic2")) codec->amics.mic1b_micbias = AMIC_MICBIAS_VAMIC2; else codec->amics.mic1b_micbias = AMIC_MICBIAS_VAMIC1; - if (of_get_property(np, "stericsson,amic2-bias-vamic1", NULL)) + if (of_property_read_bool(np, "stericsson,amic2-bias-vamic1")) codec->amics.mic2_micbias = AMIC_MICBIAS_VAMIC1; else codec->amics.mic2_micbias = AMIC_MICBIAS_VAMIC2; @@ -2525,12 +2525,14 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec) static struct snd_soc_codec_driver ab8500_codec_driver = { .probe = ab8500_codec_probe, - .controls = ab8500_ctrls, - .num_controls = ARRAY_SIZE(ab8500_ctrls), - .dapm_widgets = ab8500_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(ab8500_dapm_widgets), - .dapm_routes = ab8500_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(ab8500_dapm_routes), + .component_driver = { + .controls = ab8500_ctrls, + .num_controls = ARRAY_SIZE(ab8500_ctrls), + .dapm_widgets = ab8500_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ab8500_dapm_widgets), + .dapm_routes = ab8500_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ab8500_dapm_routes), + }, }; static int ab8500_codec_driver_probe(struct platform_device *pdev) @@ -2585,8 +2587,6 @@ static struct platform_driver ab8500_codec_platform_driver = { }, .probe = ab8500_codec_driver_probe, .remove = ab8500_codec_driver_remove, - .suspend = NULL, - .resume = NULL, }; module_platform_driver(ab8500_codec_platform_driver); diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c index 5b3224c63943..f7f04c6be01e 100644 --- a/sound/soc/codecs/ac97.c +++ b/sound/soc/codecs/ac97.c @@ -117,10 +117,12 @@ static struct snd_soc_codec_driver soc_codec_dev_ac97 = { .suspend = ac97_soc_suspend, .resume = ac97_soc_resume, - .dapm_widgets = ac97_widgets, - .num_dapm_widgets = ARRAY_SIZE(ac97_widgets), - .dapm_routes = ac97_routes, - .num_dapm_routes = ARRAY_SIZE(ac97_routes), + .component_driver = { + .dapm_widgets = ac97_widgets, + .num_dapm_widgets = ARRAY_SIZE(ac97_widgets), + .dapm_routes = ac97_routes, + .num_dapm_routes = ARRAY_SIZE(ac97_routes), + }, }; static int ac97_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c index e2ce6c4d7ece..a478239aadac 100644 --- a/sound/soc/codecs/ad1836.c +++ b/sound/soc/codecs/ad1836.c @@ -327,12 +327,14 @@ static struct snd_soc_codec_driver soc_codec_dev_ad1836 = { .suspend = ad1836_suspend, .resume = ad1836_resume, - .controls = ad183x_controls, - .num_controls = ARRAY_SIZE(ad183x_controls), - .dapm_widgets = ad183x_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(ad183x_dapm_widgets), - .dapm_routes = ad183x_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(ad183x_dapm_routes), + .component_driver = { + .controls = ad183x_controls, + .num_controls = ARRAY_SIZE(ad183x_controls), + .dapm_widgets = ad183x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ad183x_dapm_widgets), + .dapm_routes = ad183x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ad183x_dapm_routes), + }, }; static const struct reg_default ad1836_reg_defaults[] = { diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c index 3a3f3f2343d7..d643557d89a7 100644 --- a/sound/soc/codecs/ad193x.c +++ b/sound/soc/codecs/ad193x.c @@ -410,12 +410,14 @@ static int ad193x_codec_probe(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_dev_ad193x = { .probe = ad193x_codec_probe, - .controls = ad193x_snd_controls, - .num_controls = ARRAY_SIZE(ad193x_snd_controls), - .dapm_widgets = ad193x_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(ad193x_dapm_widgets), - .dapm_routes = audio_paths, - .num_dapm_routes = ARRAY_SIZE(audio_paths), + .component_driver = { + .controls = ad193x_snd_controls, + .num_controls = ARRAY_SIZE(ad193x_snd_controls), + .dapm_widgets = ad193x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ad193x_dapm_widgets), + .dapm_routes = audio_paths, + .num_dapm_routes = ARRAY_SIZE(audio_paths), + }, }; const struct regmap_config ad193x_regmap_config = { diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c index 9ef20dbccbe3..b7c1b9f4bf5f 100644 --- a/sound/soc/codecs/ad1980.c +++ b/sound/soc/codecs/ad1980.c @@ -299,12 +299,14 @@ static struct snd_soc_codec_driver soc_codec_dev_ad1980 = { .probe = ad1980_soc_probe, .remove = ad1980_soc_remove, - .controls = ad1980_snd_ac97_controls, - .num_controls = ARRAY_SIZE(ad1980_snd_ac97_controls), - .dapm_widgets = ad1980_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(ad1980_dapm_widgets), - .dapm_routes = ad1980_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(ad1980_dapm_routes), + .component_driver = { + .controls = ad1980_snd_ac97_controls, + .num_controls = ARRAY_SIZE(ad1980_snd_ac97_controls), + .dapm_widgets = ad1980_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ad1980_dapm_widgets), + .dapm_routes = ad1980_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ad1980_dapm_routes), + }, }; static int ad1980_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c index a9400aef60b5..3e358a49442d 100644 --- a/sound/soc/codecs/ad73311.c +++ b/sound/soc/codecs/ad73311.c @@ -55,10 +55,12 @@ static struct snd_soc_dai_driver ad73311_dai = { }; static struct snd_soc_codec_driver soc_codec_dev_ad73311 = { - .dapm_widgets = ad73311_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(ad73311_dapm_widgets), - .dapm_routes = ad73311_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(ad73311_dapm_routes), + .component_driver = { + .dapm_widgets = ad73311_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ad73311_dapm_widgets), + .dapm_routes = ad73311_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ad73311_dapm_routes), + }, }; static int ad73311_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c index 1556b360fa15..8fa9045571ff 100644 --- a/sound/soc/codecs/adau1373.c +++ b/sound/soc/codecs/adau1373.c @@ -1466,12 +1466,14 @@ static struct snd_soc_codec_driver adau1373_codec_driver = { .set_pll = adau1373_set_pll, - .controls = adau1373_controls, - .num_controls = ARRAY_SIZE(adau1373_controls), - .dapm_widgets = adau1373_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(adau1373_dapm_widgets), - .dapm_routes = adau1373_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(adau1373_dapm_routes), + .component_driver = { + .controls = adau1373_controls, + .num_controls = ARRAY_SIZE(adau1373_controls), + .dapm_widgets = adau1373_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adau1373_dapm_widgets), + .dapm_routes = adau1373_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(adau1373_dapm_routes), + }, }; static int adau1373_i2c_probe(struct i2c_client *client, diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index de53c0d7bf10..3bad1bc8c00a 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -765,13 +765,14 @@ static struct snd_soc_codec_driver adau1701_codec_drv = { .set_bias_level = adau1701_set_bias_level, .idle_bias_off = true, - .controls = adau1701_controls, - .num_controls = ARRAY_SIZE(adau1701_controls), - .dapm_widgets = adau1701_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(adau1701_dapm_widgets), - .dapm_routes = adau1701_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(adau1701_dapm_routes), - + .component_driver = { + .controls = adau1701_controls, + .num_controls = ARRAY_SIZE(adau1701_controls), + .dapm_widgets = adau1701_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adau1701_dapm_widgets), + .dapm_routes = adau1701_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(adau1701_dapm_routes), + }, .set_sysclk = adau1701_set_sysclk, }; diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c index b95d29dbd13d..3bc3cc559dde 100644 --- a/sound/soc/codecs/adau1761.c +++ b/sound/soc/codecs/adau1761.c @@ -719,12 +719,14 @@ static const struct snd_soc_codec_driver adau1761_codec_driver = { .set_bias_level = adau1761_set_bias_level, .suspend_bias_off = true, - .controls = adau1761_controls, - .num_controls = ARRAY_SIZE(adau1761_controls), - .dapm_widgets = adau1x61_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(adau1x61_dapm_widgets), - .dapm_routes = adau1x61_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(adau1x61_dapm_routes), + .component_driver = { + .controls = adau1761_controls, + .num_controls = ARRAY_SIZE(adau1761_controls), + .dapm_widgets = adau1x61_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adau1x61_dapm_widgets), + .dapm_routes = adau1x61_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(adau1x61_dapm_routes), + }, }; #define ADAU1761_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ diff --git a/sound/soc/codecs/adau1781.c b/sound/soc/codecs/adau1781.c index bc1bb56dae63..546071c6c0d0 100644 --- a/sound/soc/codecs/adau1781.c +++ b/sound/soc/codecs/adau1781.c @@ -432,12 +432,14 @@ static const struct snd_soc_codec_driver adau1781_codec_driver = { .set_bias_level = adau1781_set_bias_level, .suspend_bias_off = true, - .controls = adau1781_controls, - .num_controls = ARRAY_SIZE(adau1781_controls), - .dapm_widgets = adau1781_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(adau1781_dapm_widgets), - .dapm_routes = adau1781_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(adau1781_dapm_routes), + .component_driver = { + .controls = adau1781_controls, + .num_controls = ARRAY_SIZE(adau1781_controls), + .dapm_widgets = adau1781_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adau1781_dapm_widgets), + .dapm_routes = adau1781_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(adau1781_dapm_routes), + }, }; #define ADAU1781_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c index 439aa3ff1f99..b36511d965c8 100644 --- a/sound/soc/codecs/adau17x1.c +++ b/sound/soc/codecs/adau17x1.c @@ -160,7 +160,7 @@ static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct adau *adau = snd_soc_codec_get_drvdata(codec); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - struct snd_soc_dapm_update update; + struct snd_soc_dapm_update update = { 0 }; unsigned int stream = e->shift_l; unsigned int val, change; int reg; diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c index 9bdd15f408c1..b319db6a69f8 100644 --- a/sound/soc/codecs/adau1977.c +++ b/sound/soc/codecs/adau1977.c @@ -873,12 +873,14 @@ static struct snd_soc_codec_driver adau1977_codec_driver = { .set_sysclk = adau1977_set_sysclk, .idle_bias_off = true, - .controls = adau1977_snd_controls, - .num_controls = ARRAY_SIZE(adau1977_snd_controls), - .dapm_widgets = adau1977_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(adau1977_dapm_widgets), - .dapm_routes = adau1977_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(adau1977_dapm_routes), + .component_driver = { + .controls = adau1977_snd_controls, + .num_controls = ARRAY_SIZE(adau1977_snd_controls), + .dapm_widgets = adau1977_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adau1977_dapm_widgets), + .dapm_routes = adau1977_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(adau1977_dapm_routes), + }, }; static int adau1977_setup_micbias(struct adau1977 *adau1977) diff --git a/sound/soc/codecs/adau7002.c b/sound/soc/codecs/adau7002.c index 9df72c6adcca..6384c5491de8 100644 --- a/sound/soc/codecs/adau7002.c +++ b/sound/soc/codecs/adau7002.c @@ -39,10 +39,12 @@ static struct snd_soc_dai_driver adau7002_dai = { }; static const struct snd_soc_codec_driver adau7002_codec_driver = { - .dapm_widgets = adau7002_widgets, - .num_dapm_widgets = ARRAY_SIZE(adau7002_widgets), - .dapm_routes = adau7002_routes, - .num_dapm_routes = ARRAY_SIZE(adau7002_routes), + .component_driver = { + .dapm_widgets = adau7002_widgets, + .num_dapm_widgets = ARRAY_SIZE(adau7002_widgets), + .dapm_routes = adau7002_routes, + .num_dapm_routes = ARRAY_SIZE(adau7002_routes), + }, }; static int adau7002_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c index acff8d62059c..6e793ebb5883 100644 --- a/sound/soc/codecs/adav80x.c +++ b/sound/soc/codecs/adav80x.c @@ -834,12 +834,14 @@ static struct snd_soc_codec_driver adav80x_codec_driver = { .set_pll = adav80x_set_pll, .set_sysclk = adav80x_set_sysclk, - .controls = adav80x_controls, - .num_controls = ARRAY_SIZE(adav80x_controls), - .dapm_widgets = adav80x_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(adav80x_dapm_widgets), - .dapm_routes = adav80x_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(adav80x_dapm_routes), + .component_driver = { + .controls = adav80x_controls, + .num_controls = ARRAY_SIZE(adav80x_controls), + .dapm_widgets = adav80x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adav80x_dapm_widgets), + .dapm_routes = adav80x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(adav80x_dapm_routes), + }, }; int adav80x_bus_probe(struct device *dev, struct regmap *regmap) diff --git a/sound/soc/codecs/ads117x.c b/sound/soc/codecs/ads117x.c index c5be1bdc2c9a..90c756d183b4 100644 --- a/sound/soc/codecs/ads117x.c +++ b/sound/soc/codecs/ads117x.c @@ -59,10 +59,12 @@ static struct snd_soc_dai_driver ads117x_dai = { }; static struct snd_soc_codec_driver soc_codec_dev_ads117x = { - .dapm_widgets = ads117x_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(ads117x_dapm_widgets), - .dapm_routes = ads117x_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(ads117x_dapm_routes), + .component_driver = { + .dapm_widgets = ads117x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ads117x_dapm_widgets), + .dapm_routes = ads117x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ads117x_dapm_routes), + }, }; static int ads117x_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c index 595d02d7602c..1a9d233c94d0 100644 --- a/sound/soc/codecs/ak4104.c +++ b/sound/soc/codecs/ak4104.c @@ -163,7 +163,10 @@ static struct snd_soc_dai_driver ak4104_dai = { .stream_name = "Playback", .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_192000, + .rates = SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE @@ -245,10 +248,12 @@ static struct snd_soc_codec_driver soc_codec_device_ak4104 = { .suspend = ak4104_soc_suspend, .resume = ak4104_soc_resume, - .dapm_widgets = ak4104_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(ak4104_dapm_widgets), - .dapm_routes = ak4104_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(ak4104_dapm_routes), + .component_driver = { + .dapm_widgets = ak4104_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4104_dapm_widgets), + .dapm_routes = ak4104_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ak4104_dapm_routes), + } }; static const struct regmap_config ak4104_regmap = { diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c index 54428c64387b..66cfffde9a12 100644 --- a/sound/soc/codecs/ak4535.c +++ b/sound/soc/codecs/ak4535.c @@ -395,12 +395,14 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4535 = { .set_bias_level = ak4535_set_bias_level, .suspend_bias_off = true, - .controls = ak4535_snd_controls, - .num_controls = ARRAY_SIZE(ak4535_snd_controls), - .dapm_widgets = ak4535_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(ak4535_dapm_widgets), - .dapm_routes = ak4535_audio_map, - .num_dapm_routes = ARRAY_SIZE(ak4535_audio_map), + .component_driver = { + .controls = ak4535_snd_controls, + .num_controls = ARRAY_SIZE(ak4535_snd_controls), + .dapm_widgets = ak4535_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4535_dapm_widgets), + .dapm_routes = ak4535_audio_map, + .num_dapm_routes = ARRAY_SIZE(ak4535_audio_map), + }, }; static int ak4535_i2c_probe(struct i2c_client *i2c, diff --git a/sound/soc/codecs/ak4554.c b/sound/soc/codecs/ak4554.c index 298dedc05140..b92c548b9d29 100644 --- a/sound/soc/codecs/ak4554.c +++ b/sound/soc/codecs/ak4554.c @@ -65,10 +65,12 @@ static struct snd_soc_dai_driver ak4554_dai = { }; static struct snd_soc_codec_driver soc_codec_dev_ak4554 = { - .dapm_widgets = ak4554_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(ak4554_dapm_widgets), - .dapm_routes = ak4554_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(ak4554_dapm_routes), + .component_driver = { + .dapm_widgets = ak4554_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4554_dapm_widgets), + .dapm_routes = ak4554_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ak4554_dapm_routes), + }, }; static int ak4554_soc_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c index 97798d250f08..e819dd8c82fd 100644 --- a/sound/soc/codecs/ak4613.c +++ b/sound/soc/codecs/ak4613.c @@ -458,12 +458,14 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4613 = { .suspend = ak4613_suspend, .resume = ak4613_resume, .set_bias_level = ak4613_set_bias_level, - .controls = ak4613_snd_controls, - .num_controls = ARRAY_SIZE(ak4613_snd_controls), - .dapm_widgets = ak4613_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(ak4613_dapm_widgets), - .dapm_routes = ak4613_intercon, - .num_dapm_routes = ARRAY_SIZE(ak4613_intercon), + .component_driver = { + .controls = ak4613_snd_controls, + .num_controls = ARRAY_SIZE(ak4613_snd_controls), + .dapm_widgets = ak4613_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4613_dapm_widgets), + .dapm_routes = ak4613_intercon, + .num_dapm_routes = ARRAY_SIZE(ak4613_intercon), + }, }; static void ak4613_parse_of(struct ak4613_priv *priv, diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c index b14176f8d884..ebdaf56c1d61 100644 --- a/sound/soc/codecs/ak4641.c +++ b/sound/soc/codecs/ak4641.c @@ -27,7 +27,27 @@ #include <sound/tlv.h> #include <sound/ak4641.h> -#include "ak4641.h" +/* AK4641 register space */ +#define AK4641_PM1 0x00 +#define AK4641_PM2 0x01 +#define AK4641_SIG1 0x02 +#define AK4641_SIG2 0x03 +#define AK4641_MODE1 0x04 +#define AK4641_MODE2 0x05 +#define AK4641_DAC 0x06 +#define AK4641_MIC 0x07 +#define AK4641_TIMER 0x08 +#define AK4641_ALC1 0x09 +#define AK4641_ALC2 0x0a +#define AK4641_PGA 0x0b +#define AK4641_LATT 0x0c +#define AK4641_RATT 0x0d +#define AK4641_VOL 0x0e +#define AK4641_STATUS 0x0f +#define AK4641_EQLO 0x10 +#define AK4641_EQMID 0x11 +#define AK4641_EQHI 0x12 +#define AK4641_BTIF 0x13 /* codec private data */ struct ak4641_priv { @@ -505,12 +525,14 @@ static struct snd_soc_dai_driver ak4641_dai[] = { }; static struct snd_soc_codec_driver soc_codec_dev_ak4641 = { - .controls = ak4641_snd_controls, - .num_controls = ARRAY_SIZE(ak4641_snd_controls), - .dapm_widgets = ak4641_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(ak4641_dapm_widgets), - .dapm_routes = ak4641_audio_map, - .num_dapm_routes = ARRAY_SIZE(ak4641_audio_map), + .component_driver = { + .controls = ak4641_snd_controls, + .num_controls = ARRAY_SIZE(ak4641_snd_controls), + .dapm_widgets = ak4641_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4641_dapm_widgets), + .dapm_routes = ak4641_audio_map, + .num_dapm_routes = ARRAY_SIZE(ak4641_audio_map), + }, .set_bias_level = ak4641_set_bias_level, .suspend_bias_off = true, }; diff --git a/sound/soc/codecs/ak4641.h b/sound/soc/codecs/ak4641.h deleted file mode 100644 index 4a263248efea..000000000000 --- a/sound/soc/codecs/ak4641.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * ak4641.h -- AK4641 SoC Audio driver - * - * Copyright 2008 Harald Welte <laforge@gnufiish.org> - * - * Based on ak4535.h - * - * 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 _AK4641_H -#define _AK4641_H - -/* AK4641 register space */ - -#define AK4641_PM1 0x00 -#define AK4641_PM2 0x01 -#define AK4641_SIG1 0x02 -#define AK4641_SIG2 0x03 -#define AK4641_MODE1 0x04 -#define AK4641_MODE2 0x05 -#define AK4641_DAC 0x06 -#define AK4641_MIC 0x07 -#define AK4641_TIMER 0x08 -#define AK4641_ALC1 0x09 -#define AK4641_ALC2 0x0a -#define AK4641_PGA 0x0b -#define AK4641_LATT 0x0c -#define AK4641_RATT 0x0d -#define AK4641_VOL 0x0e -#define AK4641_STATUS 0x0f -#define AK4641_EQLO 0x10 -#define AK4641_EQMID 0x11 -#define AK4641_EQHI 0x12 -#define AK4641_BTIF 0x13 - -#define AK4641_CACHEREGNUM 0x14 - - - -#define AK4641_DAI_HIFI 0 -#define AK4641_DAI_VOICE 1 - - -#endif diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index cc941d66ec3d..2609f95b7d19 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -555,12 +555,14 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4642 = { .suspend = ak4642_suspend, .resume = ak4642_resume, .set_bias_level = ak4642_set_bias_level, - .controls = ak4642_snd_controls, - .num_controls = ARRAY_SIZE(ak4642_snd_controls), - .dapm_widgets = ak4642_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(ak4642_dapm_widgets), - .dapm_routes = ak4642_intercon, - .num_dapm_routes = ARRAY_SIZE(ak4642_intercon), + .component_driver = { + .controls = ak4642_snd_controls, + .num_controls = ARRAY_SIZE(ak4642_snd_controls), + .dapm_widgets = ak4642_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4642_dapm_widgets), + .dapm_routes = ak4642_intercon, + .num_dapm_routes = ARRAY_SIZE(ak4642_intercon), + }, }; static const struct regmap_config ak4642_regmap = { diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c index c73a9f6914b6..6088afaabf62 100644 --- a/sound/soc/codecs/ak4671.c +++ b/sound/soc/codecs/ak4671.c @@ -612,12 +612,14 @@ static struct snd_soc_dai_driver ak4671_dai = { static struct snd_soc_codec_driver soc_codec_dev_ak4671 = { .set_bias_level = ak4671_set_bias_level, - .controls = ak4671_snd_controls, - .num_controls = ARRAY_SIZE(ak4671_snd_controls), - .dapm_widgets = ak4671_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(ak4671_dapm_widgets), - .dapm_routes = ak4671_intercon, - .num_dapm_routes = ARRAY_SIZE(ak4671_intercon), + .component_driver = { + .controls = ak4671_snd_controls, + .num_controls = ARRAY_SIZE(ak4671_snd_controls), + .dapm_widgets = ak4671_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4671_dapm_widgets), + .dapm_routes = ak4671_intercon, + .num_dapm_routes = ARRAY_SIZE(ak4671_intercon), + }, }; static const struct regmap_config ak4671_regmap = { diff --git a/sound/soc/codecs/ak5386.c b/sound/soc/codecs/ak5386.c index afa95360826d..0ef2df223336 100644 --- a/sound/soc/codecs/ak5386.c +++ b/sound/soc/codecs/ak5386.c @@ -74,10 +74,12 @@ static struct snd_soc_codec_driver soc_codec_ak5386 = { .remove = ak5386_soc_remove, .suspend = ak5386_soc_suspend, .resume = ak5386_soc_resume, - .dapm_widgets = ak5386_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(ak5386_dapm_widgets), - .dapm_routes = ak5386_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(ak5386_dapm_routes), + .component_driver = { + .dapm_widgets = ak5386_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak5386_dapm_widgets), + .dapm_routes = ak5386_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ak5386_dapm_routes), + }, }; static int ak5386_set_dai_fmt(struct snd_soc_dai *codec_dai, diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c index 4d3ba33eb6f9..adb80d8719bd 100644 --- a/sound/soc/codecs/alc5632.c +++ b/sound/soc/codecs/alc5632.c @@ -1072,12 +1072,14 @@ static const struct snd_soc_codec_driver soc_codec_device_alc5632 = { .set_bias_level = alc5632_set_bias_level, .suspend_bias_off = true, - .controls = alc5632_snd_controls, - .num_controls = ARRAY_SIZE(alc5632_snd_controls), - .dapm_widgets = alc5632_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(alc5632_dapm_widgets), - .dapm_routes = alc5632_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(alc5632_dapm_routes), + .component_driver = { + .controls = alc5632_snd_controls, + .num_controls = ARRAY_SIZE(alc5632_snd_controls), + .dapm_widgets = alc5632_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(alc5632_dapm_widgets), + .dapm_routes = alc5632_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(alc5632_dapm_routes), + }, }; static const struct regmap_config alc5632_regmap = { diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index ecfdbfcae366..0a734d910850 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -109,7 +109,7 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w, break; } - return 0; + return arizona_out_ev(w, kcontrol, event); } static irqreturn_t arizona_thermal_warn(int irq, void *data) @@ -159,12 +159,14 @@ static irqreturn_t arizona_thermal_shutdown(int irq, void *data) static const struct snd_soc_dapm_widget arizona_spkl = SND_SOC_DAPM_PGA_E("OUT4L", SND_SOC_NOPM, ARIZONA_OUT4L_ENA_SHIFT, 0, NULL, 0, arizona_spk_ev, - SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU); + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD); static const struct snd_soc_dapm_widget arizona_spkr = SND_SOC_DAPM_PGA_E("OUT4R", SND_SOC_NOPM, ARIZONA_OUT4R_ENA_SHIFT, 0, NULL, 0, arizona_spk_ev, - SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU); + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD); int arizona_init_spk(struct snd_soc_codec *codec) { @@ -189,6 +191,14 @@ int arizona_init_spk(struct snd_soc_codec *codec) break; } + return 0; +} +EXPORT_SYMBOL_GPL(arizona_init_spk); + +int arizona_init_spk_irqs(struct arizona *arizona) +{ + int ret; + ret = arizona_request_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT_WARN, "Thermal warning", arizona_thermal_warn, arizona); @@ -207,19 +217,16 @@ int arizona_init_spk(struct snd_soc_codec *codec) return 0; } -EXPORT_SYMBOL_GPL(arizona_init_spk); +EXPORT_SYMBOL_GPL(arizona_init_spk_irqs); -int arizona_free_spk(struct snd_soc_codec *codec) +int arizona_free_spk_irqs(struct arizona *arizona) { - struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); - struct arizona *arizona = priv->arizona; - arizona_free_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT_WARN, arizona); arizona_free_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT, arizona); return 0; } -EXPORT_SYMBOL_GPL(arizona_free_spk); +EXPORT_SYMBOL_GPL(arizona_free_spk_irqs); static const struct snd_soc_dapm_route arizona_mono_routes[] = { { "OUT1R", NULL, "OUT1L" }, @@ -250,6 +257,7 @@ EXPORT_SYMBOL_GPL(arizona_init_mono); int arizona_init_gpio(struct snd_soc_codec *codec) { struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); struct arizona *arizona = priv->arizona; int i; @@ -257,21 +265,24 @@ int arizona_init_gpio(struct snd_soc_codec *codec) switch (arizona->type) { case WM5110: case WM8280: - snd_soc_dapm_disable_pin(dapm, "DRC2 Signal Activity"); + snd_soc_component_disable_pin(component, + "DRC2 Signal Activity"); break; default: break; } - snd_soc_dapm_disable_pin(dapm, "DRC1 Signal Activity"); + snd_soc_component_disable_pin(component, "DRC1 Signal Activity"); for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) { switch (arizona->pdata.gpio_defaults[i] & ARIZONA_GPN_FN_MASK) { case ARIZONA_GP_FN_DRC1_SIGNAL_DETECT: - snd_soc_dapm_enable_pin(dapm, "DRC1 Signal Activity"); + snd_soc_component_enable_pin(component, + "DRC1 Signal Activity"); break; case ARIZONA_GP_FN_DRC2_SIGNAL_DETECT: - snd_soc_dapm_enable_pin(dapm, "DRC2 Signal Activity"); + snd_soc_component_enable_pin(component, + "DRC2 Signal Activity"); break; default: break; @@ -864,6 +875,7 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w, { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->arizona; switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -877,6 +889,18 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w, priv->out_up_pending++; priv->out_up_delay += 17; break; + case ARIZONA_OUT4L_ENA_SHIFT: + case ARIZONA_OUT4R_ENA_SHIFT: + priv->out_up_pending++; + switch (arizona->type) { + case WM5102: + case WM8997: + break; + default: + priv->out_up_delay += 10; + break; + } + break; default: break; } @@ -889,8 +913,12 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w, case ARIZONA_OUT2R_ENA_SHIFT: case ARIZONA_OUT3L_ENA_SHIFT: case ARIZONA_OUT3R_ENA_SHIFT: + case ARIZONA_OUT4L_ENA_SHIFT: + case ARIZONA_OUT4R_ENA_SHIFT: priv->out_up_pending--; - if (!priv->out_up_pending) { + if (!priv->out_up_pending && priv->out_up_delay) { + dev_dbg(codec->dev, "Power up delay: %d\n", + priv->out_up_delay); msleep(priv->out_up_delay); priv->out_up_delay = 0; } @@ -911,6 +939,21 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w, priv->out_down_pending++; priv->out_down_delay++; break; + case ARIZONA_OUT4L_ENA_SHIFT: + case ARIZONA_OUT4R_ENA_SHIFT: + priv->out_down_pending++; + switch (arizona->type) { + case WM5102: + case WM8997: + break; + case WM8998: + case WM1814: + priv->out_down_delay += 5; + break; + default: + priv->out_down_delay++; + break; + } default: break; } @@ -923,8 +966,12 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w, case ARIZONA_OUT2R_ENA_SHIFT: case ARIZONA_OUT3L_ENA_SHIFT: case ARIZONA_OUT3R_ENA_SHIFT: + case ARIZONA_OUT4L_ENA_SHIFT: + case ARIZONA_OUT4R_ENA_SHIFT: priv->out_down_pending--; - if (!priv->out_down_pending) { + if (!priv->out_down_pending && priv->out_down_delay) { + dev_dbg(codec->dev, "Power down delay: %d\n", + priv->out_down_delay); msleep(priv->out_down_delay); priv->out_down_delay = 0; } @@ -1195,6 +1242,46 @@ static int arizona_set_opclk(struct snd_soc_codec *codec, unsigned int clk, return -EINVAL; } +int arizona_clk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct arizona *arizona = dev_get_drvdata(codec->dev->parent); + unsigned int val; + int clk_idx; + int ret; + + ret = regmap_read(arizona->regmap, w->reg, &val); + if (ret) { + dev_err(codec->dev, "Failed to check clock source: %d\n", ret); + return ret; + } + + val = (val & ARIZONA_SYSCLK_SRC_MASK) >> ARIZONA_SYSCLK_SRC_SHIFT; + + switch (val) { + case ARIZONA_CLK_SRC_MCLK1: + clk_idx = ARIZONA_MCLK1; + break; + case ARIZONA_CLK_SRC_MCLK2: + clk_idx = ARIZONA_MCLK2; + break; + default: + return 0; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return clk_prepare_enable(arizona->mclk[clk_idx]); + case SND_SOC_DAPM_POST_PMD: + clk_disable_unprepare(arizona->mclk[clk_idx]); + return 0; + default: + return 0; + } +} +EXPORT_SYMBOL_GPL(arizona_clk_ev); + int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id, int source, unsigned int freq, int dir) { @@ -1920,8 +2007,8 @@ static struct { struct arizona_fll_cfg { int n; - int theta; - int lambda; + unsigned int theta; + unsigned int lambda; int refdiv; int outdiv; int fratio; @@ -2188,13 +2275,13 @@ static void arizona_apply_fll(struct arizona *arizona, unsigned int base, ARIZONA_FLL1_CTRL_UPD | cfg->n); } -static int arizona_is_enabled_fll(struct arizona_fll *fll) +static int arizona_is_enabled_fll(struct arizona_fll *fll, int base) { struct arizona *arizona = fll->arizona; unsigned int reg; int ret; - ret = regmap_read(arizona->regmap, fll->base + 1, ®); + ret = regmap_read(arizona->regmap, base + 1, ®); if (ret != 0) { arizona_fll_err(fll, "Failed to read current state: %d\n", ret); @@ -2204,25 +2291,68 @@ static int arizona_is_enabled_fll(struct arizona_fll *fll) return reg & ARIZONA_FLL1_ENA; } +static int arizona_set_fll_clks(struct arizona_fll *fll, int base, bool ena) +{ + struct arizona *arizona = fll->arizona; + unsigned int val; + struct clk *clk; + int ret; + + ret = regmap_read(arizona->regmap, base + 6, &val); + if (ret != 0) { + arizona_fll_err(fll, "Failed to read current source: %d\n", + ret); + return ret; + } + + val &= ARIZONA_FLL1_CLK_REF_SRC_MASK; + val >>= ARIZONA_FLL1_CLK_REF_SRC_SHIFT; + + switch (val) { + case ARIZONA_FLL_SRC_MCLK1: + clk = arizona->mclk[ARIZONA_MCLK1]; + break; + case ARIZONA_FLL_SRC_MCLK2: + clk = arizona->mclk[ARIZONA_MCLK2]; + break; + default: + return 0; + } + + if (ena) { + return clk_prepare_enable(clk); + } else { + clk_disable_unprepare(clk); + return 0; + } +} + static int arizona_enable_fll(struct arizona_fll *fll) { struct arizona *arizona = fll->arizona; bool use_sync = false; - int already_enabled = arizona_is_enabled_fll(fll); + int already_enabled = arizona_is_enabled_fll(fll, fll->base); + int sync_enabled = arizona_is_enabled_fll(fll, fll->base + 0x10); struct arizona_fll_cfg cfg; int i; unsigned int val; if (already_enabled < 0) return already_enabled; + if (sync_enabled < 0) + return sync_enabled; if (already_enabled) { /* Facilitate smooth refclk across the transition */ - regmap_update_bits_async(fll->arizona->regmap, fll->base + 0x9, - ARIZONA_FLL1_GAIN_MASK, 0); regmap_update_bits(fll->arizona->regmap, fll->base + 1, ARIZONA_FLL1_FREERUN, ARIZONA_FLL1_FREERUN); udelay(32); + regmap_update_bits_async(fll->arizona->regmap, fll->base + 0x9, + ARIZONA_FLL1_GAIN_MASK, 0); + + if (arizona_is_enabled_fll(fll, fll->base + 0x10) > 0) + arizona_set_fll_clks(fll, fll->base + 0x10, false); + arizona_set_fll_clks(fll, fll->base, false); } /* @@ -2233,6 +2363,10 @@ static int arizona_enable_fll(struct arizona_fll *fll) fll->ref_src != fll->sync_src) { arizona_calc_fll(fll, &cfg, fll->ref_freq, false); + /* Ref path hardcodes lambda to 65536 when sync is on */ + if (fll->sync_src >= 0 && cfg.lambda) + cfg.theta = (cfg.theta * (1 << 16)) / cfg.lambda; + arizona_apply_fll(arizona, fll->base, &cfg, fll->ref_src, false); if (fll->sync_src >= 0) { @@ -2255,6 +2389,9 @@ static int arizona_enable_fll(struct arizona_fll *fll) return -EINVAL; } + if (already_enabled && !!sync_enabled != use_sync) + arizona_fll_warn(fll, "Synchroniser changed on active FLL\n"); + /* * Increase the bandwidth if we're not using a low frequency * sync source. @@ -2268,14 +2405,17 @@ static int arizona_enable_fll(struct arizona_fll *fll) ARIZONA_FLL1_SYNC_BW); if (!already_enabled) - pm_runtime_get(arizona->dev); + pm_runtime_get_sync(arizona->dev); - regmap_update_bits_async(arizona->regmap, fll->base + 1, - ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA); - if (use_sync) + if (use_sync) { + arizona_set_fll_clks(fll, fll->base + 0x10, true); regmap_update_bits_async(arizona->regmap, fll->base + 0x11, ARIZONA_FLL1_SYNC_ENA, ARIZONA_FLL1_SYNC_ENA); + } + arizona_set_fll_clks(fll, fll->base, true); + regmap_update_bits_async(arizona->regmap, fll->base + 1, + ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA); if (already_enabled) regmap_update_bits_async(arizona->regmap, fll->base + 1, @@ -2306,19 +2446,24 @@ static int arizona_enable_fll(struct arizona_fll *fll) static void arizona_disable_fll(struct arizona_fll *fll) { struct arizona *arizona = fll->arizona; - bool change; + bool ref_change, sync_change; regmap_update_bits_async(arizona->regmap, fll->base + 1, ARIZONA_FLL1_FREERUN, ARIZONA_FLL1_FREERUN); regmap_update_bits_check(arizona->regmap, fll->base + 1, - ARIZONA_FLL1_ENA, 0, &change); - regmap_update_bits(arizona->regmap, fll->base + 0x11, - ARIZONA_FLL1_SYNC_ENA, 0); + ARIZONA_FLL1_ENA, 0, &ref_change); + regmap_update_bits_check(arizona->regmap, fll->base + 0x11, + ARIZONA_FLL1_SYNC_ENA, 0, &sync_change); regmap_update_bits_async(arizona->regmap, fll->base + 1, ARIZONA_FLL1_FREERUN, 0); - if (change) + if (sync_change) + arizona_set_fll_clks(fll, fll->base + 0x10, false); + + if (ref_change) { + arizona_set_fll_clks(fll, fll->base, false); pm_runtime_put_autosuspend(arizona->dev); + } } int arizona_set_fll_refclk(struct arizona_fll *fll, int source, @@ -2550,30 +2695,6 @@ int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(arizona_lhpf_coeff_put); -int arizona_register_notifier(struct snd_soc_codec *codec, - struct notifier_block *nb, - int (*notify)(struct notifier_block *nb, - unsigned long action, void *data)) -{ - struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); - struct arizona *arizona = priv->arizona; - - nb->notifier_call = notify; - - return blocking_notifier_chain_register(&arizona->notifier, nb); -} -EXPORT_SYMBOL_GPL(arizona_register_notifier); - -int arizona_unregister_notifier(struct snd_soc_codec *codec, - struct notifier_block *nb) -{ - struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); - struct arizona *arizona = priv->arizona; - - return blocking_notifier_chain_unregister(&arizona->notifier, nb); -} -EXPORT_SYMBOL_GPL(arizona_unregister_notifier); - MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support"); MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index 69da1ef3a17c..56707860657c 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -14,6 +14,8 @@ #define _ASOC_ARIZONA_H #include <linux/completion.h> +#include <linux/notifier.h> +#include <linux/mfd/arizona/core.h> #include <sound/soc.h> @@ -66,7 +68,6 @@ /* Notifier events */ #define ARIZONA_NOTIFY_VOICE_TRIGGER 0x1 -struct arizona; struct wm_adsp; struct arizona_dai_priv { @@ -190,20 +191,21 @@ extern unsigned int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS]; #define ARIZONA_DSP_ROUTES(name) \ { name, NULL, name " Preloader"}, \ - { name " Preloader", NULL, name " Aux 1" }, \ - { name " Preloader", NULL, name " Aux 2" }, \ - { name " Preloader", NULL, name " Aux 3" }, \ - { name " Preloader", NULL, name " Aux 4" }, \ - { name " Preloader", NULL, name " Aux 5" }, \ - { name " Preloader", NULL, name " Aux 6" }, \ + { name " Preloader", NULL, "SYSCLK" }, \ + { name, NULL, name " Aux 1" }, \ + { name, NULL, name " Aux 2" }, \ + { name, NULL, name " Aux 3" }, \ + { name, NULL, name " Aux 4" }, \ + { name, NULL, name " Aux 5" }, \ + { name, NULL, name " Aux 6" }, \ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 1"), \ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 2"), \ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 3"), \ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 4"), \ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 5"), \ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 6"), \ - ARIZONA_MIXER_ROUTES(name " Preloader", name "L"), \ - ARIZONA_MIXER_ROUTES(name " Preloader", name "R") + ARIZONA_MIXER_ROUTES(name, name "L"), \ + ARIZONA_MIXER_ROUTES(name, name "R") #define ARIZONA_EQ_CONTROL(xname, xbase) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ @@ -254,26 +256,24 @@ extern const struct soc_enum arizona_output_anc_src[]; extern const struct snd_kcontrol_new arizona_voice_trigger_switch[]; -extern int arizona_in_ev(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, - int event); -extern int arizona_out_ev(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, - int event); -extern int arizona_hp_ev(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, - int event); -extern int arizona_anc_ev(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, - int event); - -extern int arizona_eq_coeff_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -extern int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); - -extern int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id, - int source, unsigned int freq, int dir); +int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, + int event); +int arizona_out_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, + int event); +int arizona_hp_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, + int event); +int arizona_anc_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, + int event); + +int arizona_eq_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +int arizona_clk_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, + int event); +int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id, int source, + unsigned int freq, int dir); extern const struct snd_soc_dai_ops arizona_dai_ops; extern const struct snd_soc_dai_ops arizona_simple_dai_ops; @@ -296,41 +296,57 @@ struct arizona_fll { char clock_ok_name[ARIZONA_FLL_NAME_LEN]; }; -extern int arizona_dvfs_up(struct snd_soc_codec *codec, unsigned int flags); -extern int arizona_dvfs_down(struct snd_soc_codec *codec, unsigned int flags); -extern int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event); -extern void arizona_init_dvfs(struct arizona_priv *priv); - -extern int arizona_init_fll(struct arizona *arizona, int id, int base, - int lock_irq, int ok_irq, struct arizona_fll *fll); -extern int arizona_set_fll_refclk(struct arizona_fll *fll, int source, - unsigned int Fref, unsigned int Fout); -extern int arizona_set_fll(struct arizona_fll *fll, int source, +int arizona_dvfs_up(struct snd_soc_codec *codec, unsigned int flags); +int arizona_dvfs_down(struct snd_soc_codec *codec, unsigned int flags); +int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); +void arizona_init_dvfs(struct arizona_priv *priv); + +int arizona_init_fll(struct arizona *arizona, int id, int base, + int lock_irq, int ok_irq, struct arizona_fll *fll); +int arizona_set_fll_refclk(struct arizona_fll *fll, int source, unsigned int Fref, unsigned int Fout); +int arizona_set_fll(struct arizona_fll *fll, int source, + unsigned int Fref, unsigned int Fout); -extern int arizona_init_spk(struct snd_soc_codec *codec); -extern int arizona_init_gpio(struct snd_soc_codec *codec); -extern int arizona_init_mono(struct snd_soc_codec *codec); -extern int arizona_init_notifiers(struct snd_soc_codec *codec); +int arizona_init_spk(struct snd_soc_codec *codec); +int arizona_init_gpio(struct snd_soc_codec *codec); +int arizona_init_mono(struct snd_soc_codec *codec); +int arizona_init_notifiers(struct snd_soc_codec *codec); -extern int arizona_free_spk(struct snd_soc_codec *codec); +int arizona_init_spk_irqs(struct arizona *arizona); +int arizona_free_spk_irqs(struct arizona *arizona); -extern int arizona_init_dai(struct arizona_priv *priv, int dai); +int arizona_init_dai(struct arizona_priv *priv, int dai); int arizona_set_output_mode(struct snd_soc_codec *codec, int output, bool diff); -extern bool arizona_input_analog(struct snd_soc_codec *codec, int shift); +bool arizona_input_analog(struct snd_soc_codec *codec, int shift); + +const char *arizona_sample_rate_val_to_name(unsigned int rate_val); + +static inline int arizona_register_notifier(struct snd_soc_codec *codec, + struct notifier_block *nb, + int (*notify) + (struct notifier_block *nb, + unsigned long action, void *data)) +{ + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->arizona; + + nb->notifier_call = notify; + + return blocking_notifier_chain_register(&arizona->notifier, nb); +} -extern const char *arizona_sample_rate_val_to_name(unsigned int rate_val); +static inline int arizona_unregister_notifier(struct snd_soc_codec *codec, + struct notifier_block *nb) +{ + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->arizona; -extern int arizona_register_notifier(struct snd_soc_codec *codec, - struct notifier_block *nb, - int (*notify)(struct notifier_block *nb, - unsigned long action, - void *data)); -extern int arizona_unregister_notifier(struct snd_soc_codec *codec, - struct notifier_block *nb); + return blocking_notifier_chain_unregister(&arizona->notifier, nb); +} #endif diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c index 2a8d0ee141d4..8014e697d540 100644 --- a/sound/soc/codecs/bt-sco.c +++ b/sound/soc/codecs/bt-sco.c @@ -63,10 +63,12 @@ static struct snd_soc_dai_driver bt_sco_dai[] = { }; static struct snd_soc_codec_driver soc_codec_dev_bt_sco = { - .dapm_widgets = bt_sco_widgets, - .num_dapm_widgets = ARRAY_SIZE(bt_sco_widgets), - .dapm_routes = bt_sco_routes, - .num_dapm_routes = ARRAY_SIZE(bt_sco_routes), + .component_driver = { + .dapm_widgets = bt_sco_widgets, + .num_dapm_widgets = ARRAY_SIZE(bt_sco_widgets), + .dapm_routes = bt_sco_routes, + .num_dapm_routes = ARRAY_SIZE(bt_sco_routes), + }, }; static int bt_sco_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c index 1c895a53001d..7a2d9a2ee427 100644 --- a/sound/soc/codecs/cq93vc.c +++ b/sound/soc/codecs/cq93vc.c @@ -131,8 +131,10 @@ static struct regmap *cq93vc_get_regmap(struct device *dev) static struct snd_soc_codec_driver soc_codec_dev_cq93vc = { .set_bias_level = cq93vc_set_bias_level, .get_regmap = cq93vc_get_regmap, - .controls = cq93vc_snd_controls, - .num_controls = ARRAY_SIZE(cq93vc_snd_controls), + .component_driver = { + .controls = cq93vc_snd_controls, + .num_controls = ARRAY_SIZE(cq93vc_snd_controls), + }, }; static int cq93vc_platform_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c index 287d13740be4..7e9806206648 100644 --- a/sound/soc/codecs/cs35l32.c +++ b/sound/soc/codecs/cs35l32.c @@ -231,13 +231,14 @@ static int cs35l32_codec_set_sysclk(struct snd_soc_codec *codec, static const 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), + .component_driver = { + .controls = cs35l32_snd_controls, + .num_controls = ARRAY_SIZE(cs35l32_snd_controls), + .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), + }, }; /* Current and threshold powerup sequence Pg37 in datasheet */ diff --git a/sound/soc/codecs/cs35l33.c b/sound/soc/codecs/cs35l33.c index 6f9c1addcd7f..6df29fa30fb9 100644 --- a/sound/soc/codecs/cs35l33.c +++ b/sound/soc/codecs/cs35l33.c @@ -837,13 +837,14 @@ static struct snd_soc_codec_driver soc_codec_dev_cs35l33 = { .set_bias_level = cs35l33_set_bias_level, .set_sysclk = cs35l33_codec_set_sysclk, - .dapm_widgets = cs35l33_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(cs35l33_dapm_widgets), - .dapm_routes = cs35l33_audio_map, - .num_dapm_routes = ARRAY_SIZE(cs35l33_audio_map), - .controls = cs35l33_snd_controls, - .num_controls = ARRAY_SIZE(cs35l33_snd_controls), - + .component_driver = { + .controls = cs35l33_snd_controls, + .num_controls = ARRAY_SIZE(cs35l33_snd_controls), + .dapm_widgets = cs35l33_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs35l33_dapm_widgets), + .dapm_routes = cs35l33_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs35l33_audio_map), + }, .idle_bias_off = true, }; diff --git a/sound/soc/codecs/cs35l34.c b/sound/soc/codecs/cs35l34.c new file mode 100644 index 000000000000..7c5d1510cf2c --- /dev/null +++ b/sound/soc/codecs/cs35l34.c @@ -0,0 +1,1251 @@ +/* + * cs35l34.c -- CS35l34 ALSA SoC audio driver + * + * Copyright 2016 Cirrus Logic, Inc. + * + * Author: Paul Handrigan <Paul.Handrigan@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/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/workqueue.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/regulator/machine.h> +#include <linux/pm_runtime.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/of_irq.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <sound/cs35l34.h> + +#include "cs35l34.h" + +#define PDN_DONE_ATTEMPTS 10 +#define CS35L34_START_DELAY 50 + +struct cs35l34_private { + struct snd_soc_codec *codec; + struct cs35l34_platform_data pdata; + struct regmap *regmap; + struct regulator_bulk_data core_supplies[2]; + int num_core_supplies; + int mclk_int; + bool tdm_mode; + struct gpio_desc *reset_gpio; /* Active-low reset GPIO */ +}; + +static const struct reg_default cs35l34_reg[] = { + {CS35L34_PWRCTL1, 0x01}, + {CS35L34_PWRCTL2, 0x19}, + {CS35L34_PWRCTL3, 0x01}, + {CS35L34_ADSP_CLK_CTL, 0x08}, + {CS35L34_MCLK_CTL, 0x11}, + {CS35L34_AMP_INP_DRV_CTL, 0x01}, + {CS35L34_AMP_DIG_VOL_CTL, 0x12}, + {CS35L34_AMP_DIG_VOL, 0x00}, + {CS35L34_AMP_ANLG_GAIN_CTL, 0x0F}, + {CS35L34_PROTECT_CTL, 0x06}, + {CS35L34_AMP_KEEP_ALIVE_CTL, 0x04}, + {CS35L34_BST_CVTR_V_CTL, 0x00}, + {CS35L34_BST_PEAK_I, 0x10}, + {CS35L34_BST_RAMP_CTL, 0x87}, + {CS35L34_BST_CONV_COEF_1, 0x24}, + {CS35L34_BST_CONV_COEF_2, 0x24}, + {CS35L34_BST_CONV_SLOPE_COMP, 0x4E}, + {CS35L34_BST_CONV_SW_FREQ, 0x08}, + {CS35L34_CLASS_H_CTL, 0x0D}, + {CS35L34_CLASS_H_HEADRM_CTL, 0x0D}, + {CS35L34_CLASS_H_RELEASE_RATE, 0x08}, + {CS35L34_CLASS_H_FET_DRIVE_CTL, 0x41}, + {CS35L34_CLASS_H_STATUS, 0x05}, + {CS35L34_VPBR_CTL, 0x0A}, + {CS35L34_VPBR_VOL_CTL, 0x90}, + {CS35L34_VPBR_TIMING_CTL, 0x6A}, + {CS35L34_PRED_MAX_ATTEN_SPK_LOAD, 0x95}, + {CS35L34_PRED_BROWNOUT_THRESH, 0x1C}, + {CS35L34_PRED_BROWNOUT_VOL_CTL, 0x00}, + {CS35L34_PRED_BROWNOUT_RATE_CTL, 0x10}, + {CS35L34_PRED_WAIT_CTL, 0x10}, + {CS35L34_PRED_ZVP_INIT_IMP_CTL, 0x08}, + {CS35L34_PRED_MAN_SAFE_VPI_CTL, 0x80}, + {CS35L34_VPBR_ATTEN_STATUS, 0x00}, + {CS35L34_PRED_BRWNOUT_ATT_STATUS, 0x00}, + {CS35L34_SPKR_MON_CTL, 0xC6}, + {CS35L34_ADSP_I2S_CTL, 0x00}, + {CS35L34_ADSP_TDM_CTL, 0x00}, + {CS35L34_TDM_TX_CTL_1_VMON, 0x00}, + {CS35L34_TDM_TX_CTL_2_IMON, 0x04}, + {CS35L34_TDM_TX_CTL_3_VPMON, 0x03}, + {CS35L34_TDM_TX_CTL_4_VBSTMON, 0x07}, + {CS35L34_TDM_TX_CTL_5_FLAG1, 0x08}, + {CS35L34_TDM_TX_CTL_6_FLAG2, 0x09}, + {CS35L34_TDM_TX_SLOT_EN_1, 0x00}, + {CS35L34_TDM_TX_SLOT_EN_2, 0x00}, + {CS35L34_TDM_TX_SLOT_EN_3, 0x00}, + {CS35L34_TDM_TX_SLOT_EN_4, 0x00}, + {CS35L34_TDM_RX_CTL_1_AUDIN, 0x40}, + {CS35L34_TDM_RX_CTL_3_ALIVE, 0x04}, + {CS35L34_MULT_DEV_SYNCH1, 0x00}, + {CS35L34_MULT_DEV_SYNCH2, 0x80}, + {CS35L34_PROT_RELEASE_CTL, 0x00}, + {CS35L34_DIAG_MODE_REG_LOCK, 0x00}, + {CS35L34_DIAG_MODE_CTL_1, 0x00}, + {CS35L34_DIAG_MODE_CTL_2, 0x00}, + {CS35L34_INT_MASK_1, 0xFF}, + {CS35L34_INT_MASK_2, 0xFF}, + {CS35L34_INT_MASK_3, 0xFF}, + {CS35L34_INT_MASK_4, 0xFF}, + {CS35L34_INT_STATUS_1, 0x30}, + {CS35L34_INT_STATUS_2, 0x05}, + {CS35L34_INT_STATUS_3, 0x00}, + {CS35L34_INT_STATUS_4, 0x00}, + {CS35L34_OTP_TRIM_STATUS, 0x00}, +}; + +static bool cs35l34_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L34_DEVID_AB: + case CS35L34_DEVID_CD: + case CS35L34_DEVID_E: + case CS35L34_FAB_ID: + case CS35L34_REV_ID: + case CS35L34_INT_STATUS_1: + case CS35L34_INT_STATUS_2: + case CS35L34_INT_STATUS_3: + case CS35L34_INT_STATUS_4: + case CS35L34_CLASS_H_STATUS: + case CS35L34_VPBR_ATTEN_STATUS: + case CS35L34_OTP_TRIM_STATUS: + return true; + default: + return false; + } +} + +static bool cs35l34_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L34_DEVID_AB: + case CS35L34_DEVID_CD: + case CS35L34_DEVID_E: + case CS35L34_FAB_ID: + case CS35L34_REV_ID: + case CS35L34_PWRCTL1: + case CS35L34_PWRCTL2: + case CS35L34_PWRCTL3: + case CS35L34_ADSP_CLK_CTL: + case CS35L34_MCLK_CTL: + case CS35L34_AMP_INP_DRV_CTL: + case CS35L34_AMP_DIG_VOL_CTL: + case CS35L34_AMP_DIG_VOL: + case CS35L34_AMP_ANLG_GAIN_CTL: + case CS35L34_PROTECT_CTL: + case CS35L34_AMP_KEEP_ALIVE_CTL: + case CS35L34_BST_CVTR_V_CTL: + case CS35L34_BST_PEAK_I: + case CS35L34_BST_RAMP_CTL: + case CS35L34_BST_CONV_COEF_1: + case CS35L34_BST_CONV_COEF_2: + case CS35L34_BST_CONV_SLOPE_COMP: + case CS35L34_BST_CONV_SW_FREQ: + case CS35L34_CLASS_H_CTL: + case CS35L34_CLASS_H_HEADRM_CTL: + case CS35L34_CLASS_H_RELEASE_RATE: + case CS35L34_CLASS_H_FET_DRIVE_CTL: + case CS35L34_CLASS_H_STATUS: + case CS35L34_VPBR_CTL: + case CS35L34_VPBR_VOL_CTL: + case CS35L34_VPBR_TIMING_CTL: + case CS35L34_PRED_MAX_ATTEN_SPK_LOAD: + case CS35L34_PRED_BROWNOUT_THRESH: + case CS35L34_PRED_BROWNOUT_VOL_CTL: + case CS35L34_PRED_BROWNOUT_RATE_CTL: + case CS35L34_PRED_WAIT_CTL: + case CS35L34_PRED_ZVP_INIT_IMP_CTL: + case CS35L34_PRED_MAN_SAFE_VPI_CTL: + case CS35L34_VPBR_ATTEN_STATUS: + case CS35L34_PRED_BRWNOUT_ATT_STATUS: + case CS35L34_SPKR_MON_CTL: + case CS35L34_ADSP_I2S_CTL: + case CS35L34_ADSP_TDM_CTL: + case CS35L34_TDM_TX_CTL_1_VMON: + case CS35L34_TDM_TX_CTL_2_IMON: + case CS35L34_TDM_TX_CTL_3_VPMON: + case CS35L34_TDM_TX_CTL_4_VBSTMON: + case CS35L34_TDM_TX_CTL_5_FLAG1: + case CS35L34_TDM_TX_CTL_6_FLAG2: + case CS35L34_TDM_TX_SLOT_EN_1: + case CS35L34_TDM_TX_SLOT_EN_2: + case CS35L34_TDM_TX_SLOT_EN_3: + case CS35L34_TDM_TX_SLOT_EN_4: + case CS35L34_TDM_RX_CTL_1_AUDIN: + case CS35L34_TDM_RX_CTL_3_ALIVE: + case CS35L34_MULT_DEV_SYNCH1: + case CS35L34_MULT_DEV_SYNCH2: + case CS35L34_PROT_RELEASE_CTL: + case CS35L34_DIAG_MODE_REG_LOCK: + case CS35L34_DIAG_MODE_CTL_1: + case CS35L34_DIAG_MODE_CTL_2: + case CS35L34_INT_MASK_1: + case CS35L34_INT_MASK_2: + case CS35L34_INT_MASK_3: + case CS35L34_INT_MASK_4: + case CS35L34_INT_STATUS_1: + case CS35L34_INT_STATUS_2: + case CS35L34_INT_STATUS_3: + case CS35L34_INT_STATUS_4: + case CS35L34_OTP_TRIM_STATUS: + return true; + default: + return false; + } +} + +static bool cs35l34_precious_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L34_INT_STATUS_1: + case CS35L34_INT_STATUS_2: + case CS35L34_INT_STATUS_3: + case CS35L34_INT_STATUS_4: + return true; + default: + return false; + } +} + +static int cs35l34_sdin_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct cs35l34_private *priv = snd_soc_codec_get_drvdata(codec); + int ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (priv->tdm_mode) + regmap_update_bits(priv->regmap, CS35L34_PWRCTL3, + CS35L34_PDN_TDM, 0x00); + + ret = regmap_update_bits(priv->regmap, CS35L34_PWRCTL1, + CS35L34_PDN_ALL, 0); + if (ret < 0) { + dev_err(codec->dev, "Cannot set Power bits %d\n", ret); + return ret; + } + usleep_range(5000, 5100); + break; + case SND_SOC_DAPM_POST_PMD: + if (priv->tdm_mode) { + regmap_update_bits(priv->regmap, CS35L34_PWRCTL3, + CS35L34_PDN_TDM, CS35L34_PDN_TDM); + } + ret = regmap_update_bits(priv->regmap, CS35L34_PWRCTL1, + CS35L34_PDN_ALL, CS35L34_PDN_ALL); + break; + default: + pr_err("Invalid event = 0x%x\n", event); + } + return 0; +} + +static int cs35l34_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; + struct cs35l34_private *priv = snd_soc_codec_get_drvdata(codec); + unsigned int reg, bit_pos; + int slot, slot_num; + + if (slot_width != 8) + return -EINVAL; + + priv->tdm_mode = true; + /* scan rx_mask for aud slot */ + slot = ffs(rx_mask) - 1; + if (slot >= 0) + snd_soc_update_bits(codec, CS35L34_TDM_RX_CTL_1_AUDIN, + CS35L34_X_LOC, slot); + + /* scan tx_mask: vmon(2 slots); imon (2 slots); vpmon (1 slot) + * vbstmon (1 slot) + */ + slot = ffs(tx_mask) - 1; + slot_num = 0; + + /* disable vpmon/vbstmon: enable later if set in tx_mask */ + snd_soc_update_bits(codec, CS35L34_TDM_TX_CTL_3_VPMON, + CS35L34_X_STATE | CS35L34_X_LOC, + CS35L34_X_STATE | CS35L34_X_LOC); + snd_soc_update_bits(codec, CS35L34_TDM_TX_CTL_4_VBSTMON, + CS35L34_X_STATE | CS35L34_X_LOC, + CS35L34_X_STATE | CS35L34_X_LOC); + + /* disconnect {vp,vbst}_mon routes: eanble later if set in tx_mask*/ + while (slot >= 0) { + /* configure VMON_TX_LOC */ + if (slot_num == 0) + snd_soc_update_bits(codec, CS35L34_TDM_TX_CTL_1_VMON, + CS35L34_X_STATE | CS35L34_X_LOC, slot); + + /* configure IMON_TX_LOC */ + if (slot_num == 4) { + snd_soc_update_bits(codec, CS35L34_TDM_TX_CTL_2_IMON, + CS35L34_X_STATE | CS35L34_X_LOC, slot); + } + /* configure VPMON_TX_LOC */ + if (slot_num == 3) { + snd_soc_update_bits(codec, CS35L34_TDM_TX_CTL_3_VPMON, + CS35L34_X_STATE | CS35L34_X_LOC, slot); + } + /* configure VBSTMON_TX_LOC */ + if (slot_num == 7) { + snd_soc_update_bits(codec, + CS35L34_TDM_TX_CTL_4_VBSTMON, + CS35L34_X_STATE | CS35L34_X_LOC, slot); + } + + /* Enable the relevant tx slot */ + reg = CS35L34_TDM_TX_SLOT_EN_4 - (slot/8); + bit_pos = slot - ((slot / 8) * (8)); + snd_soc_update_bits(codec, reg, + 1 << bit_pos, 1 << bit_pos); + + tx_mask &= ~(1 << slot); + slot = ffs(tx_mask) - 1; + slot_num++; + } + + return 0; +} + +static int cs35l34_main_amp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct cs35l34_private *priv = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(priv->regmap, CS35L34_BST_CVTR_V_CTL, + CS35L34_BST_CVTL_MASK, priv->pdata.boost_vtge); + usleep_range(5000, 5100); + regmap_update_bits(priv->regmap, CS35L34_PROTECT_CTL, + CS35L34_MUTE, 0); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(priv->regmap, CS35L34_BST_CVTR_V_CTL, + CS35L34_BST_CVTL_MASK, 0); + regmap_update_bits(priv->regmap, CS35L34_PROTECT_CTL, + CS35L34_MUTE, CS35L34_MUTE); + usleep_range(5000, 5100); + break; + default: + pr_err("Invalid event = 0x%x\n", event); + } + return 0; +} + +static DECLARE_TLV_DB_SCALE(dig_vol_tlv, -10200, 50, 0); + +static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 300, 100, 0); + + +static const struct snd_kcontrol_new cs35l34_snd_controls[] = { + SOC_SINGLE_SX_TLV("Digital Volume", CS35L34_AMP_DIG_VOL, + 0, 0x34, 0xE4, dig_vol_tlv), + SOC_SINGLE_TLV("Amp Gain Volume", CS35L34_AMP_ANLG_GAIN_CTL, + 0, 0xF, 0, amp_gain_tlv), +}; + + +static int cs35l34_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct cs35l34_private *priv = snd_soc_codec_get_drvdata(codec); + int ret, i; + unsigned int reg; + + switch (event) { + case SND_SOC_DAPM_PRE_PMD: + ret = regmap_read(priv->regmap, CS35L34_AMP_DIG_VOL_CTL, + ®); + if (ret != 0) { + pr_err("%s regmap read failure %d\n", __func__, ret); + return ret; + } + if (reg & CS35L34_AMP_DIGSFT) + msleep(40); + else + usleep_range(2000, 2100); + + for (i = 0; i < PDN_DONE_ATTEMPTS; i++) { + ret = regmap_read(priv->regmap, CS35L34_INT_STATUS_2, + ®); + if (ret != 0) { + pr_err("%s regmap read failure %d\n", + __func__, ret); + return ret; + } + if (reg & CS35L34_PDN_DONE) + break; + + usleep_range(5000, 5100); + } + if (i == PDN_DONE_ATTEMPTS) + pr_err("%s Device did not power down properly\n", + __func__); + break; + default: + pr_err("Invalid event = 0x%x\n", event); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget cs35l34_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN_E("SDIN", NULL, 0, CS35L34_PWRCTL3, + 1, 1, cs35l34_sdin_event, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, CS35L34_PWRCTL3, 2, 1), + + SND_SOC_DAPM_SUPPLY("EXTCLK", CS35L34_PWRCTL3, 7, 1, + cs35l34_mclk_event, SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_OUTPUT("SPK"), + + SND_SOC_DAPM_INPUT("VP"), + SND_SOC_DAPM_INPUT("VPST"), + SND_SOC_DAPM_INPUT("ISENSE"), + SND_SOC_DAPM_INPUT("VSENSE"), + + SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L34_PWRCTL2, 7, 1), + SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L34_PWRCTL2, 6, 1), + SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L34_PWRCTL3, 3, 1), + SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, CS35L34_PWRCTL3, 4, 1), + SND_SOC_DAPM_ADC("CLASS H", NULL, CS35L34_PWRCTL2, 5, 1), + SND_SOC_DAPM_ADC("BOOST", NULL, CS35L34_PWRCTL2, 2, 1), + + SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L34_PWRCTL2, 0, 1, NULL, 0, + cs35l34_main_amp_event, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route cs35l34_audio_map[] = { + {"SDIN", NULL, "AMP Playback"}, + {"BOOST", NULL, "SDIN"}, + {"CLASS H", NULL, "BOOST"}, + {"Main AMP", NULL, "CLASS H"}, + {"SPK", NULL, "Main AMP"}, + + {"VPMON ADC", NULL, "CLASS H"}, + {"VBSTMON ADC", NULL, "CLASS H"}, + {"SPK", NULL, "VPMON ADC"}, + {"SPK", NULL, "VBSTMON ADC"}, + + {"IMON ADC", NULL, "ISENSE"}, + {"VMON ADC", NULL, "VSENSE"}, + {"SDOUT", NULL, "IMON ADC"}, + {"SDOUT", NULL, "VMON ADC"}, + {"AMP Capture", NULL, "SDOUT"}, + + {"SDIN", NULL, "EXTCLK"}, + {"SDOUT", NULL, "EXTCLK"}, +}; + +struct cs35l34_mclk_div { + int mclk; + int srate; + u8 adsp_rate; +}; + +static struct cs35l34_mclk_div cs35l34_mclk_coeffs[] = { + + /* MCLK, Sample Rate, adsp_rate */ + + {5644800, 11025, 0x1}, + {5644800, 22050, 0x4}, + {5644800, 44100, 0x7}, + + {6000000, 8000, 0x0}, + {6000000, 11025, 0x1}, + {6000000, 12000, 0x2}, + {6000000, 16000, 0x3}, + {6000000, 22050, 0x4}, + {6000000, 24000, 0x5}, + {6000000, 32000, 0x6}, + {6000000, 44100, 0x7}, + {6000000, 48000, 0x8}, + + {6144000, 8000, 0x0}, + {6144000, 11025, 0x1}, + {6144000, 12000, 0x2}, + {6144000, 16000, 0x3}, + {6144000, 22050, 0x4}, + {6144000, 24000, 0x5}, + {6144000, 32000, 0x6}, + {6144000, 44100, 0x7}, + {6144000, 48000, 0x8}, +}; + +static int cs35l34_get_mclk_coeff(int mclk, int srate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cs35l34_mclk_coeffs); i++) { + if (cs35l34_mclk_coeffs[i].mclk == mclk && + cs35l34_mclk_coeffs[i].srate == srate) + return i; + } + return -EINVAL; +} + +static int cs35l34_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct cs35l34_private *priv = snd_soc_codec_get_drvdata(codec); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + regmap_update_bits(priv->regmap, CS35L34_ADSP_CLK_CTL, + 0x80, 0x80); + break; + case SND_SOC_DAIFMT_CBS_CFS: + regmap_update_bits(priv->regmap, CS35L34_ADSP_CLK_CTL, + 0x80, 0x00); + break; + default: + return -EINVAL; + } + return 0; +} + +static int cs35l34_pcm_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 cs35l34_private *priv = snd_soc_codec_get_drvdata(codec); + int srate = params_rate(params); + int ret; + + int coeff = cs35l34_get_mclk_coeff(priv->mclk_int, srate); + + if (coeff < 0) { + dev_err(codec->dev, "ERROR: Invalid mclk %d and/or srate %d\n", + priv->mclk_int, srate); + return coeff; + } + + ret = regmap_update_bits(priv->regmap, CS35L34_ADSP_CLK_CTL, + CS35L34_ADSP_RATE, cs35l34_mclk_coeffs[coeff].adsp_rate); + if (ret != 0) + dev_err(codec->dev, "Failed to set clock state %d\n", ret); + + return ret; +} + +static unsigned int cs35l34_src_rates[] = { + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 +}; + + +static struct snd_pcm_hw_constraint_list cs35l34_constraints = { + .count = ARRAY_SIZE(cs35l34_src_rates), + .list = cs35l34_src_rates, +}; + +static int cs35l34_pcm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &cs35l34_constraints); + return 0; +} + + +static int cs35l34_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + + struct snd_soc_codec *codec = dai->codec; + + if (tristate) + snd_soc_update_bits(codec, CS35L34_PWRCTL3, + CS35L34_PDN_SDOUT, CS35L34_PDN_SDOUT); + else + snd_soc_update_bits(codec, CS35L34_PWRCTL3, + CS35L34_PDN_SDOUT, 0); + return 0; +} + +static int cs35l34_dai_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct cs35l34_private *cs35l34 = snd_soc_codec_get_drvdata(codec); + unsigned int value; + + switch (freq) { + case CS35L34_MCLK_5644: + value = CS35L34_MCLK_RATE_5P6448; + cs35l34->mclk_int = freq; + break; + case CS35L34_MCLK_6: + value = CS35L34_MCLK_RATE_6P0000; + cs35l34->mclk_int = freq; + break; + case CS35L34_MCLK_6144: + value = CS35L34_MCLK_RATE_6P1440; + cs35l34->mclk_int = freq; + break; + case CS35L34_MCLK_11289: + value = CS35L34_MCLK_DIV | CS35L34_MCLK_RATE_5P6448; + cs35l34->mclk_int = freq / 2; + break; + case CS35L34_MCLK_12: + value = CS35L34_MCLK_DIV | CS35L34_MCLK_RATE_6P0000; + cs35l34->mclk_int = freq / 2; + break; + case CS35L34_MCLK_12288: + value = CS35L34_MCLK_DIV | CS35L34_MCLK_RATE_6P1440; + cs35l34->mclk_int = freq / 2; + break; + default: + dev_err(codec->dev, "ERROR: Invalid Frequency %d\n", freq); + cs35l34->mclk_int = 0; + return -EINVAL; + } + regmap_update_bits(cs35l34->regmap, CS35L34_MCLK_CTL, + CS35L34_MCLK_DIV | CS35L34_MCLK_RATE_MASK, value); + return 0; +} + +static const struct snd_soc_dai_ops cs35l34_ops = { + .startup = cs35l34_pcm_startup, + .set_tristate = cs35l34_set_tristate, + .set_fmt = cs35l34_set_dai_fmt, + .hw_params = cs35l34_pcm_hw_params, + .set_sysclk = cs35l34_dai_set_sysclk, + .set_tdm_slot = cs35l34_set_tdm_slot, +}; + +static struct snd_soc_dai_driver cs35l34_dai = { + .name = "cs35l34", + .id = 0, + .playback = { + .stream_name = "AMP Playback", + .channels_min = 1, + .channels_max = 8, + .rates = CS35L34_RATES, + .formats = CS35L34_FORMATS, + }, + .capture = { + .stream_name = "AMP Capture", + .channels_min = 1, + .channels_max = 8, + .rates = CS35L34_RATES, + .formats = CS35L34_FORMATS, + }, + .ops = &cs35l34_ops, + .symmetric_rates = 1, +}; + +static int cs35l34_boost_inductor(struct cs35l34_private *cs35l34, + unsigned int inductor) +{ + struct snd_soc_codec *codec = cs35l34->codec; + + switch (inductor) { + case 1000: /* 1 uH */ + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_1, 0x24); + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_2, 0x24); + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SLOPE_COMP, + 0x4E); + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SW_FREQ, 0); + break; + case 1200: /* 1.2 uH */ + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_1, 0x20); + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_2, 0x20); + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SLOPE_COMP, + 0x47); + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SW_FREQ, 1); + break; + case 1500: /* 1.5uH */ + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_1, 0x20); + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_2, 0x20); + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SLOPE_COMP, + 0x3C); + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SW_FREQ, 2); + break; + case 2200: /* 2.2uH */ + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_1, 0x19); + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_2, 0x25); + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SLOPE_COMP, + 0x23); + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SW_FREQ, 3); + break; + default: + dev_err(codec->dev, "%s Invalid Inductor Value %d uH\n", + __func__, inductor); + return -EINVAL; + } + return 0; +} + +static int cs35l34_probe(struct snd_soc_codec *codec) +{ + int ret = 0; + struct cs35l34_private *cs35l34 = snd_soc_codec_get_drvdata(codec); + + pm_runtime_get_sync(codec->dev); + + /* Set over temperature warning attenuation to 6 dB */ + regmap_update_bits(cs35l34->regmap, CS35L34_PROTECT_CTL, + CS35L34_OTW_ATTN_MASK, 0x8); + + /* Set Power control registers 2 and 3 to have everything + * powered down at initialization + */ + regmap_write(cs35l34->regmap, CS35L34_PWRCTL2, 0xFD); + regmap_write(cs35l34->regmap, CS35L34_PWRCTL3, 0x1F); + + /* Set mute bit at startup */ + regmap_update_bits(cs35l34->regmap, CS35L34_PROTECT_CTL, + CS35L34_MUTE, CS35L34_MUTE); + + /* Set Platform Data */ + if (cs35l34->pdata.boost_peak) + regmap_update_bits(cs35l34->regmap, CS35L34_BST_PEAK_I, + CS35L34_BST_PEAK_MASK, + cs35l34->pdata.boost_peak); + + if (cs35l34->pdata.gain_zc_disable) + regmap_update_bits(cs35l34->regmap, CS35L34_PROTECT_CTL, + CS35L34_GAIN_ZC_MASK, 0); + else + regmap_update_bits(cs35l34->regmap, CS35L34_PROTECT_CTL, + CS35L34_GAIN_ZC_MASK, CS35L34_GAIN_ZC_MASK); + + if (cs35l34->pdata.aif_half_drv) + regmap_update_bits(cs35l34->regmap, CS35L34_ADSP_CLK_CTL, + CS35L34_ADSP_DRIVE, 0); + + if (cs35l34->pdata.digsft_disable) + regmap_update_bits(cs35l34->regmap, CS35L34_AMP_DIG_VOL_CTL, + CS35L34_AMP_DIGSFT, 0); + + if (cs35l34->pdata.amp_inv) + regmap_update_bits(cs35l34->regmap, CS35L34_AMP_DIG_VOL_CTL, + CS35L34_INV, CS35L34_INV); + + if (cs35l34->pdata.boost_ind) + ret = cs35l34_boost_inductor(cs35l34, cs35l34->pdata.boost_ind); + + if (cs35l34->pdata.i2s_sdinloc) + regmap_update_bits(cs35l34->regmap, CS35L34_ADSP_I2S_CTL, + CS35L34_I2S_LOC_MASK, + cs35l34->pdata.i2s_sdinloc << CS35L34_I2S_LOC_SHIFT); + + if (cs35l34->pdata.tdm_rising_edge) + regmap_update_bits(cs35l34->regmap, CS35L34_ADSP_TDM_CTL, + 1, 1); + + pm_runtime_put_sync(codec->dev); + + return ret; +} + + +static struct snd_soc_codec_driver soc_codec_dev_cs35l34 = { + .probe = cs35l34_probe, + + .component_driver = { + .dapm_widgets = cs35l34_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs35l34_dapm_widgets), + .dapm_routes = cs35l34_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs35l34_audio_map), + .controls = cs35l34_snd_controls, + .num_controls = ARRAY_SIZE(cs35l34_snd_controls), + }, +}; + +static struct regmap_config cs35l34_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = CS35L34_MAX_REGISTER, + .reg_defaults = cs35l34_reg, + .num_reg_defaults = ARRAY_SIZE(cs35l34_reg), + .volatile_reg = cs35l34_volatile_register, + .readable_reg = cs35l34_readable_register, + .precious_reg = cs35l34_precious_register, + .cache_type = REGCACHE_RBTREE, +}; + +static int cs35l34_handle_of_data(struct i2c_client *i2c_client, + struct cs35l34_platform_data *pdata) +{ + struct device_node *np = i2c_client->dev.of_node; + unsigned int val; + + if (of_property_read_u32(np, "cirrus,boost-vtge-millivolt", + &val) >= 0) { + /* Boost Voltage has a maximum of 8V */ + if (val > 8000 || (val < 3300 && val > 0)) { + dev_err(&i2c_client->dev, + "Invalid Boost Voltage %d mV\n", val); + return -EINVAL; + } + if (val == 0) + pdata->boost_vtge = 0; /* Use VP */ + else + pdata->boost_vtge = ((val - 3300)/100) + 1; + } else { + dev_warn(&i2c_client->dev, + "Boost Voltage not specified. Using VP\n"); + } + + if (of_property_read_u32(np, "cirrus,boost-ind-nanohenry", &val) >= 0) { + pdata->boost_ind = val; + } else { + dev_err(&i2c_client->dev, "Inductor not specified.\n"); + return -EINVAL; + } + + if (of_property_read_u32(np, "cirrus,boost-peak-milliamp", &val) >= 0) { + if (val > 3840 || val < 1200) { + dev_err(&i2c_client->dev, + "Invalid Boost Peak Current %d mA\n", val); + return -EINVAL; + } + pdata->boost_peak = ((val - 1200)/80) + 1; + } + + pdata->aif_half_drv = of_property_read_bool(np, + "cirrus,aif-half-drv"); + pdata->digsft_disable = of_property_read_bool(np, + "cirrus,digsft-disable"); + + pdata->gain_zc_disable = of_property_read_bool(np, + "cirrus,gain-zc-disable"); + pdata->amp_inv = of_property_read_bool(np, "cirrus,amp-inv"); + + if (of_property_read_u32(np, "cirrus,i2s-sdinloc", &val) >= 0) + pdata->i2s_sdinloc = val; + if (of_property_read_u32(np, "cirrus,tdm-rising-edge", &val) >= 0) + pdata->tdm_rising_edge = val; + + return 0; +} + +static irqreturn_t cs35l34_irq_thread(int irq, void *data) +{ + struct cs35l34_private *cs35l34 = data; + struct snd_soc_codec *codec = cs35l34->codec; + unsigned int sticky1, sticky2, sticky3, sticky4; + unsigned int mask1, mask2, mask3, mask4, current1; + + + /* ack the irq by reading all status registers */ + regmap_read(cs35l34->regmap, CS35L34_INT_STATUS_4, &sticky4); + regmap_read(cs35l34->regmap, CS35L34_INT_STATUS_3, &sticky3); + regmap_read(cs35l34->regmap, CS35L34_INT_STATUS_2, &sticky2); + regmap_read(cs35l34->regmap, CS35L34_INT_STATUS_1, &sticky1); + + regmap_read(cs35l34->regmap, CS35L34_INT_MASK_4, &mask4); + regmap_read(cs35l34->regmap, CS35L34_INT_MASK_3, &mask3); + regmap_read(cs35l34->regmap, CS35L34_INT_MASK_2, &mask2); + regmap_read(cs35l34->regmap, CS35L34_INT_MASK_1, &mask1); + + if (!(sticky1 & ~mask1) && !(sticky2 & ~mask2) && !(sticky3 & ~mask3) + && !(sticky4 & ~mask4)) + return IRQ_NONE; + + regmap_read(cs35l34->regmap, CS35L34_INT_STATUS_1, ¤t1); + + if (sticky1 & CS35L34_CAL_ERR) { + dev_err(codec->dev, "Cal error\n"); + + /* error is no longer asserted; safe to reset */ + if (!(current1 & CS35L34_CAL_ERR)) { + dev_dbg(codec->dev, "Cal error release\n"); + regmap_update_bits(cs35l34->regmap, + CS35L34_PROT_RELEASE_CTL, + CS35L34_CAL_ERR_RLS, 0); + regmap_update_bits(cs35l34->regmap, + CS35L34_PROT_RELEASE_CTL, + CS35L34_CAL_ERR_RLS, + CS35L34_CAL_ERR_RLS); + regmap_update_bits(cs35l34->regmap, + CS35L34_PROT_RELEASE_CTL, + CS35L34_CAL_ERR_RLS, 0); + /* note: amp will re-calibrate on next resume */ + } + } + + if (sticky1 & CS35L34_ALIVE_ERR) + dev_err(codec->dev, "Alive error\n"); + + if (sticky1 & CS35L34_AMP_SHORT) { + dev_crit(codec->dev, "Amp short error\n"); + + /* error is no longer asserted; safe to reset */ + if (!(current1 & CS35L34_AMP_SHORT)) { + dev_dbg(codec->dev, + "Amp short error release\n"); + regmap_update_bits(cs35l34->regmap, + CS35L34_PROT_RELEASE_CTL, + CS35L34_SHORT_RLS, 0); + regmap_update_bits(cs35l34->regmap, + CS35L34_PROT_RELEASE_CTL, + CS35L34_SHORT_RLS, + CS35L34_SHORT_RLS); + regmap_update_bits(cs35l34->regmap, + CS35L34_PROT_RELEASE_CTL, + CS35L34_SHORT_RLS, 0); + } + } + + if (sticky1 & CS35L34_OTW) { + dev_crit(codec->dev, "Over temperature warning\n"); + + /* error is no longer asserted; safe to reset */ + if (!(current1 & CS35L34_OTW)) { + dev_dbg(codec->dev, + "Over temperature warning release\n"); + regmap_update_bits(cs35l34->regmap, + CS35L34_PROT_RELEASE_CTL, + CS35L34_OTW_RLS, 0); + regmap_update_bits(cs35l34->regmap, + CS35L34_PROT_RELEASE_CTL, + CS35L34_OTW_RLS, + CS35L34_OTW_RLS); + regmap_update_bits(cs35l34->regmap, + CS35L34_PROT_RELEASE_CTL, + CS35L34_OTW_RLS, 0); + } + } + + if (sticky1 & CS35L34_OTE) { + dev_crit(codec->dev, "Over temperature error\n"); + + /* error is no longer asserted; safe to reset */ + if (!(current1 & CS35L34_OTE)) { + dev_dbg(codec->dev, + "Over temperature error release\n"); + regmap_update_bits(cs35l34->regmap, + CS35L34_PROT_RELEASE_CTL, + CS35L34_OTE_RLS, 0); + regmap_update_bits(cs35l34->regmap, + CS35L34_PROT_RELEASE_CTL, + CS35L34_OTE_RLS, + CS35L34_OTE_RLS); + regmap_update_bits(cs35l34->regmap, + CS35L34_PROT_RELEASE_CTL, + CS35L34_OTE_RLS, 0); + } + } + + if (sticky3 & CS35L34_BST_HIGH) { + dev_crit(codec->dev, "VBST too high error; powering off!\n"); + regmap_update_bits(cs35l34->regmap, CS35L34_PWRCTL2, + CS35L34_PDN_AMP, CS35L34_PDN_AMP); + regmap_update_bits(cs35l34->regmap, CS35L34_PWRCTL1, + CS35L34_PDN_ALL, CS35L34_PDN_ALL); + } + + if (sticky3 & CS35L34_LBST_SHORT) { + dev_crit(codec->dev, "LBST short error; powering off!\n"); + regmap_update_bits(cs35l34->regmap, CS35L34_PWRCTL2, + CS35L34_PDN_AMP, CS35L34_PDN_AMP); + regmap_update_bits(cs35l34->regmap, CS35L34_PWRCTL1, + CS35L34_PDN_ALL, CS35L34_PDN_ALL); + } + + return IRQ_HANDLED; +} + +static const char * const cs35l34_core_supplies[] = { + "VA", + "VP", +}; + +static int cs35l34_i2c_probe(struct i2c_client *i2c_client, + const struct i2c_device_id *id) +{ + struct cs35l34_private *cs35l34; + struct cs35l34_platform_data *pdata = + dev_get_platdata(&i2c_client->dev); + int i; + int ret; + unsigned int devid = 0; + unsigned int reg; + + cs35l34 = devm_kzalloc(&i2c_client->dev, + sizeof(struct cs35l34_private), + GFP_KERNEL); + if (!cs35l34) { + dev_err(&i2c_client->dev, "could not allocate codec\n"); + return -ENOMEM; + } + + i2c_set_clientdata(i2c_client, cs35l34); + cs35l34->regmap = devm_regmap_init_i2c(i2c_client, &cs35l34_regmap); + if (IS_ERR(cs35l34->regmap)) { + ret = PTR_ERR(cs35l34->regmap); + dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + cs35l34->num_core_supplies = ARRAY_SIZE(cs35l34_core_supplies); + for (i = 0; i < ARRAY_SIZE(cs35l34_core_supplies); i++) + cs35l34->core_supplies[i].supply = cs35l34_core_supplies[i]; + + ret = devm_regulator_bulk_get(&i2c_client->dev, + cs35l34->num_core_supplies, + cs35l34->core_supplies); + if (ret != 0) { + dev_err(&i2c_client->dev, + "Failed to request core supplies %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(cs35l34->num_core_supplies, + cs35l34->core_supplies); + if (ret != 0) { + dev_err(&i2c_client->dev, + "Failed to enable core supplies: %d\n", ret); + return ret; + } + + if (pdata) { + cs35l34->pdata = *pdata; + } else { + pdata = devm_kzalloc(&i2c_client->dev, + sizeof(struct cs35l34_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 = cs35l34_handle_of_data(i2c_client, pdata); + if (ret != 0) + return ret; + + } + cs35l34->pdata = *pdata; + } + + ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL, + cs35l34_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW, + "cs35l34", cs35l34); + if (ret != 0) + dev_err(&i2c_client->dev, "Failed to request IRQ: %d\n", ret); + + cs35l34->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev, + "reset-gpios", GPIOD_OUT_LOW); + if (IS_ERR(cs35l34->reset_gpio)) + return PTR_ERR(cs35l34->reset_gpio); + + gpiod_set_value_cansleep(cs35l34->reset_gpio, 1); + + msleep(CS35L34_START_DELAY); + + ret = regmap_read(cs35l34->regmap, CS35L34_DEVID_AB, ®); + + devid = (reg & 0xFF) << 12; + ret = regmap_read(cs35l34->regmap, CS35L34_DEVID_CD, ®); + devid |= (reg & 0xFF) << 4; + ret = regmap_read(cs35l34->regmap, CS35L34_DEVID_E, ®); + devid |= (reg & 0xF0) >> 4; + + if (devid != CS35L34_CHIP_ID) { + dev_err(&i2c_client->dev, + "CS35l34 Device ID (%X). Expected ID %X\n", + devid, CS35L34_CHIP_ID); + ret = -ENODEV; + goto err_regulator; + } + + ret = regmap_read(cs35l34->regmap, CS35L34_REV_ID, ®); + if (ret < 0) { + dev_err(&i2c_client->dev, "Get Revision ID failed\n"); + goto err_regulator; + } + + dev_info(&i2c_client->dev, + "Cirrus Logic CS35l34 (%x), Revision: %02X\n", devid, + reg & 0xFF); + + /* Unmask critical interrupts */ + regmap_update_bits(cs35l34->regmap, CS35L34_INT_MASK_1, + CS35L34_M_CAL_ERR | CS35L34_M_ALIVE_ERR | + CS35L34_M_AMP_SHORT | CS35L34_M_OTW | + CS35L34_M_OTE, 0); + regmap_update_bits(cs35l34->regmap, CS35L34_INT_MASK_3, + CS35L34_M_BST_HIGH | CS35L34_M_LBST_SHORT, 0); + + pm_runtime_set_autosuspend_delay(&i2c_client->dev, 100); + pm_runtime_use_autosuspend(&i2c_client->dev); + pm_runtime_set_active(&i2c_client->dev); + pm_runtime_enable(&i2c_client->dev); + + ret = snd_soc_register_codec(&i2c_client->dev, + &soc_codec_dev_cs35l34, &cs35l34_dai, 1); + if (ret < 0) { + dev_err(&i2c_client->dev, + "%s: Register codec failed\n", __func__); + goto err_regulator; + } + + return 0; + +err_regulator: + regulator_bulk_disable(cs35l34->num_core_supplies, + cs35l34->core_supplies); + + return ret; +} + +static int cs35l34_i2c_remove(struct i2c_client *client) +{ + struct cs35l34_private *cs35l34 = i2c_get_clientdata(client); + + snd_soc_unregister_codec(&client->dev); + + if (cs35l34->reset_gpio) + gpiod_set_value_cansleep(cs35l34->reset_gpio, 0); + + pm_runtime_disable(&client->dev); + regulator_bulk_disable(cs35l34->num_core_supplies, + cs35l34->core_supplies); + + return 0; +} + +static int __maybe_unused cs35l34_runtime_resume(struct device *dev) +{ + struct cs35l34_private *cs35l34 = dev_get_drvdata(dev); + int ret; + + ret = regulator_bulk_enable(cs35l34->num_core_supplies, + cs35l34->core_supplies); + + if (ret != 0) { + dev_err(dev, "Failed to enable core supplies: %d\n", + ret); + return ret; + } + + regcache_cache_only(cs35l34->regmap, false); + + gpiod_set_value_cansleep(cs35l34->reset_gpio, 1); + msleep(CS35L34_START_DELAY); + + ret = regcache_sync(cs35l34->regmap); + if (ret != 0) { + dev_err(dev, "Failed to restore register cache\n"); + goto err; + } + return 0; +err: + regcache_cache_only(cs35l34->regmap, true); + regulator_bulk_disable(cs35l34->num_core_supplies, + cs35l34->core_supplies); + + return ret; +} + +static int __maybe_unused cs35l34_runtime_suspend(struct device *dev) +{ + struct cs35l34_private *cs35l34 = dev_get_drvdata(dev); + + regcache_cache_only(cs35l34->regmap, true); + regcache_mark_dirty(cs35l34->regmap); + + gpiod_set_value_cansleep(cs35l34->reset_gpio, 0); + + regulator_bulk_disable(cs35l34->num_core_supplies, + cs35l34->core_supplies); + + return 0; +} + +static const struct dev_pm_ops cs35l34_pm_ops = { + SET_RUNTIME_PM_OPS(cs35l34_runtime_suspend, + cs35l34_runtime_resume, + NULL) +}; + +static const struct of_device_id cs35l34_of_match[] = { + {.compatible = "cirrus,cs35l34"}, + {}, +}; +MODULE_DEVICE_TABLE(of, cs35l34_of_match); + +static const struct i2c_device_id cs35l34_id[] = { + {"cs35l34", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, cs35l34_id); + +static struct i2c_driver cs35l34_i2c_driver = { + .driver = { + .name = "cs35l34", + .pm = &cs35l34_pm_ops, + .of_match_table = cs35l34_of_match, + + }, + .id_table = cs35l34_id, + .probe = cs35l34_i2c_probe, + .remove = cs35l34_i2c_remove, + +}; + +static int __init cs35l34_modinit(void) +{ + int ret; + + ret = i2c_add_driver(&cs35l34_i2c_driver); + if (ret != 0) { + pr_err("Failed to register CS35l34 I2C driver: %d\n", ret); + return ret; + } + return 0; +} +module_init(cs35l34_modinit); + +static void __exit cs35l34_exit(void) +{ + i2c_del_driver(&cs35l34_i2c_driver); +} +module_exit(cs35l34_exit); + +MODULE_DESCRIPTION("ASoC CS35l34 driver"); +MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <Paul.Handrigan@cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l34.h b/sound/soc/codecs/cs35l34.h new file mode 100644 index 000000000000..bcd54f127559 --- /dev/null +++ b/sound/soc/codecs/cs35l34.h @@ -0,0 +1,269 @@ +/* + * cs35l34.h -- CS35L34 ALSA SoC audio driver + * + * Copyright 2016 Cirrus Logic, Inc. + * + * Author: Paul Handrigan <Paul.Handrigan@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 __CS35L34_H__ +#define __CS35L34_H__ + +#define CS35L34_CHIP_ID 0x00035A34 +#define CS35L34_DEVID_AB 0x01 /* Device ID A & B [RO] */ +#define CS35L34_DEVID_CD 0x02 /* Device ID C & D [RO] */ +#define CS35L34_DEVID_E 0x03 /* Device ID E [RO] */ +#define CS35L34_FAB_ID 0x04 /* Fab ID [RO] */ +#define CS35L34_REV_ID 0x05 /* Revision ID [RO] */ +#define CS35L34_PWRCTL1 0x06 /* Power Ctl 1 */ +#define CS35L34_PWRCTL2 0x07 /* Power Ctl 2 */ +#define CS35L34_PWRCTL3 0x08 /* Power Ctl 3 */ +#define CS35L34_ADSP_CLK_CTL 0x0A /* (ADSP) Clock Ctl */ +#define CS35L34_MCLK_CTL 0x0B /* Master Clocking Ctl */ +#define CS35L34_AMP_INP_DRV_CTL 0x14 /* Amp Input Drive Ctl */ +#define CS35L34_AMP_DIG_VOL_CTL 0x15 /* Amplifier Dig Volume Ctl */ +#define CS35L34_AMP_DIG_VOL 0x16 /* Amplifier Dig Volume */ +#define CS35L34_AMP_ANLG_GAIN_CTL 0x17 /* Amplifier Analog Gain Ctl */ +#define CS35L34_PROTECT_CTL 0x18 /* Amp Gain - Prot Ctl Param */ +#define CS35L34_AMP_KEEP_ALIVE_CTL 0x1A /* Amplifier Keep Alive Ctl */ +#define CS35L34_BST_CVTR_V_CTL 0x1D /* Boost Conv Voltage Ctl */ +#define CS35L34_BST_PEAK_I 0x1E /* Boost Conv Peak Current */ +#define CS35L34_BST_RAMP_CTL 0x20 /* Boost Conv Soft Ramp Ctl */ +#define CS35L34_BST_CONV_COEF_1 0x21 /* Boost Conv Coefficients 1 */ +#define CS35L34_BST_CONV_COEF_2 0x22 /* Boost Conv Coefficients 2 */ +#define CS35L34_BST_CONV_SLOPE_COMP 0x23 /* Boost Conv Slope Comp */ +#define CS35L34_BST_CONV_SW_FREQ 0x24 /* Boost Conv L BST SW Freq */ +#define CS35L34_CLASS_H_CTL 0x30 /* CLS H Control */ +#define CS35L34_CLASS_H_HEADRM_CTL 0x31 /* CLS H Headroom Ctl */ +#define CS35L34_CLASS_H_RELEASE_RATE 0x32 /* CLS H Release Rate */ +#define CS35L34_CLASS_H_FET_DRIVE_CTL 0x33 /* CLS H Weak FET Drive Ctl */ +#define CS35L34_CLASS_H_STATUS 0x38 /* CLS H Status */ +#define CS35L34_VPBR_CTL 0x3A /* VPBR Ctl */ +#define CS35L34_VPBR_VOL_CTL 0x3B /* VPBR Volume Ctl */ +#define CS35L34_VPBR_TIMING_CTL 0x3C /* VPBR Timing Ctl */ +#define CS35L34_PRED_MAX_ATTEN_SPK_LOAD 0x40 /* PRD Max Atten / Spkr Load */ +#define CS35L34_PRED_BROWNOUT_THRESH 0x41 /* PRD Brownout Threshold */ +#define CS35L34_PRED_BROWNOUT_VOL_CTL 0x42 /* PRD Brownout Volume Ctl */ +#define CS35L34_PRED_BROWNOUT_RATE_CTL 0x43 /* PRD Brownout Rate Ctl */ +#define CS35L34_PRED_WAIT_CTL 0x44 /* PRD Wait Ctl */ +#define CS35L34_PRED_ZVP_INIT_IMP_CTL 0x46 /* PRD ZVP Initial Imp Ctl */ +#define CS35L34_PRED_MAN_SAFE_VPI_CTL 0x47 /* PRD Manual Safe VPI Ctl */ +#define CS35L34_VPBR_ATTEN_STATUS 0x4B /* VPBR Attenuation Status */ +#define CS35L34_PRED_BRWNOUT_ATT_STATUS 0x4C /* PRD Brownout Atten Status */ +#define CS35L34_SPKR_MON_CTL 0x4E /* Speaker Monitoring Ctl */ +#define CS35L34_ADSP_I2S_CTL 0x50 /* ADSP I2S Ctl */ +#define CS35L34_ADSP_TDM_CTL 0x51 /* ADSP TDM Ctl */ +#define CS35L34_TDM_TX_CTL_1_VMON 0x52 /* TDM TX Ctl 1 (VMON) */ +#define CS35L34_TDM_TX_CTL_2_IMON 0x53 /* TDM TX Ctl 2 (IMON) */ +#define CS35L34_TDM_TX_CTL_3_VPMON 0x54 /* TDM TX Ctl 3 (VPMON) */ +#define CS35L34_TDM_TX_CTL_4_VBSTMON 0x55 /* TDM TX Ctl 4 (VBSTMON) */ +#define CS35L34_TDM_TX_CTL_5_FLAG1 0x56 /* TDM TX Ctl 5 (FLAG1) */ +#define CS35L34_TDM_TX_CTL_6_FLAG2 0x57 /* TDM TX Ctl 6 (FLAG2) */ +#define CS35L34_TDM_TX_SLOT_EN_1 0x5A /* TDM TX Slot Enable */ +#define CS35L34_TDM_TX_SLOT_EN_2 0x5B /* TDM TX Slot Enable */ +#define CS35L34_TDM_TX_SLOT_EN_3 0x5C /* TDM TX Slot Enable */ +#define CS35L34_TDM_TX_SLOT_EN_4 0x5D /* TDM TX Slot Enable */ +#define CS35L34_TDM_RX_CTL_1_AUDIN 0x5E /* TDM RX Ctl 1 */ +#define CS35L34_TDM_RX_CTL_3_ALIVE 0x60 /* TDM RX Ctl 3 (ALIVE) */ +#define CS35L34_MULT_DEV_SYNCH1 0x62 /* Multidevice Synch */ +#define CS35L34_MULT_DEV_SYNCH2 0x63 /* Multidevice Synch 2 */ +#define CS35L34_PROT_RELEASE_CTL 0x64 /* Protection Release Ctl */ +#define CS35L34_DIAG_MODE_REG_LOCK 0x68 /* Diagnostic Mode Reg Lock */ +#define CS35L34_DIAG_MODE_CTL_1 0x69 /* Diagnostic Mode Ctl 1 */ +#define CS35L34_DIAG_MODE_CTL_2 0x6A /* Diagnostic Mode Ctl 2 */ +#define CS35L34_INT_MASK_1 0x70 /* Interrupt Mask 1 */ +#define CS35L34_INT_MASK_2 0x71 /* Interrupt Mask 2 */ +#define CS35L34_INT_MASK_3 0x72 /* Interrupt Mask 3 */ +#define CS35L34_INT_MASK_4 0x73 /* Interrupt Mask 4 */ +#define CS35L34_INT_STATUS_1 0x74 /* Interrupt Status 1 */ +#define CS35L34_INT_STATUS_2 0x75 /* Interrupt Status 2 */ +#define CS35L34_INT_STATUS_3 0x76 /* Interrupt Status 3 */ +#define CS35L34_INT_STATUS_4 0x77 /* Interrupt Status 4 */ +#define CS35L34_OTP_TRIM_STATUS 0x7E /* OTP Trim Status */ + +#define CS35L34_MAX_REGISTER 0x7F +#define CS35L34_REGISTER_COUNT 0x4E + +#define CS35L34_MCLK_5644 5644800 +#define CS35L34_MCLK_6144 6144000 +#define CS35L34_MCLK_6 6000000 +#define CS35L34_MCLK_11289 11289600 +#define CS35L34_MCLK_12 12000000 +#define CS35L34_MCLK_12288 12288000 + +/* CS35L34_PWRCTL1 */ +#define CS35L34_SFT_RST (1 << 7) +#define CS35L34_DISCHG_FLT (1 << 1) +#define CS35L34_PDN_ALL 1 + +/* CS35L34_PWRCTL2 */ +#define CS35L34_PDN_VMON (1 << 7) +#define CS35L34_PDN_IMON (1 << 6) +#define CS35L34_PDN_CLASSH (1 << 5) +#define CS35L34_PDN_VPBR (1 << 4) +#define CS35L34_PDN_PRED (1 << 3) +#define CS35L34_PDN_BST (1 << 2) +#define CS35L34_PDN_AMP 1 + +/* CS35L34_PWRCTL3 */ +#define CS35L34_MCLK_DIS (1 << 7) +#define CS35L34_PDN_VBSTMON_OUT (1 << 4) +#define CS35L34_PDN_VMON_OUT (1 << 3) +/* Tristate the ADSP SDOUT when in I2C mode */ +#define CS35L34_PDN_SDOUT (1 << 2) +#define CS35L34_PDN_SDIN (1 << 1) +#define CS35L34_PDN_TDM 1 + +/* CS35L34_ADSP_CLK_CTL */ +#define CS35L34_ADSP_RATE 0xF +#define CS35L34_ADSP_DRIVE (1 << 4) +#define CS35L34_ADSP_M_S (1 << 7) + +/* CS35L34_MCLK_CTL */ +#define CS35L34_MCLK_DIV (1 << 4) +#define CS35L34_MCLK_RATE_MASK 0x7 +#define CS35L34_MCLK_RATE_6P1440 0x2 +#define CS35L34_MCLK_RATE_6P0000 0x1 +#define CS35L34_MCLK_RATE_5P6448 0x0 +#define CS35L34_MCLKDIS (1 << 7) +#define CS35L34_MCLKDIV2 (1 << 6) +#define CS35L34_SDOUT_3ST_TDM (1 << 5) +#define CS35L34_INT_FS_RATE (1 << 4) +#define CS35L34_ADSP_FS 0xF + +/* CS35L34_AMP_INP_DRV_CTL */ +#define CS35L34_DRV_STR_SRC (1 << 1) +#define CS35L34_DRV_STR 1 + +/* CS35L34_AMP_DIG_VOL_CTL */ +#define CS35L34_AMP_DSR_RATE_MASK 0xF0 +#define CS35L34_AMP_DSR_RATE_SHIFT (1 << 4) +#define CS35L34_NOTCH_DIS (1 << 3) +#define CS35L34_AMP_DIGSFT (1 << 1) +#define CS35L34_INV 1 + +/* CS35L34_PROTECT_CTL */ +#define CS35L34_OTW_ATTN_MASK 0xC +#define CS35L34_OTW_THRD_MASK 0x3 +#define CS35L34_MUTE (1 << 5) +#define CS35L34_GAIN_ZC (1 << 4) +#define CS35L34_GAIN_ZC_MASK 0x10 +#define CS35L34_GAIN_ZC_SHIFT 4 + +/* CS35L34_AMP_KEEP_ALIVE_CTL */ +#define CS35L34_ALIVE_WD_DIS (1 << 2) + +/* CS35L34_BST_CVTR_V_CTL */ +#define CS35L34_BST_CVTL_MASK 0x3F + +/* CS35L34_BST_PEAK_I */ +#define CS35L34_BST_PEAK_MASK 0x3F + +/* CS35L34_ADSP_I2S_CTL */ +#define CS35L34_I2S_LOC_MASK 0xC +#define CS35L34_I2S_LOC_SHIFT 2 + +/* CS35L34_MULT_DEV_SYNCH2 */ +#define CS35L34_SYNC2_MASK 0xF + +/* CS35L34_PROT_RELEASE_CTL */ +#define CS35L34_CAL_ERR_RLS (1 << 7) +#define CS35L34_SHORT_RLS (1 << 2) +#define CS35L34_OTW_RLS (1 << 1) +#define CS35L34_OTE_RLS 1 + +/* CS35L34_INT_MASK_1 */ +#define CS35L34_M_CAL_ERR_SHIFT 7 +#define CS35L34_M_CAL_ERR (1 << CS35L34_M_CAL_ERR_SHIFT) +#define CS35L34_M_ALIVE_ERR_SHIFT 5 +#define CS35L34_M_ALIVE_ERR (1 << CS35L34_M_ALIVE_ERR_SHIFT) +#define CS35L34_M_ADSP_CLK_SHIFT 4 +#define CS35L34_M_ADSP_CLK_ERR (1 << CS35L34_M_ADSP_CLK_SHIFT) +#define CS35L34_M_MCLK_SHIFT 3 +#define CS35L34_M_MCLK_ERR (1 << CS35L34_M_MCLK_SHIFT) +#define CS35L34_M_AMP_SHORT_SHIFT 2 +#define CS35L34_M_AMP_SHORT (1 << CS35L34_M_AMP_SHORT_SHIFT) +#define CS35L34_M_OTW_SHIFT 1 +#define CS35L34_M_OTW (1 << CS35L34_M_OTW_SHIFT) +#define CS35L34_M_OTE_SHIFT 0 +#define CS35L34_M_OTE (1 << CS35L34_M_OTE_SHIFT) + +/* CS35L34_INT_MASK_2 */ +#define CS35L34_M_PDN_DONE_SHIFT 4 +#define CS35L34_M_PDN_DONE (1 << CS35L34_M_PDN_DONE_SHIFT) +#define CS35L34_M_PRED_SHIFT 3 +#define CS35L34_M_PRED_ERR (1 << CS35L34_M_PRED_SHIFT) +#define CS35L34_M_PRED_CLR_SHIFT 2 +#define CS35L34_M_PRED_CLR (1 << CS35L34_M_PRED_CLR_SHIFT) +#define CS35L34_M_VPBR_SHIFT 1 +#define CS35L34_M_VPBR_ERR (1 << CS35L34_M_VPBR_SHIFT) +#define CS35L34_M_VPBR_CLR_SHIFT 0 +#define CS35L34_M_VPBR_CLR (1 << CS35L34_M_VPBR_CLR_SHIFT) + +/* CS35L34_INT_MASK_3 */ +#define CS35L34_M_BST_HIGH_SHIFT 4 +#define CS35L34_M_BST_HIGH (1 << CS35L34_M_BST_HIGH_SHIFT) +#define CS35L34_M_BST_HIGH_FLAG_SHIFT 3 +#define CS35L34_M_BST_HIGH_FLAG (1 << CS35L34_M_BST_HIGH_FLAG_SHIFT) +#define CS35L34_M_BST_IPK_FLAG_SHIFT 2 +#define CS35L34_M_BST_IPK_FLAG (1 << CS35L34_M_BST_IPK_FLAG_SHIFT) +#define CS35L34_M_LBST_SHORT_SHIFT 0 +#define CS35L34_M_LBST_SHORT (1 << CS35L34_M_LBST_SHORT_SHIFT) + +/* CS35L34_INT_MASK_4 */ +#define CS35L34_M_VMON_OVFL_SHIFT 3 +#define CS35L34_M_VMON_OVFL (1 << CS35L34_M_VMON_OVFL_SHIFT) +#define CS35L34_M_IMON_OVFL_SHIFT 2 +#define CS35L34_M_IMON_OVFL (1 << CS35L34_M_IMON_OVFL_SHIFT) +#define CS35L34_M_VPMON_OVFL_SHIFT 1 +#define CS35L34_M_VPMON_OVFL (1 << CS35L34_M_VPMON_OVFL_SHIFT) +#define CS35L34_M_VBSTMON_OVFL_SHIFT 1 +#define CS35L34_M_VBSTMON_OVFL (1 << CS35L34_M_VBSTMON_OVFL_SHIFT) + +/* CS35L34_INT_1 */ +#define CS35L34_CAL_ERR (1 << CS35L34_M_CAL_ERR_SHIFT) +#define CS35L34_ALIVE_ERR (1 << CS35L34_M_ALIVE_ERR_SHIFT) +#define CS35L34_M_ADSP_CLK_ERR (1 << CS35L34_M_ADSP_CLK_SHIFT) +#define CS35L34_MCLK_ERR (1 << CS35L34_M_MCLK_SHIFT) +#define CS35L34_AMP_SHORT (1 << CS35L34_M_AMP_SHORT_SHIFT) +#define CS35L34_OTW (1 << CS35L34_M_OTW_SHIFT) +#define CS35L34_OTE (1 << CS35L34_M_OTE_SHIFT) + +/* CS35L34_INT_2 */ +#define CS35L34_PDN_DONE (1 << CS35L34_M_PDN_DONE_SHIFT) +#define CS35L34_PRED_ERR (1 << CS35L34_M_PRED_SHIFT) +#define CS35L34_PRED_CLR (1 << CS35L34_M_PRED_CLR_SHIFT) +#define CS35L34_VPBR_ERR (1 << CS35L34_M_VPBR_SHIFT) +#define CS35L34_VPBR_CLR (1 << CS35L34_M_VPBR_CLR_SHIFT) + +/* CS35L34_INT_3 */ +#define CS35L34_BST_HIGH (1 << CS35L34_M_BST_HIGH_SHIFT) +#define CS35L34_BST_HIGH_FLAG (1 << CS35L34_M_BST_HIGH_FLAG_SHIFT) +#define CS35L34_BST_IPK_FLAG (1 << CS35L34_M_BST_IPK_FLAG_SHIFT) +#define CS35L34_LBST_SHORT (1 << CS35L34_M_LBST_SHORT_SHIFT) + +/* CS35L34_INT_4 */ +#define CS35L34_VMON_OVFL (1 << CS35L34_M_VMON_OVFL_SHIFT) +#define CS35L34_IMON_OVFL (1 << CS35L34_M_IMON_OVFL_SHIFT) +#define CS35L34_VPMON_OVFL (1 << CS35L34_M_VPMON_OVFL_SHIFT) +#define CS35L34_VBSTMON_OVFL (1 << CS35L34_M_VBSTMON_OVFL_SHIFT) + +/* CS35L34_{RX,TX}_X */ +#define CS35L34_X_STATE_SHIFT 7 +#define CS35L34_X_STATE (1 << CS35L34_X_STATE_SHIFT) +#define CS35L34_X_LOC_SHIFT 0 +#define CS35L34_X_LOC (0x1F << CS35L34_X_LOC_SHIFT) + +#define CS35L34_RATES (SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_32000) +#define CS35L34_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 55db19ddc5ff..fd966bb851cb 100644 --- a/sound/soc/codecs/cs4265.c +++ b/sound/soc/codecs/cs4265.c @@ -547,13 +547,14 @@ static struct snd_soc_dai_driver cs4265_dai[] = { static const struct snd_soc_codec_driver soc_codec_cs4265 = { .set_bias_level = cs4265_set_bias_level, - .dapm_widgets = cs4265_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(cs4265_dapm_widgets), - .dapm_routes = cs4265_audio_map, - .num_dapm_routes = ARRAY_SIZE(cs4265_audio_map), - - .controls = cs4265_snd_controls, - .num_controls = ARRAY_SIZE(cs4265_snd_controls), + .component_driver = { + .controls = cs4265_snd_controls, + .num_controls = ARRAY_SIZE(cs4265_snd_controls), + .dapm_widgets = cs4265_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs4265_dapm_widgets), + .dapm_routes = cs4265_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs4265_audio_map), + }, }; static const struct regmap_config cs4265_regmap = { diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index e07807d96b68..84f86745c30e 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -148,11 +148,11 @@ SND_SOC_DAPM_OUTPUT("AOUTR"), }; static const struct snd_soc_dapm_route cs4270_dapm_routes[] = { - { "Capture", NULL, "AINA" }, - { "Capture", NULL, "AINB" }, + { "Capture", NULL, "AINL" }, + { "Capture", NULL, "AINR" }, - { "AOUTA", NULL, "Playback" }, - { "AOUTB", NULL, "Playback" }, + { "AOUTL", NULL, "Playback" }, + { "AOUTR", NULL, "Playback" }, }; /** @@ -617,12 +617,14 @@ static const struct snd_soc_codec_driver soc_codec_device_cs4270 = { .suspend = cs4270_soc_suspend, .resume = cs4270_soc_resume, - .controls = cs4270_snd_controls, - .num_controls = ARRAY_SIZE(cs4270_snd_controls), - .dapm_widgets = cs4270_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(cs4270_dapm_widgets), - .dapm_routes = cs4270_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(cs4270_dapm_routes), + .component_driver = { + .controls = cs4270_snd_controls, + .num_controls = ARRAY_SIZE(cs4270_snd_controls), + .dapm_widgets = cs4270_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs4270_dapm_widgets), + .dapm_routes = cs4270_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs4270_dapm_routes), + }, }; /* diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index 0c0010b25421..8c0f3b89b5bc 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -645,12 +645,14 @@ static struct snd_soc_codec_driver soc_codec_dev_cs4271 = { .suspend = cs4271_soc_suspend, .resume = cs4271_soc_resume, - .controls = cs4271_snd_controls, - .num_controls = ARRAY_SIZE(cs4271_snd_controls), - .dapm_widgets = cs4271_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(cs4271_dapm_widgets), - .dapm_routes = cs4271_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(cs4271_dapm_routes), + .component_driver = { + .controls = cs4271_snd_controls, + .num_controls = ARRAY_SIZE(cs4271_snd_controls), + .dapm_widgets = cs4271_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs4271_dapm_widgets), + .dapm_routes = cs4271_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs4271_dapm_routes), + }, }; static int cs4271_common_probe(struct device *dev, diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c new file mode 100644 index 000000000000..55e4520cdcaf --- /dev/null +++ b/sound/soc/codecs/cs42l42.c @@ -0,0 +1,1986 @@ +/* + * cs42l42.c -- CS42L42 ALSA SoC audio driver + * + * Copyright 2016 Cirrus Logic, Inc. + * + * Author: James Schulman <james.schulman@cirrus.com> + * Author: Brian Austin <brian.austin@cirrus.com> + * Author: Michael White <michael.white@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.h> +#include <linux/of_gpio.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/cs42l42.h> + +#include "cs42l42.h" + +static const struct reg_default cs42l42_reg_defaults[] = { + { CS42L42_FRZ_CTL, 0x00 }, + { CS42L42_SRC_CTL, 0x10 }, + { CS42L42_MCLK_STATUS, 0x02 }, + { CS42L42_MCLK_CTL, 0x02 }, + { CS42L42_SFTRAMP_RATE, 0xA4 }, + { CS42L42_I2C_DEBOUNCE, 0x88 }, + { CS42L42_I2C_STRETCH, 0x03 }, + { CS42L42_I2C_TIMEOUT, 0xB7 }, + { CS42L42_PWR_CTL1, 0xFF }, + { CS42L42_PWR_CTL2, 0x84 }, + { CS42L42_PWR_CTL3, 0x20 }, + { CS42L42_RSENSE_CTL1, 0x40 }, + { CS42L42_RSENSE_CTL2, 0x00 }, + { CS42L42_OSC_SWITCH, 0x00 }, + { CS42L42_OSC_SWITCH_STATUS, 0x05 }, + { CS42L42_RSENSE_CTL3, 0x1B }, + { CS42L42_TSENSE_CTL, 0x1B }, + { CS42L42_TSRS_INT_DISABLE, 0x00 }, + { CS42L42_TRSENSE_STATUS, 0x00 }, + { CS42L42_HSDET_CTL1, 0x77 }, + { CS42L42_HSDET_CTL2, 0x00 }, + { CS42L42_HS_SWITCH_CTL, 0xF3 }, + { CS42L42_HS_DET_STATUS, 0x00 }, + { CS42L42_HS_CLAMP_DISABLE, 0x00 }, + { CS42L42_MCLK_SRC_SEL, 0x00 }, + { CS42L42_SPDIF_CLK_CFG, 0x00 }, + { CS42L42_FSYNC_PW_LOWER, 0x00 }, + { CS42L42_FSYNC_PW_UPPER, 0x00 }, + { CS42L42_FSYNC_P_LOWER, 0xF9 }, + { CS42L42_FSYNC_P_UPPER, 0x00 }, + { CS42L42_ASP_CLK_CFG, 0x00 }, + { CS42L42_ASP_FRM_CFG, 0x10 }, + { CS42L42_FS_RATE_EN, 0x00 }, + { CS42L42_IN_ASRC_CLK, 0x00 }, + { CS42L42_OUT_ASRC_CLK, 0x00 }, + { CS42L42_PLL_DIV_CFG1, 0x00 }, + { CS42L42_ADC_OVFL_STATUS, 0x00 }, + { CS42L42_MIXER_STATUS, 0x00 }, + { CS42L42_SRC_STATUS, 0x00 }, + { CS42L42_ASP_RX_STATUS, 0x00 }, + { CS42L42_ASP_TX_STATUS, 0x00 }, + { CS42L42_CODEC_STATUS, 0x00 }, + { CS42L42_DET_INT_STATUS1, 0x00 }, + { CS42L42_DET_INT_STATUS2, 0x00 }, + { CS42L42_SRCPL_INT_STATUS, 0x00 }, + { CS42L42_VPMON_STATUS, 0x00 }, + { CS42L42_PLL_LOCK_STATUS, 0x00 }, + { CS42L42_TSRS_PLUG_STATUS, 0x00 }, + { CS42L42_ADC_OVFL_INT_MASK, 0x01 }, + { CS42L42_MIXER_INT_MASK, 0x0F }, + { CS42L42_SRC_INT_MASK, 0x0F }, + { CS42L42_ASP_RX_INT_MASK, 0x1F }, + { CS42L42_ASP_TX_INT_MASK, 0x0F }, + { CS42L42_CODEC_INT_MASK, 0x03 }, + { CS42L42_SRCPL_INT_MASK, 0xFF }, + { CS42L42_VPMON_INT_MASK, 0x01 }, + { CS42L42_PLL_LOCK_INT_MASK, 0x01 }, + { CS42L42_TSRS_PLUG_INT_MASK, 0x0F }, + { CS42L42_PLL_CTL1, 0x00 }, + { CS42L42_PLL_DIV_FRAC0, 0x00 }, + { CS42L42_PLL_DIV_FRAC1, 0x00 }, + { CS42L42_PLL_DIV_FRAC2, 0x00 }, + { CS42L42_PLL_DIV_INT, 0x40 }, + { CS42L42_PLL_CTL3, 0x10 }, + { CS42L42_PLL_CAL_RATIO, 0x80 }, + { CS42L42_PLL_CTL4, 0x03 }, + { CS42L42_LOAD_DET_RCSTAT, 0x00 }, + { CS42L42_LOAD_DET_DONE, 0x00 }, + { CS42L42_LOAD_DET_EN, 0x00 }, + { CS42L42_HSBIAS_SC_AUTOCTL, 0x03 }, + { CS42L42_WAKE_CTL, 0xC0 }, + { CS42L42_ADC_DISABLE_MUTE, 0x00 }, + { CS42L42_TIPSENSE_CTL, 0x02 }, + { CS42L42_MISC_DET_CTL, 0x03 }, + { CS42L42_MIC_DET_CTL1, 0x1F }, + { CS42L42_MIC_DET_CTL2, 0x2F }, + { CS42L42_DET_STATUS1, 0x00 }, + { CS42L42_DET_STATUS2, 0x00 }, + { CS42L42_DET_INT1_MASK, 0xE0 }, + { CS42L42_DET_INT2_MASK, 0xFF }, + { CS42L42_HS_BIAS_CTL, 0xC2 }, + { CS42L42_ADC_CTL, 0x00 }, + { CS42L42_ADC_VOLUME, 0x00 }, + { CS42L42_ADC_WNF_HPF_CTL, 0x71 }, + { CS42L42_DAC_CTL1, 0x00 }, + { CS42L42_DAC_CTL2, 0x02 }, + { CS42L42_HP_CTL, 0x0D }, + { CS42L42_CLASSH_CTL, 0x07 }, + { CS42L42_MIXER_CHA_VOL, 0x3F }, + { CS42L42_MIXER_ADC_VOL, 0x3F }, + { CS42L42_MIXER_CHB_VOL, 0x3F }, + { CS42L42_EQ_COEF_IN0, 0x22 }, + { CS42L42_EQ_COEF_IN1, 0x00 }, + { CS42L42_EQ_COEF_IN2, 0x00 }, + { CS42L42_EQ_COEF_IN3, 0x00 }, + { CS42L42_EQ_COEF_RW, 0x00 }, + { CS42L42_EQ_COEF_OUT0, 0x00 }, + { CS42L42_EQ_COEF_OUT1, 0x00 }, + { CS42L42_EQ_COEF_OUT2, 0x00 }, + { CS42L42_EQ_COEF_OUT3, 0x00 }, + { CS42L42_EQ_INIT_STAT, 0x00 }, + { CS42L42_EQ_START_FILT, 0x00 }, + { CS42L42_EQ_MUTE_CTL, 0x00 }, + { CS42L42_SP_RX_CH_SEL, 0x04 }, + { CS42L42_SP_RX_ISOC_CTL, 0x04 }, + { CS42L42_SP_RX_FS, 0x8C }, + { CS42l42_SPDIF_CH_SEL, 0x0E }, + { CS42L42_SP_TX_ISOC_CTL, 0x04 }, + { CS42L42_SP_TX_FS, 0xCC }, + { CS42L42_SPDIF_SW_CTL1, 0x3F }, + { CS42L42_SRC_SDIN_FS, 0x40 }, + { CS42L42_SRC_SDOUT_FS, 0x40 }, + { CS42L42_SPDIF_CTL1, 0x01 }, + { CS42L42_SPDIF_CTL2, 0x00 }, + { CS42L42_SPDIF_CTL3, 0x00 }, + { CS42L42_SPDIF_CTL4, 0x42 }, + { CS42L42_ASP_TX_SZ_EN, 0x00 }, + { CS42L42_ASP_TX_CH_EN, 0x00 }, + { CS42L42_ASP_TX_CH_AP_RES, 0x0F }, + { CS42L42_ASP_TX_CH1_BIT_MSB, 0x00 }, + { CS42L42_ASP_TX_CH1_BIT_LSB, 0x00 }, + { CS42L42_ASP_TX_HIZ_DLY_CFG, 0x00 }, + { CS42L42_ASP_TX_CH2_BIT_MSB, 0x00 }, + { CS42L42_ASP_TX_CH2_BIT_LSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_EN, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH1_AP_RES, 0x03 }, + { CS42L42_ASP_RX_DAI0_CH1_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH1_BIT_LSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH2_AP_RES, 0x03 }, + { CS42L42_ASP_RX_DAI0_CH2_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH2_BIT_LSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH3_AP_RES, 0x03 }, + { CS42L42_ASP_RX_DAI0_CH3_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH3_BIT_LSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH4_AP_RES, 0x03 }, + { CS42L42_ASP_RX_DAI0_CH4_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH4_BIT_LSB, 0x00 }, + { CS42L42_ASP_RX_DAI1_CH1_AP_RES, 0x03 }, + { CS42L42_ASP_RX_DAI1_CH1_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI1_CH1_BIT_LSB, 0x00 }, + { CS42L42_ASP_RX_DAI1_CH2_AP_RES, 0x03 }, + { CS42L42_ASP_RX_DAI1_CH2_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI1_CH2_BIT_LSB, 0x00 }, + { CS42L42_SUB_REVID, 0x03 }, +}; + +static bool cs42l42_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42L42_PAGE_REGISTER: + case CS42L42_DEVID_AB: + case CS42L42_DEVID_CD: + case CS42L42_DEVID_E: + case CS42L42_FABID: + case CS42L42_REVID: + case CS42L42_FRZ_CTL: + case CS42L42_SRC_CTL: + case CS42L42_MCLK_STATUS: + case CS42L42_MCLK_CTL: + case CS42L42_SFTRAMP_RATE: + case CS42L42_I2C_DEBOUNCE: + case CS42L42_I2C_STRETCH: + case CS42L42_I2C_TIMEOUT: + case CS42L42_PWR_CTL1: + case CS42L42_PWR_CTL2: + case CS42L42_PWR_CTL3: + case CS42L42_RSENSE_CTL1: + case CS42L42_RSENSE_CTL2: + case CS42L42_OSC_SWITCH: + case CS42L42_OSC_SWITCH_STATUS: + case CS42L42_RSENSE_CTL3: + case CS42L42_TSENSE_CTL: + case CS42L42_TSRS_INT_DISABLE: + case CS42L42_TRSENSE_STATUS: + case CS42L42_HSDET_CTL1: + case CS42L42_HSDET_CTL2: + case CS42L42_HS_SWITCH_CTL: + case CS42L42_HS_DET_STATUS: + case CS42L42_HS_CLAMP_DISABLE: + case CS42L42_MCLK_SRC_SEL: + case CS42L42_SPDIF_CLK_CFG: + case CS42L42_FSYNC_PW_LOWER: + case CS42L42_FSYNC_PW_UPPER: + case CS42L42_FSYNC_P_LOWER: + case CS42L42_FSYNC_P_UPPER: + case CS42L42_ASP_CLK_CFG: + case CS42L42_ASP_FRM_CFG: + case CS42L42_FS_RATE_EN: + case CS42L42_IN_ASRC_CLK: + case CS42L42_OUT_ASRC_CLK: + case CS42L42_PLL_DIV_CFG1: + case CS42L42_ADC_OVFL_STATUS: + case CS42L42_MIXER_STATUS: + case CS42L42_SRC_STATUS: + case CS42L42_ASP_RX_STATUS: + case CS42L42_ASP_TX_STATUS: + case CS42L42_CODEC_STATUS: + case CS42L42_DET_INT_STATUS1: + case CS42L42_DET_INT_STATUS2: + case CS42L42_SRCPL_INT_STATUS: + case CS42L42_VPMON_STATUS: + case CS42L42_PLL_LOCK_STATUS: + case CS42L42_TSRS_PLUG_STATUS: + case CS42L42_ADC_OVFL_INT_MASK: + case CS42L42_MIXER_INT_MASK: + case CS42L42_SRC_INT_MASK: + case CS42L42_ASP_RX_INT_MASK: + case CS42L42_ASP_TX_INT_MASK: + case CS42L42_CODEC_INT_MASK: + case CS42L42_SRCPL_INT_MASK: + case CS42L42_VPMON_INT_MASK: + case CS42L42_PLL_LOCK_INT_MASK: + case CS42L42_TSRS_PLUG_INT_MASK: + case CS42L42_PLL_CTL1: + case CS42L42_PLL_DIV_FRAC0: + case CS42L42_PLL_DIV_FRAC1: + case CS42L42_PLL_DIV_FRAC2: + case CS42L42_PLL_DIV_INT: + case CS42L42_PLL_CTL3: + case CS42L42_PLL_CAL_RATIO: + case CS42L42_PLL_CTL4: + case CS42L42_LOAD_DET_RCSTAT: + case CS42L42_LOAD_DET_DONE: + case CS42L42_LOAD_DET_EN: + case CS42L42_HSBIAS_SC_AUTOCTL: + case CS42L42_WAKE_CTL: + case CS42L42_ADC_DISABLE_MUTE: + case CS42L42_TIPSENSE_CTL: + case CS42L42_MISC_DET_CTL: + case CS42L42_MIC_DET_CTL1: + case CS42L42_MIC_DET_CTL2: + case CS42L42_DET_STATUS1: + case CS42L42_DET_STATUS2: + case CS42L42_DET_INT1_MASK: + case CS42L42_DET_INT2_MASK: + case CS42L42_HS_BIAS_CTL: + case CS42L42_ADC_CTL: + case CS42L42_ADC_VOLUME: + case CS42L42_ADC_WNF_HPF_CTL: + case CS42L42_DAC_CTL1: + case CS42L42_DAC_CTL2: + case CS42L42_HP_CTL: + case CS42L42_CLASSH_CTL: + case CS42L42_MIXER_CHA_VOL: + case CS42L42_MIXER_ADC_VOL: + case CS42L42_MIXER_CHB_VOL: + case CS42L42_EQ_COEF_IN0: + case CS42L42_EQ_COEF_IN1: + case CS42L42_EQ_COEF_IN2: + case CS42L42_EQ_COEF_IN3: + case CS42L42_EQ_COEF_RW: + case CS42L42_EQ_COEF_OUT0: + case CS42L42_EQ_COEF_OUT1: + case CS42L42_EQ_COEF_OUT2: + case CS42L42_EQ_COEF_OUT3: + case CS42L42_EQ_INIT_STAT: + case CS42L42_EQ_START_FILT: + case CS42L42_EQ_MUTE_CTL: + case CS42L42_SP_RX_CH_SEL: + case CS42L42_SP_RX_ISOC_CTL: + case CS42L42_SP_RX_FS: + case CS42l42_SPDIF_CH_SEL: + case CS42L42_SP_TX_ISOC_CTL: + case CS42L42_SP_TX_FS: + case CS42L42_SPDIF_SW_CTL1: + case CS42L42_SRC_SDIN_FS: + case CS42L42_SRC_SDOUT_FS: + case CS42L42_SPDIF_CTL1: + case CS42L42_SPDIF_CTL2: + case CS42L42_SPDIF_CTL3: + case CS42L42_SPDIF_CTL4: + case CS42L42_ASP_TX_SZ_EN: + case CS42L42_ASP_TX_CH_EN: + case CS42L42_ASP_TX_CH_AP_RES: + case CS42L42_ASP_TX_CH1_BIT_MSB: + case CS42L42_ASP_TX_CH1_BIT_LSB: + case CS42L42_ASP_TX_HIZ_DLY_CFG: + case CS42L42_ASP_TX_CH2_BIT_MSB: + case CS42L42_ASP_TX_CH2_BIT_LSB: + case CS42L42_ASP_RX_DAI0_EN: + case CS42L42_ASP_RX_DAI0_CH1_AP_RES: + case CS42L42_ASP_RX_DAI0_CH1_BIT_MSB: + case CS42L42_ASP_RX_DAI0_CH1_BIT_LSB: + case CS42L42_ASP_RX_DAI0_CH2_AP_RES: + case CS42L42_ASP_RX_DAI0_CH2_BIT_MSB: + case CS42L42_ASP_RX_DAI0_CH2_BIT_LSB: + case CS42L42_ASP_RX_DAI0_CH3_AP_RES: + case CS42L42_ASP_RX_DAI0_CH3_BIT_MSB: + case CS42L42_ASP_RX_DAI0_CH3_BIT_LSB: + case CS42L42_ASP_RX_DAI0_CH4_AP_RES: + case CS42L42_ASP_RX_DAI0_CH4_BIT_MSB: + case CS42L42_ASP_RX_DAI0_CH4_BIT_LSB: + case CS42L42_ASP_RX_DAI1_CH1_AP_RES: + case CS42L42_ASP_RX_DAI1_CH1_BIT_MSB: + case CS42L42_ASP_RX_DAI1_CH1_BIT_LSB: + case CS42L42_ASP_RX_DAI1_CH2_AP_RES: + case CS42L42_ASP_RX_DAI1_CH2_BIT_MSB: + case CS42L42_ASP_RX_DAI1_CH2_BIT_LSB: + case CS42L42_SUB_REVID: + return true; + default: + return false; + } +} + +static bool cs42l42_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42L42_DEVID_AB: + case CS42L42_DEVID_CD: + case CS42L42_DEVID_E: + case CS42L42_MCLK_STATUS: + case CS42L42_TRSENSE_STATUS: + case CS42L42_HS_DET_STATUS: + case CS42L42_ADC_OVFL_STATUS: + case CS42L42_MIXER_STATUS: + case CS42L42_SRC_STATUS: + case CS42L42_ASP_RX_STATUS: + case CS42L42_ASP_TX_STATUS: + case CS42L42_CODEC_STATUS: + case CS42L42_DET_INT_STATUS1: + case CS42L42_DET_INT_STATUS2: + case CS42L42_SRCPL_INT_STATUS: + case CS42L42_VPMON_STATUS: + case CS42L42_PLL_LOCK_STATUS: + case CS42L42_TSRS_PLUG_STATUS: + case CS42L42_LOAD_DET_RCSTAT: + case CS42L42_LOAD_DET_DONE: + case CS42L42_DET_STATUS1: + case CS42L42_DET_STATUS2: + return true; + default: + return false; + } +} + +static const struct regmap_range_cfg cs42l42_page_range = { + .name = "Pages", + .range_min = 0, + .range_max = CS42L42_MAX_REGISTER, + .selector_reg = CS42L42_PAGE_REGISTER, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 256, +}; + +static const struct regmap_config cs42l42_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .readable_reg = cs42l42_readable_register, + .volatile_reg = cs42l42_volatile_register, + + .ranges = &cs42l42_page_range, + .num_ranges = 1, + + .max_register = CS42L42_MAX_REGISTER, + .reg_defaults = cs42l42_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs42l42_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; + +static DECLARE_TLV_DB_SCALE(adc_tlv, -9600, 100, false); +static DECLARE_TLV_DB_SCALE(mixer_tlv, -6200, 100, false); + +static const char * const cs42l42_hpf_freq_text[] = { + "1.86Hz", "120Hz", "235Hz", "466Hz" +}; + +static SOC_ENUM_SINGLE_DECL(cs42l42_hpf_freq_enum, CS42L42_ADC_WNF_HPF_CTL, + CS42L42_ADC_HPF_CF_SHIFT, + cs42l42_hpf_freq_text); + +static const char * const cs42l42_wnf3_freq_text[] = { + "160Hz", "180Hz", "200Hz", "220Hz", + "240Hz", "260Hz", "280Hz", "300Hz" +}; + +static SOC_ENUM_SINGLE_DECL(cs42l42_wnf3_freq_enum, CS42L42_ADC_WNF_HPF_CTL, + CS42L42_ADC_WNF_CF_SHIFT, + cs42l42_wnf3_freq_text); + +static const char * const cs42l42_wnf05_freq_text[] = { + "280Hz", "315Hz", "350Hz", "385Hz", + "420Hz", "455Hz", "490Hz", "525Hz" +}; + +static SOC_ENUM_SINGLE_DECL(cs42l42_wnf05_freq_enum, CS42L42_ADC_WNF_HPF_CTL, + CS42L42_ADC_WNF_CF_SHIFT, + cs42l42_wnf05_freq_text); + +static const struct snd_kcontrol_new cs42l42_snd_controls[] = { + /* ADC Volume and Filter Controls */ + SOC_SINGLE("ADC Notch Switch", CS42L42_ADC_CTL, + CS42L42_ADC_NOTCH_DIS_SHIFT, true, false), + SOC_SINGLE("ADC Weak Force Switch", CS42L42_ADC_CTL, + CS42L42_ADC_FORCE_WEAK_VCM_SHIFT, true, false), + SOC_SINGLE("ADC Invert Switch", CS42L42_ADC_CTL, + CS42L42_ADC_INV_SHIFT, true, false), + SOC_SINGLE("ADC Boost Switch", CS42L42_ADC_CTL, + CS42L42_ADC_DIG_BOOST_SHIFT, true, false), + SOC_SINGLE_SX_TLV("ADC Volume", CS42L42_ADC_VOLUME, + CS42L42_ADC_VOL_SHIFT, 0xA0, 0x6C, adc_tlv), + SOC_SINGLE("ADC WNF Switch", CS42L42_ADC_WNF_HPF_CTL, + CS42L42_ADC_WNF_EN_SHIFT, true, false), + SOC_SINGLE("ADC HPF Switch", CS42L42_ADC_WNF_HPF_CTL, + CS42L42_ADC_HPF_EN_SHIFT, true, false), + SOC_ENUM("HPF Corner Freq", cs42l42_hpf_freq_enum), + SOC_ENUM("WNF 3dB Freq", cs42l42_wnf3_freq_enum), + SOC_ENUM("WNF 05dB Freq", cs42l42_wnf05_freq_enum), + + /* DAC Volume and Filter Controls */ + SOC_SINGLE("DACA Invert Switch", CS42L42_DAC_CTL1, + CS42L42_DACA_INV_SHIFT, true, false), + SOC_SINGLE("DACB Invert Switch", CS42L42_DAC_CTL1, + CS42L42_DACB_INV_SHIFT, true, false), + SOC_SINGLE("DAC HPF Switch", CS42L42_DAC_CTL2, + CS42L42_DAC_HPF_EN_SHIFT, true, false), + SOC_DOUBLE_R_TLV("Mixer Volume", CS42L42_MIXER_CHA_VOL, + CS42L42_MIXER_CHB_VOL, CS42L42_MIXER_CH_VOL_SHIFT, + 0x3e, 1, mixer_tlv) +}; + +static int cs42l42_hpdrv_evt(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + if (event & SND_SOC_DAPM_POST_PMU) { + /* Enable the channels */ + snd_soc_update_bits(codec, CS42L42_ASP_RX_DAI0_EN, + CS42L42_ASP_RX0_CH_EN_MASK, + (CS42L42_ASP_RX0_CH1_EN | + CS42L42_ASP_RX0_CH2_EN) << + CS42L42_ASP_RX0_CH_EN_SHIFT); + + /* Power up */ + snd_soc_update_bits(codec, CS42L42_PWR_CTL1, + CS42L42_ASP_DAI_PDN_MASK | CS42L42_MIXER_PDN_MASK | + CS42L42_HP_PDN_MASK, 0); + } else if (event & SND_SOC_DAPM_PRE_PMD) { + /* Disable the channels */ + snd_soc_update_bits(codec, CS42L42_ASP_RX_DAI0_EN, + CS42L42_ASP_RX0_CH_EN_MASK, 0); + + /* Power down */ + snd_soc_update_bits(codec, CS42L42_PWR_CTL1, + CS42L42_ASP_DAI_PDN_MASK | CS42L42_MIXER_PDN_MASK | + CS42L42_HP_PDN_MASK, + CS42L42_ASP_DAI_PDN_MASK | CS42L42_MIXER_PDN_MASK | + CS42L42_HP_PDN_MASK); + } else { + dev_err(codec->dev, "Invalid event 0x%x\n", event); + } + return 0; +} + +static const struct snd_soc_dapm_widget cs42l42_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_AIF_IN("SDIN", NULL, 0, CS42L42_ASP_CLK_CFG, + CS42L42_ASP_SCLK_EN_SHIFT, false), + SND_SOC_DAPM_OUT_DRV_E("HPDRV", SND_SOC_NOPM, 0, + 0, NULL, 0, cs42l42_hpdrv_evt, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD) +}; + +static const struct snd_soc_dapm_route cs42l42_audio_map[] = { + {"SDIN", NULL, "Playback"}, + {"HPDRV", NULL, "SDIN"}, + {"HP", NULL, "HPDRV"} +}; + +static int cs42l42_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct cs42l42_private *cs42l42 = snd_soc_codec_get_drvdata(codec); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { + regcache_cache_only(cs42l42->regmap, false); + regcache_sync(cs42l42->regmap); + ret = regulator_bulk_enable( + ARRAY_SIZE(cs42l42->supplies), + cs42l42->supplies); + if (ret != 0) { + dev_err(codec->dev, + "Failed to enable regulators: %d\n", + ret); + return ret; + } + } + break; + case SND_SOC_BIAS_OFF: + + regcache_cache_only(cs42l42->regmap, true); + regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies), + cs42l42->supplies); + break; + } + + return 0; +} + +static int cs42l42_codec_probe(struct snd_soc_codec *codec) +{ + struct cs42l42_private *cs42l42 = + (struct cs42l42_private *)snd_soc_codec_get_drvdata(codec); + + cs42l42->codec = codec; + + return 0; +} + +static const struct snd_soc_codec_driver soc_codec_dev_cs42l42 = { + .probe = cs42l42_codec_probe, + .set_bias_level = cs42l42_set_bias_level, + .ignore_pmdown_time = true, + + .component_driver = { + .dapm_widgets = cs42l42_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs42l42_dapm_widgets), + .dapm_routes = cs42l42_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs42l42_audio_map), + + .controls = cs42l42_snd_controls, + .num_controls = ARRAY_SIZE(cs42l42_snd_controls), + }, +}; + +struct cs42l42_pll_params { + u32 sclk; + u8 mclk_div; + u8 mclk_src_sel; + u8 sclk_prediv; + u8 pll_div_int; + u32 pll_div_frac; + u8 pll_mode; + u8 pll_divout; + u32 mclk_int; + u8 pll_cal_ratio; +}; + +/* + * Common PLL Settings for given SCLK + * Table 4-5 from the Datasheet + */ +static const struct cs42l42_pll_params pll_ratio_table[] = { + { 1536000, 0, 1, 0x00, 0x7D, 0x000000, 0x03, 0x10, 12000000, 125 }, + { 2822400, 0, 1, 0x00, 0x40, 0x000000, 0x03, 0x10, 11289600, 128 }, + { 3000000, 0, 1, 0x00, 0x40, 0x000000, 0x03, 0x10, 12000000, 128 }, + { 3072000, 0, 1, 0x00, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125 }, + { 4000000, 0, 1, 0x00, 0x30, 0x800000, 0x03, 0x10, 12000000, 96 }, + { 4096000, 0, 1, 0x00, 0x2E, 0xE00000, 0x03, 0x10, 12000000, 94 }, + { 5644800, 0, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 11289600, 128 }, + { 6000000, 0, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 12000000, 128 }, + { 6144000, 0, 1, 0x01, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125 }, + { 11289600, 0, 0, 0, 0, 0, 0, 0, 11289600, 0 }, + { 12000000, 0, 0, 0, 0, 0, 0, 0, 12000000, 0 }, + { 12288000, 0, 0, 0, 0, 0, 0, 0, 12288000, 0 }, + { 22579200, 1, 0, 0, 0, 0, 0, 0, 22579200, 0 }, + { 24000000, 1, 0, 0, 0, 0, 0, 0, 24000000, 0 }, + { 24576000, 1, 0, 0, 0, 0, 0, 0, 24576000, 0 } +}; + +static int cs42l42_pll_config(struct snd_soc_codec *codec) +{ + struct cs42l42_private *cs42l42 = snd_soc_codec_get_drvdata(codec); + int i; + u32 fsync; + + for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) { + if (pll_ratio_table[i].sclk == cs42l42->sclk) { + /* Configure the internal sample rate */ + snd_soc_update_bits(codec, CS42L42_MCLK_CTL, + CS42L42_INTERNAL_FS_MASK, + ((pll_ratio_table[i].mclk_int != + 12000000) && + (pll_ratio_table[i].mclk_int != + 24000000)) << + CS42L42_INTERNAL_FS_SHIFT); + /* Set the MCLK src (PLL or SCLK) and the divide + * ratio + */ + snd_soc_update_bits(codec, CS42L42_MCLK_SRC_SEL, + CS42L42_MCLK_SRC_SEL_MASK | + CS42L42_MCLKDIV_MASK, + (pll_ratio_table[i].mclk_src_sel + << CS42L42_MCLK_SRC_SEL_SHIFT) | + (pll_ratio_table[i].mclk_div << + CS42L42_MCLKDIV_SHIFT)); + /* Set up the LRCLK */ + fsync = cs42l42->sclk / cs42l42->srate; + if (((fsync * cs42l42->srate) != cs42l42->sclk) + || ((fsync % 2) != 0)) { + dev_err(codec->dev, + "Unsupported sclk %d/sample rate %d\n", + cs42l42->sclk, + cs42l42->srate); + return -EINVAL; + } + /* Set the LRCLK period */ + snd_soc_update_bits(codec, + CS42L42_FSYNC_P_LOWER, + CS42L42_FSYNC_PERIOD_MASK, + CS42L42_FRAC0_VAL(fsync - 1) << + CS42L42_FSYNC_PERIOD_SHIFT); + snd_soc_update_bits(codec, + CS42L42_FSYNC_P_UPPER, + CS42L42_FSYNC_PERIOD_MASK, + CS42L42_FRAC1_VAL(fsync - 1) << + CS42L42_FSYNC_PERIOD_SHIFT); + /* Set the LRCLK to 50% duty cycle */ + fsync = fsync / 2; + snd_soc_update_bits(codec, + CS42L42_FSYNC_PW_LOWER, + CS42L42_FSYNC_PULSE_WIDTH_MASK, + CS42L42_FRAC0_VAL(fsync - 1) << + CS42L42_FSYNC_PULSE_WIDTH_SHIFT); + snd_soc_update_bits(codec, + CS42L42_FSYNC_PW_UPPER, + CS42L42_FSYNC_PULSE_WIDTH_MASK, + CS42L42_FRAC1_VAL(fsync - 1) << + CS42L42_FSYNC_PULSE_WIDTH_SHIFT); + snd_soc_update_bits(codec, + CS42L42_ASP_FRM_CFG, + CS42L42_ASP_5050_MASK, + CS42L42_ASP_5050_MASK); + /* Set the frame delay to 1.0 SCLK clocks */ + snd_soc_update_bits(codec, CS42L42_ASP_FRM_CFG, + CS42L42_ASP_FSD_MASK, + CS42L42_ASP_FSD_1_0 << + CS42L42_ASP_FSD_SHIFT); + /* Set the sample rates (96k or lower) */ + snd_soc_update_bits(codec, CS42L42_FS_RATE_EN, + CS42L42_FS_EN_MASK, + (CS42L42_FS_EN_IASRC_96K | + CS42L42_FS_EN_OASRC_96K) << + CS42L42_FS_EN_SHIFT); + /* Set the input/output internal MCLK clock ~12 MHz */ + snd_soc_update_bits(codec, CS42L42_IN_ASRC_CLK, + CS42L42_CLK_IASRC_SEL_MASK, + CS42L42_CLK_IASRC_SEL_12 << + CS42L42_CLK_IASRC_SEL_SHIFT); + snd_soc_update_bits(codec, + CS42L42_OUT_ASRC_CLK, + CS42L42_CLK_OASRC_SEL_MASK, + CS42L42_CLK_OASRC_SEL_12 << + CS42L42_CLK_OASRC_SEL_SHIFT); + /* channel 1 on low LRCLK, 32 bit */ + snd_soc_update_bits(codec, + CS42L42_ASP_RX_DAI0_CH1_AP_RES, + CS42L42_ASP_RX_CH_AP_MASK | + CS42L42_ASP_RX_CH_RES_MASK, + (CS42L42_ASP_RX_CH_AP_LOW << + CS42L42_ASP_RX_CH_AP_SHIFT) | + (CS42L42_ASP_RX_CH_RES_32 << + CS42L42_ASP_RX_CH_RES_SHIFT)); + /* Channel 2 on high LRCLK, 32 bit */ + snd_soc_update_bits(codec, + CS42L42_ASP_RX_DAI0_CH2_AP_RES, + CS42L42_ASP_RX_CH_AP_MASK | + CS42L42_ASP_RX_CH_RES_MASK, + (CS42L42_ASP_RX_CH_AP_HI << + CS42L42_ASP_RX_CH_AP_SHIFT) | + (CS42L42_ASP_RX_CH_RES_32 << + CS42L42_ASP_RX_CH_RES_SHIFT)); + if (pll_ratio_table[i].mclk_src_sel == 0) { + /* Pass the clock straight through */ + snd_soc_update_bits(codec, + CS42L42_PLL_CTL1, + CS42L42_PLL_START_MASK, 0); + } else { + /* Configure PLL per table 4-5 */ + snd_soc_update_bits(codec, + CS42L42_PLL_DIV_CFG1, + CS42L42_SCLK_PREDIV_MASK, + pll_ratio_table[i].sclk_prediv + << CS42L42_SCLK_PREDIV_SHIFT); + snd_soc_update_bits(codec, + CS42L42_PLL_DIV_INT, + CS42L42_PLL_DIV_INT_MASK, + pll_ratio_table[i].pll_div_int + << CS42L42_PLL_DIV_INT_SHIFT); + snd_soc_update_bits(codec, + CS42L42_PLL_DIV_FRAC0, + CS42L42_PLL_DIV_FRAC_MASK, + CS42L42_FRAC0_VAL( + pll_ratio_table[i].pll_div_frac) + << CS42L42_PLL_DIV_FRAC_SHIFT); + snd_soc_update_bits(codec, + CS42L42_PLL_DIV_FRAC1, + CS42L42_PLL_DIV_FRAC_MASK, + CS42L42_FRAC1_VAL( + pll_ratio_table[i].pll_div_frac) + << CS42L42_PLL_DIV_FRAC_SHIFT); + snd_soc_update_bits(codec, + CS42L42_PLL_DIV_FRAC2, + CS42L42_PLL_DIV_FRAC_MASK, + CS42L42_FRAC2_VAL( + pll_ratio_table[i].pll_div_frac) + << CS42L42_PLL_DIV_FRAC_SHIFT); + snd_soc_update_bits(codec, + CS42L42_PLL_CTL4, + CS42L42_PLL_MODE_MASK, + pll_ratio_table[i].pll_mode + << CS42L42_PLL_MODE_SHIFT); + snd_soc_update_bits(codec, + CS42L42_PLL_CTL3, + CS42L42_PLL_DIVOUT_MASK, + pll_ratio_table[i].pll_divout + << CS42L42_PLL_DIVOUT_SHIFT); + snd_soc_update_bits(codec, + CS42L42_PLL_CAL_RATIO, + CS42L42_PLL_CAL_RATIO_MASK, + pll_ratio_table[i].pll_cal_ratio + << CS42L42_PLL_CAL_RATIO_SHIFT); + } + return 0; + } + } + + return -EINVAL; +} + +static int cs42l42_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u32 asp_cfg_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFM: + asp_cfg_val |= CS42L42_ASP_MASTER_MODE << + CS42L42_ASP_MODE_SHIFT; + break; + case SND_SOC_DAIFMT_CBS_CFS: + asp_cfg_val |= CS42L42_ASP_SLAVE_MODE << + CS42L42_ASP_MODE_SHIFT; + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_LEFT_J: + break; + default: + return -EINVAL; + } + + /* Bitclock/frame inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + asp_cfg_val |= CS42L42_ASP_POL_INV << + CS42L42_ASP_LCPOL_IN_SHIFT; + break; + case SND_SOC_DAIFMT_IB_NF: + asp_cfg_val |= CS42L42_ASP_POL_INV << + CS42L42_ASP_SCPOL_IN_DAC_SHIFT; + break; + case SND_SOC_DAIFMT_IB_IF: + asp_cfg_val |= CS42L42_ASP_POL_INV << + CS42L42_ASP_LCPOL_IN_SHIFT; + asp_cfg_val |= CS42L42_ASP_POL_INV << + CS42L42_ASP_SCPOL_IN_DAC_SHIFT; + break; + } + + snd_soc_update_bits(codec, CS42L42_ASP_CLK_CFG, + CS42L42_ASP_MODE_MASK | + CS42L42_ASP_SCPOL_IN_DAC_MASK | + CS42L42_ASP_LCPOL_IN_MASK, asp_cfg_val); + + return 0; +} + +static int cs42l42_pcm_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 cs42l42_private *cs42l42 = snd_soc_codec_get_drvdata(codec); + int retval; + + cs42l42->srate = params_rate(params); + cs42l42->swidth = params_width(params); + + retval = cs42l42_pll_config(codec); + + return retval; +} + +static int cs42l42_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct cs42l42_private *cs42l42 = snd_soc_codec_get_drvdata(codec); + + cs42l42->sclk = freq; + + return 0; +} + +static int cs42l42_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + unsigned int regval; + u8 fullScaleVol; + + if (mute) { + /* Mark SCLK as not present to turn on the internal + * oscillator. + */ + snd_soc_update_bits(codec, CS42L42_OSC_SWITCH, + CS42L42_SCLK_PRESENT_MASK, 0); + + snd_soc_update_bits(codec, CS42L42_PLL_CTL1, + CS42L42_PLL_START_MASK, + 0 << CS42L42_PLL_START_SHIFT); + + /* Mute the headphone */ + snd_soc_update_bits(codec, CS42L42_HP_CTL, + CS42L42_HP_ANA_AMUTE_MASK | + CS42L42_HP_ANA_BMUTE_MASK, + CS42L42_HP_ANA_AMUTE_MASK | + CS42L42_HP_ANA_BMUTE_MASK); + } else { + snd_soc_update_bits(codec, CS42L42_PLL_CTL1, + CS42L42_PLL_START_MASK, + 1 << CS42L42_PLL_START_SHIFT); + /* Read the headphone load */ + regval = snd_soc_read(codec, CS42L42_LOAD_DET_RCSTAT); + if (((regval & CS42L42_RLA_STAT_MASK) >> + CS42L42_RLA_STAT_SHIFT) == CS42L42_RLA_STAT_15_OHM) { + fullScaleVol = CS42L42_HP_FULL_SCALE_VOL_MASK; + } else { + fullScaleVol = 0; + } + + /* Un-mute the headphone, set the full scale volume flag */ + snd_soc_update_bits(codec, CS42L42_HP_CTL, + CS42L42_HP_ANA_AMUTE_MASK | + CS42L42_HP_ANA_BMUTE_MASK | + CS42L42_HP_FULL_SCALE_VOL_MASK, fullScaleVol); + + /* Mark SCLK as present, turn off internal oscillator */ + snd_soc_update_bits(codec, CS42L42_OSC_SWITCH, + CS42L42_SCLK_PRESENT_MASK, + CS42L42_SCLK_PRESENT_MASK); + } + + return 0; +} + +#define CS42L42_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + + +static struct snd_soc_dai_ops cs42l42_ops = { + .hw_params = cs42l42_pcm_hw_params, + .set_fmt = cs42l42_set_dai_fmt, + .set_sysclk = cs42l42_set_sysclk, + .digital_mute = cs42l42_digital_mute +}; + +static struct snd_soc_dai_driver cs42l42_dai = { + .name = "cs42l42", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = CS42L42_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = CS42L42_FORMATS, + }, + .ops = &cs42l42_ops, +}; + +static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42) +{ + unsigned int hs_det_status; + unsigned int int_status; + + /* Mask the auto detect interrupt */ + regmap_update_bits(cs42l42->regmap, + CS42L42_CODEC_INT_MASK, + CS42L42_PDN_DONE_MASK | + CS42L42_HSDET_AUTO_DONE_MASK, + (1 << CS42L42_PDN_DONE_SHIFT) | + (1 << CS42L42_HSDET_AUTO_DONE_SHIFT)); + + /* Set hs detect to automatic, disabled mode */ + regmap_update_bits(cs42l42->regmap, + CS42L42_HSDET_CTL2, + CS42L42_HSDET_CTRL_MASK | + CS42L42_HSDET_SET_MASK | + CS42L42_HSBIAS_REF_MASK | + CS42L42_HSDET_AUTO_TIME_MASK, + (2 << CS42L42_HSDET_CTRL_SHIFT) | + (2 << CS42L42_HSDET_SET_SHIFT) | + (0 << CS42L42_HSBIAS_REF_SHIFT) | + (3 << CS42L42_HSDET_AUTO_TIME_SHIFT)); + + /* Read and save the hs detection result */ + regmap_read(cs42l42->regmap, CS42L42_HS_DET_STATUS, &hs_det_status); + + cs42l42->hs_type = (hs_det_status & CS42L42_HSDET_TYPE_MASK) >> + CS42L42_HSDET_TYPE_SHIFT; + + /* Set up button detection */ + if ((cs42l42->hs_type == CS42L42_PLUG_CTIA) || + (cs42l42->hs_type == CS42L42_PLUG_OMTP)) { + /* Set auto HS bias settings to default */ + regmap_update_bits(cs42l42->regmap, + CS42L42_HSBIAS_SC_AUTOCTL, + CS42L42_HSBIAS_SENSE_EN_MASK | + CS42L42_AUTO_HSBIAS_HIZ_MASK | + CS42L42_TIP_SENSE_EN_MASK | + CS42L42_HSBIAS_SENSE_TRIP_MASK, + (0 << CS42L42_HSBIAS_SENSE_EN_SHIFT) | + (0 << CS42L42_AUTO_HSBIAS_HIZ_SHIFT) | + (0 << CS42L42_TIP_SENSE_EN_SHIFT) | + (3 << CS42L42_HSBIAS_SENSE_TRIP_SHIFT)); + + /* Set up hs detect level sensitivity */ + regmap_update_bits(cs42l42->regmap, + CS42L42_MIC_DET_CTL1, + CS42L42_LATCH_TO_VP_MASK | + CS42L42_EVENT_STAT_SEL_MASK | + CS42L42_HS_DET_LEVEL_MASK, + (1 << CS42L42_LATCH_TO_VP_SHIFT) | + (0 << CS42L42_EVENT_STAT_SEL_SHIFT) | + (cs42l42->bias_thresholds[0] << + CS42L42_HS_DET_LEVEL_SHIFT)); + + /* Set auto HS bias settings to default */ + regmap_update_bits(cs42l42->regmap, + CS42L42_HSBIAS_SC_AUTOCTL, + CS42L42_HSBIAS_SENSE_EN_MASK | + CS42L42_AUTO_HSBIAS_HIZ_MASK | + CS42L42_TIP_SENSE_EN_MASK | + CS42L42_HSBIAS_SENSE_TRIP_MASK, + (1 << CS42L42_HSBIAS_SENSE_EN_SHIFT) | + (1 << CS42L42_AUTO_HSBIAS_HIZ_SHIFT) | + (0 << CS42L42_TIP_SENSE_EN_SHIFT) | + (3 << CS42L42_HSBIAS_SENSE_TRIP_SHIFT)); + + /* Turn on level detect circuitry */ + regmap_update_bits(cs42l42->regmap, + CS42L42_MISC_DET_CTL, + CS42L42_DETECT_MODE_MASK | + CS42L42_HSBIAS_CTL_MASK | + CS42L42_PDN_MIC_LVL_DET_MASK, + (0 << CS42L42_DETECT_MODE_SHIFT) | + (3 << CS42L42_HSBIAS_CTL_SHIFT) | + (0 << CS42L42_PDN_MIC_LVL_DET_SHIFT)); + + msleep(cs42l42->btn_det_init_dbnce); + + /* Clear any button interrupts before unmasking them */ + regmap_read(cs42l42->regmap, CS42L42_DET_INT_STATUS2, + &int_status); + + /* Unmask button detect interrupts */ + regmap_update_bits(cs42l42->regmap, + CS42L42_DET_INT2_MASK, + CS42L42_M_DETECT_TF_MASK | + CS42L42_M_DETECT_FT_MASK | + CS42L42_M_HSBIAS_HIZ_MASK | + CS42L42_M_SHORT_RLS_MASK | + CS42L42_M_SHORT_DET_MASK, + (0 << CS42L42_M_DETECT_TF_SHIFT) | + (0 << CS42L42_M_DETECT_FT_SHIFT) | + (0 << CS42L42_M_HSBIAS_HIZ_SHIFT) | + (1 << CS42L42_M_SHORT_RLS_SHIFT) | + (1 << CS42L42_M_SHORT_DET_SHIFT)); + } else { + /* Make sure button detect and HS bias circuits are off */ + regmap_update_bits(cs42l42->regmap, + CS42L42_MISC_DET_CTL, + CS42L42_DETECT_MODE_MASK | + CS42L42_HSBIAS_CTL_MASK | + CS42L42_PDN_MIC_LVL_DET_MASK, + (0 << CS42L42_DETECT_MODE_SHIFT) | + (1 << CS42L42_HSBIAS_CTL_SHIFT) | + (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT)); + } + + regmap_update_bits(cs42l42->regmap, + CS42L42_DAC_CTL2, + CS42L42_HPOUT_PULLDOWN_MASK | + CS42L42_HPOUT_LOAD_MASK | + CS42L42_HPOUT_CLAMP_MASK | + CS42L42_DAC_HPF_EN_MASK | + CS42L42_DAC_MON_EN_MASK, + (0 << CS42L42_HPOUT_PULLDOWN_SHIFT) | + (0 << CS42L42_HPOUT_LOAD_SHIFT) | + (0 << CS42L42_HPOUT_CLAMP_SHIFT) | + (1 << CS42L42_DAC_HPF_EN_SHIFT) | + (0 << CS42L42_DAC_MON_EN_SHIFT)); + + /* Unmask tip sense interrupts */ + regmap_update_bits(cs42l42->regmap, + CS42L42_TSRS_PLUG_INT_MASK, + CS42L42_RS_PLUG_MASK | + CS42L42_RS_UNPLUG_MASK | + CS42L42_TS_PLUG_MASK | + CS42L42_TS_UNPLUG_MASK, + (1 << CS42L42_RS_PLUG_SHIFT) | + (1 << CS42L42_RS_UNPLUG_SHIFT) | + (0 << CS42L42_TS_PLUG_SHIFT) | + (0 << CS42L42_TS_UNPLUG_SHIFT)); +} + +static void cs42l42_init_hs_type_detect(struct cs42l42_private *cs42l42) +{ + /* Mask tip sense interrupts */ + regmap_update_bits(cs42l42->regmap, + CS42L42_TSRS_PLUG_INT_MASK, + CS42L42_RS_PLUG_MASK | + CS42L42_RS_UNPLUG_MASK | + CS42L42_TS_PLUG_MASK | + CS42L42_TS_UNPLUG_MASK, + (1 << CS42L42_RS_PLUG_SHIFT) | + (1 << CS42L42_RS_UNPLUG_SHIFT) | + (1 << CS42L42_TS_PLUG_SHIFT) | + (1 << CS42L42_TS_UNPLUG_SHIFT)); + + /* Make sure button detect and HS bias circuits are off */ + regmap_update_bits(cs42l42->regmap, + CS42L42_MISC_DET_CTL, + CS42L42_DETECT_MODE_MASK | + CS42L42_HSBIAS_CTL_MASK | + CS42L42_PDN_MIC_LVL_DET_MASK, + (0 << CS42L42_DETECT_MODE_SHIFT) | + (1 << CS42L42_HSBIAS_CTL_SHIFT) | + (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT)); + + /* Set auto HS bias settings to default */ + regmap_update_bits(cs42l42->regmap, + CS42L42_HSBIAS_SC_AUTOCTL, + CS42L42_HSBIAS_SENSE_EN_MASK | + CS42L42_AUTO_HSBIAS_HIZ_MASK | + CS42L42_TIP_SENSE_EN_MASK | + CS42L42_HSBIAS_SENSE_TRIP_MASK, + (0 << CS42L42_HSBIAS_SENSE_EN_SHIFT) | + (0 << CS42L42_AUTO_HSBIAS_HIZ_SHIFT) | + (0 << CS42L42_TIP_SENSE_EN_SHIFT) | + (3 << CS42L42_HSBIAS_SENSE_TRIP_SHIFT)); + + /* Set hs detect to manual, disabled mode */ + regmap_update_bits(cs42l42->regmap, + CS42L42_HSDET_CTL2, + CS42L42_HSDET_CTRL_MASK | + CS42L42_HSDET_SET_MASK | + CS42L42_HSBIAS_REF_MASK | + CS42L42_HSDET_AUTO_TIME_MASK, + (0 << CS42L42_HSDET_CTRL_SHIFT) | + (2 << CS42L42_HSDET_SET_SHIFT) | + (0 << CS42L42_HSBIAS_REF_SHIFT) | + (3 << CS42L42_HSDET_AUTO_TIME_SHIFT)); + + regmap_update_bits(cs42l42->regmap, + CS42L42_DAC_CTL2, + CS42L42_HPOUT_PULLDOWN_MASK | + CS42L42_HPOUT_LOAD_MASK | + CS42L42_HPOUT_CLAMP_MASK | + CS42L42_DAC_HPF_EN_MASK | + CS42L42_DAC_MON_EN_MASK, + (8 << CS42L42_HPOUT_PULLDOWN_SHIFT) | + (0 << CS42L42_HPOUT_LOAD_SHIFT) | + (1 << CS42L42_HPOUT_CLAMP_SHIFT) | + (1 << CS42L42_DAC_HPF_EN_SHIFT) | + (1 << CS42L42_DAC_MON_EN_SHIFT)); + + /* Power up HS bias to 2.7V */ + regmap_update_bits(cs42l42->regmap, + CS42L42_MISC_DET_CTL, + CS42L42_DETECT_MODE_MASK | + CS42L42_HSBIAS_CTL_MASK | + CS42L42_PDN_MIC_LVL_DET_MASK, + (0 << CS42L42_DETECT_MODE_SHIFT) | + (3 << CS42L42_HSBIAS_CTL_SHIFT) | + (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT)); + + /* Wait for HS bias to ramp up */ + msleep(cs42l42->hs_bias_ramp_time); + + /* Unmask auto detect interrupt */ + regmap_update_bits(cs42l42->regmap, + CS42L42_CODEC_INT_MASK, + CS42L42_PDN_DONE_MASK | + CS42L42_HSDET_AUTO_DONE_MASK, + (1 << CS42L42_PDN_DONE_SHIFT) | + (0 << CS42L42_HSDET_AUTO_DONE_SHIFT)); + + /* Set hs detect to automatic, enabled mode */ + regmap_update_bits(cs42l42->regmap, + CS42L42_HSDET_CTL2, + CS42L42_HSDET_CTRL_MASK | + CS42L42_HSDET_SET_MASK | + CS42L42_HSBIAS_REF_MASK | + CS42L42_HSDET_AUTO_TIME_MASK, + (3 << CS42L42_HSDET_CTRL_SHIFT) | + (2 << CS42L42_HSDET_SET_SHIFT) | + (0 << CS42L42_HSBIAS_REF_SHIFT) | + (3 << CS42L42_HSDET_AUTO_TIME_SHIFT)); +} + +static void cs42l42_cancel_hs_type_detect(struct cs42l42_private *cs42l42) +{ + /* Mask button detect interrupts */ + regmap_update_bits(cs42l42->regmap, + CS42L42_DET_INT2_MASK, + CS42L42_M_DETECT_TF_MASK | + CS42L42_M_DETECT_FT_MASK | + CS42L42_M_HSBIAS_HIZ_MASK | + CS42L42_M_SHORT_RLS_MASK | + CS42L42_M_SHORT_DET_MASK, + (1 << CS42L42_M_DETECT_TF_SHIFT) | + (1 << CS42L42_M_DETECT_FT_SHIFT) | + (1 << CS42L42_M_HSBIAS_HIZ_SHIFT) | + (1 << CS42L42_M_SHORT_RLS_SHIFT) | + (1 << CS42L42_M_SHORT_DET_SHIFT)); + + /* Ground HS bias */ + regmap_update_bits(cs42l42->regmap, + CS42L42_MISC_DET_CTL, + CS42L42_DETECT_MODE_MASK | + CS42L42_HSBIAS_CTL_MASK | + CS42L42_PDN_MIC_LVL_DET_MASK, + (0 << CS42L42_DETECT_MODE_SHIFT) | + (1 << CS42L42_HSBIAS_CTL_SHIFT) | + (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT)); + + /* Set auto HS bias settings to default */ + regmap_update_bits(cs42l42->regmap, + CS42L42_HSBIAS_SC_AUTOCTL, + CS42L42_HSBIAS_SENSE_EN_MASK | + CS42L42_AUTO_HSBIAS_HIZ_MASK | + CS42L42_TIP_SENSE_EN_MASK | + CS42L42_HSBIAS_SENSE_TRIP_MASK, + (0 << CS42L42_HSBIAS_SENSE_EN_SHIFT) | + (0 << CS42L42_AUTO_HSBIAS_HIZ_SHIFT) | + (0 << CS42L42_TIP_SENSE_EN_SHIFT) | + (3 << CS42L42_HSBIAS_SENSE_TRIP_SHIFT)); + + /* Set hs detect to manual, disabled mode */ + regmap_update_bits(cs42l42->regmap, + CS42L42_HSDET_CTL2, + CS42L42_HSDET_CTRL_MASK | + CS42L42_HSDET_SET_MASK | + CS42L42_HSBIAS_REF_MASK | + CS42L42_HSDET_AUTO_TIME_MASK, + (0 << CS42L42_HSDET_CTRL_SHIFT) | + (2 << CS42L42_HSDET_SET_SHIFT) | + (0 << CS42L42_HSBIAS_REF_SHIFT) | + (3 << CS42L42_HSDET_AUTO_TIME_SHIFT)); +} + +static void cs42l42_handle_button_press(struct cs42l42_private *cs42l42) +{ + int bias_level; + unsigned int detect_status; + + /* Mask button detect interrupts */ + regmap_update_bits(cs42l42->regmap, + CS42L42_DET_INT2_MASK, + CS42L42_M_DETECT_TF_MASK | + CS42L42_M_DETECT_FT_MASK | + CS42L42_M_HSBIAS_HIZ_MASK | + CS42L42_M_SHORT_RLS_MASK | + CS42L42_M_SHORT_DET_MASK, + (1 << CS42L42_M_DETECT_TF_SHIFT) | + (1 << CS42L42_M_DETECT_FT_SHIFT) | + (1 << CS42L42_M_HSBIAS_HIZ_SHIFT) | + (1 << CS42L42_M_SHORT_RLS_SHIFT) | + (1 << CS42L42_M_SHORT_DET_SHIFT)); + + usleep_range(cs42l42->btn_det_event_dbnce * 1000, + cs42l42->btn_det_event_dbnce * 2000); + + /* Test all 4 level detect biases */ + bias_level = 1; + do { + /* Adjust button detect level sensitivity */ + regmap_update_bits(cs42l42->regmap, + CS42L42_MIC_DET_CTL1, + CS42L42_LATCH_TO_VP_MASK | + CS42L42_EVENT_STAT_SEL_MASK | + CS42L42_HS_DET_LEVEL_MASK, + (1 << CS42L42_LATCH_TO_VP_SHIFT) | + (0 << CS42L42_EVENT_STAT_SEL_SHIFT) | + (cs42l42->bias_thresholds[bias_level] << + CS42L42_HS_DET_LEVEL_SHIFT)); + + regmap_read(cs42l42->regmap, CS42L42_DET_STATUS2, + &detect_status); + } while ((detect_status & CS42L42_HS_TRUE_MASK) && + (++bias_level < CS42L42_NUM_BIASES)); + + switch (bias_level) { + case 1: /* Function C button press */ + dev_dbg(cs42l42->codec->dev, "Function C button press\n"); + break; + case 2: /* Function B button press */ + dev_dbg(cs42l42->codec->dev, "Function B button press\n"); + break; + case 3: /* Function D button press */ + dev_dbg(cs42l42->codec->dev, "Function D button press\n"); + break; + case 4: /* Function A button press */ + dev_dbg(cs42l42->codec->dev, "Function A button press\n"); + break; + } + + /* Set button detect level sensitivity back to default */ + regmap_update_bits(cs42l42->regmap, + CS42L42_MIC_DET_CTL1, + CS42L42_LATCH_TO_VP_MASK | + CS42L42_EVENT_STAT_SEL_MASK | + CS42L42_HS_DET_LEVEL_MASK, + (1 << CS42L42_LATCH_TO_VP_SHIFT) | + (0 << CS42L42_EVENT_STAT_SEL_SHIFT) | + (cs42l42->bias_thresholds[0] << CS42L42_HS_DET_LEVEL_SHIFT)); + + /* Clear any button interrupts before unmasking them */ + regmap_read(cs42l42->regmap, CS42L42_DET_INT_STATUS2, + &detect_status); + + /* Unmask button detect interrupts */ + regmap_update_bits(cs42l42->regmap, + CS42L42_DET_INT2_MASK, + CS42L42_M_DETECT_TF_MASK | + CS42L42_M_DETECT_FT_MASK | + CS42L42_M_HSBIAS_HIZ_MASK | + CS42L42_M_SHORT_RLS_MASK | + CS42L42_M_SHORT_DET_MASK, + (0 << CS42L42_M_DETECT_TF_SHIFT) | + (0 << CS42L42_M_DETECT_FT_SHIFT) | + (0 << CS42L42_M_HSBIAS_HIZ_SHIFT) | + (1 << CS42L42_M_SHORT_RLS_SHIFT) | + (1 << CS42L42_M_SHORT_DET_SHIFT)); +} + +struct cs42l42_irq_params { + u16 status_addr; + u16 mask_addr; + u8 mask; +}; + +static const struct cs42l42_irq_params irq_params_table[] = { + {CS42L42_ADC_OVFL_STATUS, CS42L42_ADC_OVFL_INT_MASK, + CS42L42_ADC_OVFL_VAL_MASK}, + {CS42L42_MIXER_STATUS, CS42L42_MIXER_INT_MASK, + CS42L42_MIXER_VAL_MASK}, + {CS42L42_SRC_STATUS, CS42L42_SRC_INT_MASK, + CS42L42_SRC_VAL_MASK}, + {CS42L42_ASP_RX_STATUS, CS42L42_ASP_RX_INT_MASK, + CS42L42_ASP_RX_VAL_MASK}, + {CS42L42_ASP_TX_STATUS, CS42L42_ASP_TX_INT_MASK, + CS42L42_ASP_TX_VAL_MASK}, + {CS42L42_CODEC_STATUS, CS42L42_CODEC_INT_MASK, + CS42L42_CODEC_VAL_MASK}, + {CS42L42_DET_INT_STATUS1, CS42L42_DET_INT1_MASK, + CS42L42_DET_INT_VAL1_MASK}, + {CS42L42_DET_INT_STATUS2, CS42L42_DET_INT2_MASK, + CS42L42_DET_INT_VAL2_MASK}, + {CS42L42_SRCPL_INT_STATUS, CS42L42_SRCPL_INT_MASK, + CS42L42_SRCPL_VAL_MASK}, + {CS42L42_VPMON_STATUS, CS42L42_VPMON_INT_MASK, + CS42L42_VPMON_VAL_MASK}, + {CS42L42_PLL_LOCK_STATUS, CS42L42_PLL_LOCK_INT_MASK, + CS42L42_PLL_LOCK_VAL_MASK}, + {CS42L42_TSRS_PLUG_STATUS, CS42L42_TSRS_PLUG_INT_MASK, + CS42L42_TSRS_PLUG_VAL_MASK} +}; + +static irqreturn_t cs42l42_irq_thread(int irq, void *data) +{ + struct cs42l42_private *cs42l42 = (struct cs42l42_private *)data; + struct snd_soc_codec *codec = cs42l42->codec; + unsigned int stickies[12]; + unsigned int masks[12]; + unsigned int current_plug_status; + unsigned int current_button_status; + unsigned int i; + + /* Read sticky registers to clear interurpt */ + for (i = 0; i < ARRAY_SIZE(stickies); i++) { + regmap_read(cs42l42->regmap, irq_params_table[i].status_addr, + &(stickies[i])); + regmap_read(cs42l42->regmap, irq_params_table[i].mask_addr, + &(masks[i])); + stickies[i] = stickies[i] & (~masks[i]) & + irq_params_table[i].mask; + } + + /* Read tip sense status before handling type detect */ + current_plug_status = (stickies[11] & + (CS42L42_TS_PLUG_MASK | CS42L42_TS_UNPLUG_MASK)) >> + CS42L42_TS_PLUG_SHIFT; + + /* Read button sense status */ + current_button_status = stickies[7] & + (CS42L42_M_DETECT_TF_MASK | + CS42L42_M_DETECT_FT_MASK | + CS42L42_M_HSBIAS_HIZ_MASK); + + /* Check auto-detect status */ + if ((~masks[5]) & irq_params_table[5].mask) { + if (stickies[5] & CS42L42_HSDET_AUTO_DONE_MASK) { + cs42l42_process_hs_type_detect(cs42l42); + dev_dbg(codec->dev, + "Auto detect done (%d)\n", + cs42l42->hs_type); + } + } + + /* Check tip sense status */ + if ((~masks[11]) & irq_params_table[11].mask) { + switch (current_plug_status) { + case CS42L42_TS_PLUG: + if (cs42l42->plug_state != CS42L42_TS_PLUG) { + cs42l42->plug_state = CS42L42_TS_PLUG; + cs42l42_init_hs_type_detect(cs42l42); + } + break; + + case CS42L42_TS_UNPLUG: + if (cs42l42->plug_state != CS42L42_TS_UNPLUG) { + cs42l42->plug_state = CS42L42_TS_UNPLUG; + cs42l42_cancel_hs_type_detect(cs42l42); + dev_dbg(codec->dev, + "Unplug event\n"); + } + break; + + default: + if (cs42l42->plug_state != CS42L42_TS_TRANS) + cs42l42->plug_state = CS42L42_TS_TRANS; + } + } + + /* Check button detect status */ + if ((~masks[7]) & irq_params_table[7].mask) { + if (!(current_button_status & + CS42L42_M_HSBIAS_HIZ_MASK)) { + + if (current_button_status & + CS42L42_M_DETECT_TF_MASK) { + dev_dbg(codec->dev, + "Button released\n"); + } else if (current_button_status & + CS42L42_M_DETECT_FT_MASK) { + cs42l42_handle_button_press(cs42l42); + } + } + } + + return IRQ_HANDLED; +} + +static void cs42l42_set_interrupt_masks(struct cs42l42_private *cs42l42) +{ + regmap_update_bits(cs42l42->regmap, CS42L42_ADC_OVFL_INT_MASK, + CS42L42_ADC_OVFL_MASK, + (1 << CS42L42_ADC_OVFL_SHIFT)); + + regmap_update_bits(cs42l42->regmap, CS42L42_MIXER_INT_MASK, + CS42L42_MIX_CHB_OVFL_MASK | + CS42L42_MIX_CHA_OVFL_MASK | + CS42L42_EQ_OVFL_MASK | + CS42L42_EQ_BIQUAD_OVFL_MASK, + (1 << CS42L42_MIX_CHB_OVFL_SHIFT) | + (1 << CS42L42_MIX_CHA_OVFL_SHIFT) | + (1 << CS42L42_EQ_OVFL_SHIFT) | + (1 << CS42L42_EQ_BIQUAD_OVFL_SHIFT)); + + regmap_update_bits(cs42l42->regmap, CS42L42_SRC_INT_MASK, + CS42L42_SRC_ILK_MASK | + CS42L42_SRC_OLK_MASK | + CS42L42_SRC_IUNLK_MASK | + CS42L42_SRC_OUNLK_MASK, + (1 << CS42L42_SRC_ILK_SHIFT) | + (1 << CS42L42_SRC_OLK_SHIFT) | + (1 << CS42L42_SRC_IUNLK_SHIFT) | + (1 << CS42L42_SRC_OUNLK_SHIFT)); + + regmap_update_bits(cs42l42->regmap, CS42L42_ASP_RX_INT_MASK, + CS42L42_ASPRX_NOLRCK_MASK | + CS42L42_ASPRX_EARLY_MASK | + CS42L42_ASPRX_LATE_MASK | + CS42L42_ASPRX_ERROR_MASK | + CS42L42_ASPRX_OVLD_MASK, + (1 << CS42L42_ASPRX_NOLRCK_SHIFT) | + (1 << CS42L42_ASPRX_EARLY_SHIFT) | + (1 << CS42L42_ASPRX_LATE_SHIFT) | + (1 << CS42L42_ASPRX_ERROR_SHIFT) | + (1 << CS42L42_ASPRX_OVLD_SHIFT)); + + regmap_update_bits(cs42l42->regmap, CS42L42_ASP_TX_INT_MASK, + CS42L42_ASPTX_NOLRCK_MASK | + CS42L42_ASPTX_EARLY_MASK | + CS42L42_ASPTX_LATE_MASK | + CS42L42_ASPTX_SMERROR_MASK, + (1 << CS42L42_ASPTX_NOLRCK_SHIFT) | + (1 << CS42L42_ASPTX_EARLY_SHIFT) | + (1 << CS42L42_ASPTX_LATE_SHIFT) | + (1 << CS42L42_ASPTX_SMERROR_SHIFT)); + + regmap_update_bits(cs42l42->regmap, CS42L42_CODEC_INT_MASK, + CS42L42_PDN_DONE_MASK | + CS42L42_HSDET_AUTO_DONE_MASK, + (1 << CS42L42_PDN_DONE_SHIFT) | + (1 << CS42L42_HSDET_AUTO_DONE_SHIFT)); + + regmap_update_bits(cs42l42->regmap, CS42L42_SRCPL_INT_MASK, + CS42L42_SRCPL_ADC_LK_MASK | + CS42L42_SRCPL_DAC_LK_MASK | + CS42L42_SRCPL_ADC_UNLK_MASK | + CS42L42_SRCPL_DAC_UNLK_MASK, + (1 << CS42L42_SRCPL_ADC_LK_SHIFT) | + (1 << CS42L42_SRCPL_DAC_LK_SHIFT) | + (1 << CS42L42_SRCPL_ADC_UNLK_SHIFT) | + (1 << CS42L42_SRCPL_DAC_UNLK_SHIFT)); + + regmap_update_bits(cs42l42->regmap, CS42L42_DET_INT1_MASK, + CS42L42_TIP_SENSE_UNPLUG_MASK | + CS42L42_TIP_SENSE_PLUG_MASK | + CS42L42_HSBIAS_SENSE_MASK, + (1 << CS42L42_TIP_SENSE_UNPLUG_SHIFT) | + (1 << CS42L42_TIP_SENSE_PLUG_SHIFT) | + (1 << CS42L42_HSBIAS_SENSE_SHIFT)); + + regmap_update_bits(cs42l42->regmap, CS42L42_DET_INT2_MASK, + CS42L42_M_DETECT_TF_MASK | + CS42L42_M_DETECT_FT_MASK | + CS42L42_M_HSBIAS_HIZ_MASK | + CS42L42_M_SHORT_RLS_MASK | + CS42L42_M_SHORT_DET_MASK, + (1 << CS42L42_M_DETECT_TF_SHIFT) | + (1 << CS42L42_M_DETECT_FT_SHIFT) | + (1 << CS42L42_M_HSBIAS_HIZ_SHIFT) | + (1 << CS42L42_M_SHORT_RLS_SHIFT) | + (1 << CS42L42_M_SHORT_DET_SHIFT)); + + regmap_update_bits(cs42l42->regmap, CS42L42_VPMON_INT_MASK, + CS42L42_VPMON_MASK, + (1 << CS42L42_VPMON_SHIFT)); + + regmap_update_bits(cs42l42->regmap, CS42L42_PLL_LOCK_INT_MASK, + CS42L42_PLL_LOCK_MASK, + (1 << CS42L42_PLL_LOCK_SHIFT)); + + regmap_update_bits(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK, + CS42L42_RS_PLUG_MASK | + CS42L42_RS_UNPLUG_MASK | + CS42L42_TS_PLUG_MASK | + CS42L42_TS_UNPLUG_MASK, + (1 << CS42L42_RS_PLUG_SHIFT) | + (1 << CS42L42_RS_UNPLUG_SHIFT) | + (0 << CS42L42_TS_PLUG_SHIFT) | + (0 << CS42L42_TS_UNPLUG_SHIFT)); +} + +static void cs42l42_setup_hs_type_detect(struct cs42l42_private *cs42l42) +{ + unsigned int reg; + + cs42l42->hs_type = CS42L42_PLUG_INVALID; + + /* Latch analog controls to VP power domain */ + regmap_update_bits(cs42l42->regmap, CS42L42_MIC_DET_CTL1, + CS42L42_LATCH_TO_VP_MASK | + CS42L42_EVENT_STAT_SEL_MASK | + CS42L42_HS_DET_LEVEL_MASK, + (1 << CS42L42_LATCH_TO_VP_SHIFT) | + (0 << CS42L42_EVENT_STAT_SEL_SHIFT) | + (cs42l42->bias_thresholds[0] << + CS42L42_HS_DET_LEVEL_SHIFT)); + + /* Remove ground noise-suppression clamps */ + regmap_update_bits(cs42l42->regmap, + CS42L42_HS_CLAMP_DISABLE, + CS42L42_HS_CLAMP_DISABLE_MASK, + (1 << CS42L42_HS_CLAMP_DISABLE_SHIFT)); + + /* Enable the tip sense circuit */ + regmap_update_bits(cs42l42->regmap, CS42L42_TIPSENSE_CTL, + CS42L42_TIP_SENSE_CTRL_MASK | + CS42L42_TIP_SENSE_INV_MASK | + CS42L42_TIP_SENSE_DEBOUNCE_MASK, + (3 << CS42L42_TIP_SENSE_CTRL_SHIFT) | + (0 << CS42L42_TIP_SENSE_INV_SHIFT) | + (2 << CS42L42_TIP_SENSE_DEBOUNCE_SHIFT)); + + /* Save the initial status of the tip sense */ + regmap_read(cs42l42->regmap, + CS42L42_TSRS_PLUG_STATUS, + ®); + cs42l42->plug_state = (((char) reg) & + (CS42L42_TS_PLUG_MASK | CS42L42_TS_UNPLUG_MASK)) >> + CS42L42_TS_PLUG_SHIFT; +} + +static const unsigned int threshold_defaults[] = { + CS42L42_HS_DET_LEVEL_15, + CS42L42_HS_DET_LEVEL_8, + CS42L42_HS_DET_LEVEL_4, + CS42L42_HS_DET_LEVEL_1 +}; + +static int cs42l42_handle_device_data(struct i2c_client *i2c_client, + struct cs42l42_private *cs42l42) +{ + struct device_node *np = i2c_client->dev.of_node; + unsigned int val; + unsigned int thresholds[CS42L42_NUM_BIASES]; + int ret; + int i; + + ret = of_property_read_u32(np, "cirrus,ts-inv", &val); + + if (!ret) { + switch (val) { + case CS42L42_TS_INV_EN: + case CS42L42_TS_INV_DIS: + cs42l42->ts_inv = val; + break; + default: + dev_err(&i2c_client->dev, + "Wrong cirrus,ts-inv DT value %d\n", + val); + cs42l42->ts_inv = CS42L42_TS_INV_DIS; + } + } else { + cs42l42->ts_inv = CS42L42_TS_INV_DIS; + } + + regmap_update_bits(cs42l42->regmap, CS42L42_TSENSE_CTL, + CS42L42_TS_INV_MASK, + (cs42l42->ts_inv << CS42L42_TS_INV_SHIFT)); + + ret = of_property_read_u32(np, "cirrus,ts-dbnc-rise", &val); + + if (!ret) { + switch (val) { + case CS42L42_TS_DBNCE_0: + case CS42L42_TS_DBNCE_125: + case CS42L42_TS_DBNCE_250: + case CS42L42_TS_DBNCE_500: + case CS42L42_TS_DBNCE_750: + case CS42L42_TS_DBNCE_1000: + case CS42L42_TS_DBNCE_1250: + case CS42L42_TS_DBNCE_1500: + cs42l42->ts_dbnc_rise = val; + break; + default: + dev_err(&i2c_client->dev, + "Wrong cirrus,ts-dbnc-rise DT value %d\n", + val); + cs42l42->ts_dbnc_rise = CS42L42_TS_DBNCE_1000; + } + } else { + cs42l42->ts_dbnc_rise = CS42L42_TS_DBNCE_1000; + } + + regmap_update_bits(cs42l42->regmap, CS42L42_TSENSE_CTL, + CS42L42_TS_RISE_DBNCE_TIME_MASK, + (cs42l42->ts_dbnc_rise << + CS42L42_TS_RISE_DBNCE_TIME_SHIFT)); + + ret = of_property_read_u32(np, "cirrus,ts-dbnc-fall", &val); + + if (!ret) { + switch (val) { + case CS42L42_TS_DBNCE_0: + case CS42L42_TS_DBNCE_125: + case CS42L42_TS_DBNCE_250: + case CS42L42_TS_DBNCE_500: + case CS42L42_TS_DBNCE_750: + case CS42L42_TS_DBNCE_1000: + case CS42L42_TS_DBNCE_1250: + case CS42L42_TS_DBNCE_1500: + cs42l42->ts_dbnc_fall = val; + break; + default: + dev_err(&i2c_client->dev, + "Wrong cirrus,ts-dbnc-fall DT value %d\n", + val); + cs42l42->ts_dbnc_fall = CS42L42_TS_DBNCE_0; + } + } else { + cs42l42->ts_dbnc_fall = CS42L42_TS_DBNCE_0; + } + + regmap_update_bits(cs42l42->regmap, CS42L42_TSENSE_CTL, + CS42L42_TS_FALL_DBNCE_TIME_MASK, + (cs42l42->ts_dbnc_fall << + CS42L42_TS_FALL_DBNCE_TIME_SHIFT)); + + ret = of_property_read_u32(np, "cirrus,btn-det-init-dbnce", &val); + + if (!ret) { + if ((val >= CS42L42_BTN_DET_INIT_DBNCE_MIN) && + (val <= CS42L42_BTN_DET_INIT_DBNCE_MAX)) + cs42l42->btn_det_init_dbnce = val; + else { + dev_err(&i2c_client->dev, + "Wrong cirrus,btn-det-init-dbnce DT value %d\n", + val); + cs42l42->btn_det_init_dbnce = + CS42L42_BTN_DET_INIT_DBNCE_DEFAULT; + } + } else { + cs42l42->btn_det_init_dbnce = + CS42L42_BTN_DET_INIT_DBNCE_DEFAULT; + } + + ret = of_property_read_u32(np, "cirrus,btn-det-event-dbnce", &val); + + if (!ret) { + if ((val >= CS42L42_BTN_DET_EVENT_DBNCE_MIN) && + (val <= CS42L42_BTN_DET_EVENT_DBNCE_MAX)) + cs42l42->btn_det_event_dbnce = val; + else { + dev_err(&i2c_client->dev, + "Wrong cirrus,btn-det-event-dbnce DT value %d\n", val); + cs42l42->btn_det_event_dbnce = + CS42L42_BTN_DET_EVENT_DBNCE_DEFAULT; + } + } else { + cs42l42->btn_det_event_dbnce = + CS42L42_BTN_DET_EVENT_DBNCE_DEFAULT; + } + + ret = of_property_read_u32_array(np, "cirrus,bias-lvls", + (u32 *)thresholds, CS42L42_NUM_BIASES); + + if (!ret) { + for (i = 0; i < CS42L42_NUM_BIASES; i++) { + if ((thresholds[i] >= CS42L42_HS_DET_LEVEL_MIN) && + (thresholds[i] <= CS42L42_HS_DET_LEVEL_MAX)) + cs42l42->bias_thresholds[i] = thresholds[i]; + else { + dev_err(&i2c_client->dev, + "Wrong cirrus,bias-lvls[%d] DT value %d\n", i, + thresholds[i]); + cs42l42->bias_thresholds[i] = + threshold_defaults[i]; + } + } + } else { + for (i = 0; i < CS42L42_NUM_BIASES; i++) + cs42l42->bias_thresholds[i] = threshold_defaults[i]; + } + + ret = of_property_read_u32(np, "cirrus,hs-bias-ramp-rate", &val); + + if (!ret) { + switch (val) { + case CS42L42_HSBIAS_RAMP_FAST_RISE_SLOW_FALL: + cs42l42->hs_bias_ramp_rate = val; + cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME0; + break; + case CS42L42_HSBIAS_RAMP_FAST: + cs42l42->hs_bias_ramp_rate = val; + cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME1; + break; + case CS42L42_HSBIAS_RAMP_SLOW: + cs42l42->hs_bias_ramp_rate = val; + cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME2; + break; + case CS42L42_HSBIAS_RAMP_SLOWEST: + cs42l42->hs_bias_ramp_rate = val; + cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME3; + break; + default: + dev_err(&i2c_client->dev, + "Wrong cirrus,hs-bias-ramp-rate DT value %d\n", + val); + cs42l42->hs_bias_ramp_rate = CS42L42_HSBIAS_RAMP_SLOW; + cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME2; + } + } else { + cs42l42->hs_bias_ramp_rate = CS42L42_HSBIAS_RAMP_SLOW; + cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME2; + } + + regmap_update_bits(cs42l42->regmap, CS42L42_HS_BIAS_CTL, + CS42L42_HSBIAS_RAMP_MASK, + (cs42l42->hs_bias_ramp_rate << + CS42L42_HSBIAS_RAMP_SHIFT)); + + return 0; +} + +static int cs42l42_i2c_probe(struct i2c_client *i2c_client, + const struct i2c_device_id *id) +{ + struct cs42l42_private *cs42l42; + int ret, i; + unsigned int devid = 0; + unsigned int reg; + + cs42l42 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42l42_private), + GFP_KERNEL); + if (!cs42l42) + return -ENOMEM; + + i2c_set_clientdata(i2c_client, cs42l42); + + cs42l42->regmap = devm_regmap_init_i2c(i2c_client, &cs42l42_regmap); + if (IS_ERR(cs42l42->regmap)) { + ret = PTR_ERR(cs42l42->regmap); + dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(cs42l42->supplies); i++) + cs42l42->supplies[i].supply = cs42l42_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c_client->dev, + ARRAY_SIZE(cs42l42->supplies), + cs42l42->supplies); + if (ret != 0) { + dev_err(&i2c_client->dev, + "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(cs42l42->supplies), + cs42l42->supplies); + if (ret != 0) { + dev_err(&i2c_client->dev, + "Failed to enable supplies: %d\n", ret); + return ret; + } + + /* Reset the Device */ + cs42l42->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev, + "reset", GPIOD_OUT_LOW); + if (IS_ERR(cs42l42->reset_gpio)) + return PTR_ERR(cs42l42->reset_gpio); + + if (cs42l42->reset_gpio) { + dev_dbg(&i2c_client->dev, "Found reset GPIO\n"); + gpiod_set_value_cansleep(cs42l42->reset_gpio, 1); + } + mdelay(3); + + /* Request IRQ */ + ret = devm_request_threaded_irq(&i2c_client->dev, + i2c_client->irq, + NULL, cs42l42_irq_thread, + IRQF_ONESHOT | IRQF_TRIGGER_LOW, + "cs42l42", cs42l42); + + if (ret != 0) + dev_err(&i2c_client->dev, + "Failed to request IRQ: %d\n", ret); + + /* initialize codec */ + ret = regmap_read(cs42l42->regmap, CS42L42_DEVID_AB, ®); + devid = (reg & 0xFF) << 12; + + ret = regmap_read(cs42l42->regmap, CS42L42_DEVID_CD, ®); + devid |= (reg & 0xFF) << 4; + + ret = regmap_read(cs42l42->regmap, CS42L42_DEVID_E, ®); + devid |= (reg & 0xF0) >> 4; + + if (devid != CS42L42_CHIP_ID) { + ret = -ENODEV; + dev_err(&i2c_client->dev, + "CS42L42 Device ID (%X). Expected %X\n", + devid, CS42L42_CHIP_ID); + return ret; + } + + ret = regmap_read(cs42l42->regmap, CS42L42_REVID, ®); + if (ret < 0) { + dev_err(&i2c_client->dev, "Get Revision ID failed\n"); + return ret; + } + + dev_info(&i2c_client->dev, + "Cirrus Logic CS42L42, Revision: %02X\n", reg & 0xFF); + + /* Power up the codec */ + regmap_update_bits(cs42l42->regmap, CS42L42_PWR_CTL1, + CS42L42_ASP_DAO_PDN_MASK | + CS42L42_ASP_DAI_PDN_MASK | + CS42L42_MIXER_PDN_MASK | + CS42L42_EQ_PDN_MASK | + CS42L42_HP_PDN_MASK | + CS42L42_ADC_PDN_MASK | + CS42L42_PDN_ALL_MASK, + (1 << CS42L42_ASP_DAO_PDN_SHIFT) | + (1 << CS42L42_ASP_DAI_PDN_SHIFT) | + (1 << CS42L42_MIXER_PDN_SHIFT) | + (1 << CS42L42_EQ_PDN_SHIFT) | + (1 << CS42L42_HP_PDN_SHIFT) | + (1 << CS42L42_ADC_PDN_SHIFT) | + (0 << CS42L42_PDN_ALL_SHIFT)); + + if (i2c_client->dev.of_node) { + ret = cs42l42_handle_device_data(i2c_client, cs42l42); + if (ret != 0) + return ret; + } + + /* Setup headset detection */ + cs42l42_setup_hs_type_detect(cs42l42); + + /* Mask/Unmask Interrupts */ + cs42l42_set_interrupt_masks(cs42l42); + + /* Register codec for machine driver */ + ret = snd_soc_register_codec(&i2c_client->dev, + &soc_codec_dev_cs42l42, &cs42l42_dai, 1); + if (ret < 0) + goto err_disable; + return 0; + +err_disable: + regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies), + cs42l42->supplies); + return ret; +} + +static int cs42l42_i2c_remove(struct i2c_client *i2c_client) +{ + struct cs42l42_private *cs42l42 = i2c_get_clientdata(i2c_client); + + snd_soc_unregister_codec(&i2c_client->dev); + + /* Hold down reset */ + if (cs42l42->reset_gpio) + gpiod_set_value_cansleep(cs42l42->reset_gpio, 0); + + return 0; +} + +#ifdef CONFIG_PM +static int cs42l42_runtime_suspend(struct device *dev) +{ + struct cs42l42_private *cs42l42 = dev_get_drvdata(dev); + + regcache_cache_only(cs42l42->regmap, true); + regcache_mark_dirty(cs42l42->regmap); + + /* Hold down reset */ + if (cs42l42->reset_gpio) + gpiod_set_value_cansleep(cs42l42->reset_gpio, 0); + + /* remove power */ + regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies), + cs42l42->supplies); + + return 0; +} + +static int cs42l42_runtime_resume(struct device *dev) +{ + struct cs42l42_private *cs42l42 = dev_get_drvdata(dev); + int ret; + + /* Enable power */ + ret = regulator_bulk_enable(ARRAY_SIZE(cs42l42->supplies), + cs42l42->supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable supplies: %d\n", + ret); + return ret; + } + + if (cs42l42->reset_gpio) + gpiod_set_value_cansleep(cs42l42->reset_gpio, 1); + + regcache_cache_only(cs42l42->regmap, false); + regcache_sync(cs42l42->regmap); + + return 0; +} +#endif + +static const struct dev_pm_ops cs42l42_runtime_pm = { + SET_RUNTIME_PM_OPS(cs42l42_runtime_suspend, cs42l42_runtime_resume, + NULL) +}; + +static const struct of_device_id cs42l42_of_match[] = { + { .compatible = "cirrus,cs42l42", }, + {}, +}; +MODULE_DEVICE_TABLE(of, cs42l42_of_match); + + +static const struct i2c_device_id cs42l42_id[] = { + {"cs42l42", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, cs42l42_id); + +static struct i2c_driver cs42l42_i2c_driver = { + .driver = { + .name = "cs42l42", + .pm = &cs42l42_runtime_pm, + .of_match_table = cs42l42_of_match, + }, + .id_table = cs42l42_id, + .probe = cs42l42_i2c_probe, + .remove = cs42l42_i2c_remove, +}; + +module_i2c_driver(cs42l42_i2c_driver); + +MODULE_DESCRIPTION("ASoC CS42L42 driver"); +MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>"); +MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>"); +MODULE_AUTHOR("Michael White, Cirrus Logic Inc, <michael.white@cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h new file mode 100644 index 000000000000..d87a0a5322d5 --- /dev/null +++ b/sound/soc/codecs/cs42l42.h @@ -0,0 +1,776 @@ +/* + * cs42l42.h -- CS42L42 ALSA SoC audio driver header + * + * Copyright 2016 Cirrus Logic, Inc. + * + * Author: James Schulman <james.schulman@cirrus.com> + * Author: Brian Austin <brian.austin@cirrus.com> + * Author: Michael White <michael.white@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 __CS42L42_H__ +#define __CS42L42_H__ + +#define CS42L42_PAGE_REGISTER 0x00 /* Page Select Register */ +#define CS42L42_WIN_START 0x00 +#define CS42L42_WIN_LEN 0x100 +#define CS42L42_RANGE_MIN 0x00 +#define CS42L42_RANGE_MAX 0x7F + +#define CS42L42_PAGE_10 0x1000 +#define CS42L42_PAGE_11 0x1100 +#define CS42L42_PAGE_12 0x1200 +#define CS42L42_PAGE_13 0x1300 +#define CS42L42_PAGE_15 0x1500 +#define CS42L42_PAGE_19 0x1900 +#define CS42L42_PAGE_1B 0x1B00 +#define CS42L42_PAGE_1C 0x1C00 +#define CS42L42_PAGE_1D 0x1D00 +#define CS42L42_PAGE_1F 0x1F00 +#define CS42L42_PAGE_20 0x2000 +#define CS42L42_PAGE_21 0x2100 +#define CS42L42_PAGE_23 0x2300 +#define CS42L42_PAGE_24 0x2400 +#define CS42L42_PAGE_25 0x2500 +#define CS42L42_PAGE_26 0x2600 +#define CS42L42_PAGE_28 0x2800 +#define CS42L42_PAGE_29 0x2900 +#define CS42L42_PAGE_2A 0x2A00 +#define CS42L42_PAGE_30 0x3000 + +#define CS42L42_CHIP_ID 0x42A42 + +/* Page 0x10 Global Registers */ +#define CS42L42_DEVID_AB (CS42L42_PAGE_10 + 0x01) +#define CS42L42_DEVID_CD (CS42L42_PAGE_10 + 0x02) +#define CS42L42_DEVID_E (CS42L42_PAGE_10 + 0x03) +#define CS42L42_FABID (CS42L42_PAGE_10 + 0x04) +#define CS42L42_REVID (CS42L42_PAGE_10 + 0x05) +#define CS42L42_FRZ_CTL (CS42L42_PAGE_10 + 0x06) + +#define CS42L42_SRC_CTL (CS42L42_PAGE_10 + 0x07) +#define CS42L42_SRC_BYPASS_DAC_SHIFT 1 +#define CS42L42_SRC_BYPASS_DAC_MASK (1 << CS42L42_SRC_BYPASS_DAC_SHIFT) + +#define CS42L42_MCLK_STATUS (CS42L42_PAGE_10 + 0x08) + +#define CS42L42_MCLK_CTL (CS42L42_PAGE_10 + 0x09) +#define CS42L42_INTERNAL_FS_SHIFT 1 +#define CS42L42_INTERNAL_FS_MASK (1 << CS42L42_INTERNAL_FS_SHIFT) + +#define CS42L42_SFTRAMP_RATE (CS42L42_PAGE_10 + 0x0A) +#define CS42L42_I2C_DEBOUNCE (CS42L42_PAGE_10 + 0x0E) +#define CS42L42_I2C_STRETCH (CS42L42_PAGE_10 + 0x0F) +#define CS42L42_I2C_TIMEOUT (CS42L42_PAGE_10 + 0x10) + +/* Page 0x11 Power and Headset Detect Registers */ +#define CS42L42_PWR_CTL1 (CS42L42_PAGE_11 + 0x01) +#define CS42L42_ASP_DAO_PDN_SHIFT 7 +#define CS42L42_ASP_DAO_PDN_MASK (1 << CS42L42_ASP_DAO_PDN_SHIFT) +#define CS42L42_ASP_DAI_PDN_SHIFT 6 +#define CS42L42_ASP_DAI_PDN_MASK (1 << CS42L42_ASP_DAI_PDN_SHIFT) +#define CS42L42_MIXER_PDN_SHIFT 5 +#define CS42L42_MIXER_PDN_MASK (1 << CS42L42_MIXER_PDN_SHIFT) +#define CS42L42_EQ_PDN_SHIFT 4 +#define CS42L42_EQ_PDN_MASK (1 << CS42L42_EQ_PDN_SHIFT) +#define CS42L42_HP_PDN_SHIFT 3 +#define CS42L42_HP_PDN_MASK (1 << CS42L42_HP_PDN_SHIFT) +#define CS42L42_ADC_PDN_SHIFT 2 +#define CS42L42_ADC_PDN_MASK (1 << CS42L42_HP_PDN_SHIFT) +#define CS42L42_PDN_ALL_SHIFT 0 +#define CS42L42_PDN_ALL_MASK (1 << CS42L42_PDN_ALL_SHIFT) + +#define CS42L42_PWR_CTL2 (CS42L42_PAGE_11 + 0x02) +#define CS42L42_ADC_SRC_PDNB_SHIFT 0 +#define CS42L42_ADC_SRC_PDNB_MASK (1 << CS42L42_ADC_SRC_PDNB_SHIFT) +#define CS42L42_DAC_SRC_PDNB_SHIFT 1 +#define CS42L42_DAC_SRC_PDNB_MASK (1 << CS42L42_DAC_SRC_PDNB_SHIFT) +#define CS42L42_ASP_DAI1_PDN_SHIFT 2 +#define CS42L42_ASP_DAI1_PDN_MASK (1 << CS42L42_ASP_DAI1_PDN_SHIFT) +#define CS42L42_SRC_PDN_OVERRIDE_SHIFT 3 +#define CS42L42_SRC_PDN_OVERRIDE_MASK (1 << CS42L42_SRC_PDN_OVERRIDE_SHIFT) +#define CS42L42_DISCHARGE_FILT_SHIFT 4 +#define CS42L42_DISCHARGE_FILT_MASK (1 << CS42L42_DISCHARGE_FILT_SHIFT) + +#define CS42L42_PWR_CTL3 (CS42L42_PAGE_11 + 0x03) +#define CS42L42_RING_SENSE_PDNB_SHIFT 1 +#define CS42L42_RING_SENSE_PDNB_MASK (1 << \ + CS42L42_RING_SENSE_PDNB_SHIFT) +#define CS42L42_VPMON_PDNB_SHIFT 2 +#define CS42L42_VPMON_PDNB_MASK (1 << \ + CS42L42_VPMON_PDNB_SHIFT) +#define CS42L42_SW_CLK_STP_STAT_SEL_SHIFT 5 +#define CS42L42_SW_CLK_STP_STAT_SEL_MASK (3 << \ + CS42L42_SW_CLK_STP_STAT_SEL_SHIFT) + +#define CS42L42_RSENSE_CTL1 (CS42L42_PAGE_11 + 0x04) +#define CS42L42_RS_TRIM_R_SHIFT 0 +#define CS42L42_RS_TRIM_R_MASK (1 << \ + CS42L42_RS_TRIM_R_SHIFT) +#define CS42L42_RS_TRIM_T_SHIFT 1 +#define CS42L42_RS_TRIM_T_MASK (1 << \ + CS42L42_RS_TRIM_T_SHIFT) +#define CS42L42_HPREF_RS_SHIFT 2 +#define CS42L42_HPREF_RS_MASK (1 << \ + CS42L42_HPREF_RS_SHIFT) +#define CS42L42_HSBIAS_FILT_REF_RS_SHIFT 3 +#define CS42L42_HSBIAS_FILT_REF_RS_MASK (1 << \ + CS42L42_HSBIAS_FILT_REF_RS_SHIFT) +#define CS42L42_RING_SENSE_PU_HIZ_SHIFT 6 +#define CS42L42_RING_SENSE_PU_HIZ_MASK (1 << \ + CS42L42_RING_SENSE_PU_HIZ_SHIFT) + +#define CS42L42_RSENSE_CTL2 (CS42L42_PAGE_11 + 0x05) +#define CS42L42_TS_RS_GATE_SHIFT 7 +#define CS42L42_TS_RS_GATE_MAS (1 << CS42L42_TS_RS_GATE_SHIFT) + +#define CS42L42_OSC_SWITCH (CS42L42_PAGE_11 + 0x07) +#define CS42L42_SCLK_PRESENT_SHIFT 0 +#define CS42L42_SCLK_PRESENT_MASK (1 << CS42L42_SCLK_PRESENT_SHIFT) + +#define CS42L42_OSC_SWITCH_STATUS (CS42L42_PAGE_11 + 0x09) +#define CS42L42_OSC_SW_SEL_STAT_SHIFT 0 +#define CS42L42_OSC_SW_SEL_STAT_MASK (3 << CS42L42_OSC_SW_SEL_STAT_SHIFT) +#define CS42L42_OSC_PDNB_STAT_SHIFT 2 +#define CS42L42_OSC_PDNB_STAT_MASK (1 << CS42L42_OSC_SW_SEL_STAT_SHIFT) + +#define CS42L42_RSENSE_CTL3 (CS42L42_PAGE_11 + 0x12) +#define CS42L42_RS_RISE_DBNCE_TIME_SHIFT 0 +#define CS42L42_RS_RISE_DBNCE_TIME_MASK (7 << \ + CS42L42_RS_RISE_DBNCE_TIME_SHIFT) +#define CS42L42_RS_FALL_DBNCE_TIME_SHIFT 3 +#define CS42L42_RS_FALL_DBNCE_TIME_MASK (7 << \ + CS42L42_RS_FALL_DBNCE_TIME_SHIFT) +#define CS42L42_RS_PU_EN_SHIFT 6 +#define CS42L42_RS_PU_EN_MASK (1 << \ + CS42L42_RS_PU_EN_SHIFT) +#define CS42L42_RS_INV_SHIFT 7 +#define CS42L42_RS_INV_MASK (1 << \ + CS42L42_RS_INV_SHIFT) + +#define CS42L42_TSENSE_CTL (CS42L42_PAGE_11 + 0x13) +#define CS42L42_TS_RISE_DBNCE_TIME_SHIFT 0 +#define CS42L42_TS_RISE_DBNCE_TIME_MASK (7 << \ + CS42L42_TS_RISE_DBNCE_TIME_SHIFT) +#define CS42L42_TS_FALL_DBNCE_TIME_SHIFT 3 +#define CS42L42_TS_FALL_DBNCE_TIME_MASK (7 << \ + CS42L42_TS_FALL_DBNCE_TIME_SHIFT) +#define CS42L42_TS_INV_SHIFT 7 +#define CS42L42_TS_INV_MASK (1 << \ + CS42L42_TS_INV_SHIFT) + +#define CS42L42_TSRS_INT_DISABLE (CS42L42_PAGE_11 + 0x14) +#define CS42L42_D_RS_PLUG_DBNC_SHIFT 0 +#define CS42L42_D_RS_PLUG_DBNC_MASK (1 << CS42L42_D_RS_PLUG_DBNC_SHIFT) +#define CS42L42_D_RS_UNPLUG_DBNC_SHIFT 1 +#define CS42L42_D_RS_UNPLUG_DBNC_MASK (1 << CS42L42_D_RS_UNPLUG_DBNC_SHIFT) +#define CS42L42_D_TS_PLUG_DBNC_SHIFT 2 +#define CS42L42_D_TS_PLUG_DBNC_MASK (1 << CS42L42_D_TS_PLUG_DBNC_SHIFT) +#define CS42L42_D_TS_UNPLUG_DBNC_SHIFT 3 +#define CS42L42_D_TS_UNPLUG_DBNC_MASK (1 << CS42L42_D_TS_UNPLUG_DBNC_SHIFT) + +#define CS42L42_TRSENSE_STATUS (CS42L42_PAGE_11 + 0x15) +#define CS42L42_RS_PLUG_DBNC_SHIFT 0 +#define CS42L42_RS_PLUG_DBNC_MASK (1 << CS42L42_RS_PLUG_DBNC_SHIFT) +#define CS42L42_RS_UNPLUG_DBNC_SHIFT 1 +#define CS42L42_RS_UNPLUG_DBNC_MASK (1 << CS42L42_RS_UNPLUG_DBNC_SHIFT) +#define CS42L42_TS_PLUG_DBNC_SHIFT 2 +#define CS42L42_TS_PLUG_DBNC_MASK (1 << CS42L42_TS_PLUG_DBNC_SHIFT) +#define CS42L42_TS_UNPLUG_DBNC_SHIFT 3 +#define CS42L42_TS_UNPLUG_DBNC_MASK (1 << CS42L42_TS_UNPLUG_DBNC_SHIFT) + +#define CS42L42_HSDET_CTL1 (CS42L42_PAGE_11 + 0x1F) +#define CS42L42_HSDET_COMP1_LVL_SHIFT 0 +#define CS42L42_HSDET_COMP1_LVL_MASK (15 << CS42L42_HSDET_COMP1_LVL_SHIFT) +#define CS42L42_HSDET_COMP2_LVL_SHIFT 4 +#define CS42L42_HSDET_COMP2_LVL_MASK (15 << CS42L42_HSDET_COMP2_LVL_SHIFT) + +#define CS42L42_HSDET_CTL2 (CS42L42_PAGE_11 + 0x20) +#define CS42L42_HSDET_AUTO_TIME_SHIFT 0 +#define CS42L42_HSDET_AUTO_TIME_MASK (3 << CS42L42_HSDET_AUTO_TIME_SHIFT) +#define CS42L42_HSBIAS_REF_SHIFT 3 +#define CS42L42_HSBIAS_REF_MASK (1 << CS42L42_HSBIAS_REF_SHIFT) +#define CS42L42_HSDET_SET_SHIFT 4 +#define CS42L42_HSDET_SET_MASK (3 << CS42L42_HSDET_SET_SHIFT) +#define CS42L42_HSDET_CTRL_SHIFT 6 +#define CS42L42_HSDET_CTRL_MASK (3 << CS42L42_HSDET_CTRL_SHIFT) + +#define CS42L42_HS_SWITCH_CTL (CS42L42_PAGE_11 + 0x21) +#define CS42L42_SW_GNDHS_HS4_SHIFT 0 +#define CS42L42_SW_GNDHS_HS4_MASK (1 << CS42L42_SW_GNDHS_HS4_SHIFT) +#define CS42L42_SW_GNDHS_HS3_SHIFT 1 +#define CS42L42_SW_GNDHS_HS3_MASK (1 << CS42L42_SW_GNDHS_HS3_SHIFT) +#define CS42L42_SW_HSB_HS4_SHIFT 2 +#define CS42L42_SW_HSB_HS4_MASK (1 << CS42L42_SW_HSB_HS4_SHIFT) +#define CS42L42_SW_HSB_HS3_SHIFT 3 +#define CS42L42_SW_HSB_HS3_MASK (1 << CS42L42_SW_HSB_HS3_SHIFT) +#define CS42L42_SW_HSB_FILT_HS4_SHIFT 4 +#define CS42L42_SW_HSB_FILT_HS4_MASK (1 << CS42L42_SW_HSB_FILT_HS4_SHIFT) +#define CS42L42_SW_HSB_FILT_HS3_SHIFT 5 +#define CS42L42_SW_HSB_FILT_HS3_MASK (1 << CS42L42_SW_HSB_FILT_HS3_SHIFT) +#define CS42L42_SW_REF_HS4_SHIFT 6 +#define CS42L42_SW_REF_HS4_MASK (1 << CS42L42_SW_REF_HS4_SHIFT) +#define CS42L42_SW_REF_HS3_SHIFT 7 +#define CS42L42_SW_REF_HS3_MASK (1 << CS42L42_SW_REF_HS3_SHIFT) + +#define CS42L42_HS_DET_STATUS (CS42L42_PAGE_11 + 0x24) +#define CS42L42_HSDET_TYPE_SHIFT 0 +#define CS42L42_HSDET_TYPE_MASK (3 << CS42L42_HSDET_TYPE_SHIFT) +#define CS42L42_HSDET_COMP1_OUT_SHIFT 6 +#define CS42L42_HSDET_COMP1_OUT_MASK (1 << CS42L42_HSDET_COMP1_OUT_SHIFT) +#define CS42L42_HSDET_COMP2_OUT_SHIFT 7 +#define CS42L42_HSDET_COMP2_OUT_MASK (1 << CS42L42_HSDET_COMP2_OUT_SHIFT) +#define CS42L42_PLUG_CTIA 0 +#define CS42L42_PLUG_OMTP 1 +#define CS42L42_PLUG_HEADPHONE 2 +#define CS42L42_PLUG_INVALID 3 + +#define CS42L42_HS_CLAMP_DISABLE (CS42L42_PAGE_11 + 0x29) +#define CS42L42_HS_CLAMP_DISABLE_SHIFT 0 +#define CS42L42_HS_CLAMP_DISABLE_MASK (1 << CS42L42_HS_CLAMP_DISABLE_SHIFT) + +/* Page 0x12 Clocking Registers */ +#define CS42L42_MCLK_SRC_SEL (CS42L42_PAGE_12 + 0x01) +#define CS42L42_MCLKDIV_SHIFT 1 +#define CS42L42_MCLKDIV_MASK (1 << CS42L42_MCLKDIV_SHIFT) +#define CS42L42_MCLK_SRC_SEL_SHIFT 0 +#define CS42L42_MCLK_SRC_SEL_MASK (1 << CS42L42_MCLK_SRC_SEL_SHIFT) + +#define CS42L42_SPDIF_CLK_CFG (CS42L42_PAGE_12 + 0x02) +#define CS42L42_FSYNC_PW_LOWER (CS42L42_PAGE_12 + 0x03) + +#define CS42L42_FSYNC_PW_UPPER (CS42L42_PAGE_12 + 0x04) +#define CS42L42_FSYNC_PULSE_WIDTH_SHIFT 0 +#define CS42L42_FSYNC_PULSE_WIDTH_MASK (0xff << \ + CS42L42_FSYNC_PULSE_WIDTH_SHIFT) + +#define CS42L42_FSYNC_P_LOWER (CS42L42_PAGE_12 + 0x05) + +#define CS42L42_FSYNC_P_UPPER (CS42L42_PAGE_12 + 0x06) +#define CS42L42_FSYNC_PERIOD_SHIFT 0 +#define CS42L42_FSYNC_PERIOD_MASK (0xff << CS42L42_FSYNC_PERIOD_SHIFT) + +#define CS42L42_ASP_CLK_CFG (CS42L42_PAGE_12 + 0x07) +#define CS42L42_ASP_SCLK_EN_SHIFT 5 +#define CS42L42_ASP_SCLK_EN_MASK (1 << CS42L42_ASP_SCLK_EN_SHIFT) +#define CS42L42_ASP_MASTER_MODE 0x01 +#define CS42L42_ASP_SLAVE_MODE 0x00 +#define CS42L42_ASP_MODE_SHIFT 4 +#define CS42L42_ASP_MODE_MASK (1 << CS42L42_ASP_MODE_SHIFT) +#define CS42L42_ASP_SCPOL_IN_DAC_SHIFT 2 +#define CS42L42_ASP_SCPOL_IN_DAC_MASK (1 << CS42L42_ASP_SCPOL_IN_DAC_SHIFT) +#define CS42L42_ASP_LCPOL_IN_SHIFT 0 +#define CS42L42_ASP_LCPOL_IN_MASK (1 << CS42L42_ASP_LCPOL_IN_SHIFT) +#define CS42L42_ASP_POL_INV 1 + +#define CS42L42_ASP_FRM_CFG (CS42L42_PAGE_12 + 0x08) +#define CS42L42_ASP_STP_SHIFT 4 +#define CS42L42_ASP_STP_MASK (1 << CS42L42_ASP_STP_SHIFT) +#define CS42L42_ASP_5050_SHIFT 3 +#define CS42L42_ASP_5050_MASK (1 << CS42L42_ASP_5050_SHIFT) +#define CS42L42_ASP_FSD_SHIFT 0 +#define CS42L42_ASP_FSD_MASK (7 << CS42L42_ASP_FSD_SHIFT) +#define CS42L42_ASP_FSD_0_5 1 +#define CS42L42_ASP_FSD_1_0 2 +#define CS42L42_ASP_FSD_1_5 3 +#define CS42L42_ASP_FSD_2_0 4 + +#define CS42L42_FS_RATE_EN (CS42L42_PAGE_12 + 0x09) +#define CS42L42_FS_EN_SHIFT 0 +#define CS42L42_FS_EN_MASK (0xf << CS42L42_FS_EN_SHIFT) +#define CS42L42_FS_EN_IASRC_96K 0x1 +#define CS42L42_FS_EN_OASRC_96K 0x2 + +#define CS42L42_IN_ASRC_CLK (CS42L42_PAGE_12 + 0x0A) +#define CS42L42_CLK_IASRC_SEL_SHIFT 0 +#define CS42L42_CLK_IASRC_SEL_MASK (1 << CS42L42_CLK_IASRC_SEL_SHIFT) +#define CS42L42_CLK_IASRC_SEL_12 1 + +#define CS42L42_OUT_ASRC_CLK (CS42L42_PAGE_12 + 0x0B) +#define CS42L42_CLK_OASRC_SEL_SHIFT 0 +#define CS42L42_CLK_OASRC_SEL_MASK (1 << CS42L42_CLK_OASRC_SEL_SHIFT) +#define CS42L42_CLK_OASRC_SEL_12 1 + +#define CS42L42_PLL_DIV_CFG1 (CS42L42_PAGE_12 + 0x0C) +#define CS42L42_SCLK_PREDIV_SHIFT 0 +#define CS42L42_SCLK_PREDIV_MASK (3 << CS42L42_SCLK_PREDIV_SHIFT) + +/* Page 0x13 Interrupt Registers */ +/* Interrupts */ +#define CS42L42_ADC_OVFL_STATUS (CS42L42_PAGE_13 + 0x01) +#define CS42L42_MIXER_STATUS (CS42L42_PAGE_13 + 0x02) +#define CS42L42_SRC_STATUS (CS42L42_PAGE_13 + 0x03) +#define CS42L42_ASP_RX_STATUS (CS42L42_PAGE_13 + 0x04) +#define CS42L42_ASP_TX_STATUS (CS42L42_PAGE_13 + 0x05) +#define CS42L42_CODEC_STATUS (CS42L42_PAGE_13 + 0x08) +#define CS42L42_DET_INT_STATUS1 (CS42L42_PAGE_13 + 0x09) +#define CS42L42_DET_INT_STATUS2 (CS42L42_PAGE_13 + 0x0A) +#define CS42L42_SRCPL_INT_STATUS (CS42L42_PAGE_13 + 0x0B) +#define CS42L42_VPMON_STATUS (CS42L42_PAGE_13 + 0x0D) +#define CS42L42_PLL_LOCK_STATUS (CS42L42_PAGE_13 + 0x0E) +#define CS42L42_TSRS_PLUG_STATUS (CS42L42_PAGE_13 + 0x0F) +/* Masks */ +#define CS42L42_ADC_OVFL_INT_MASK (CS42L42_PAGE_13 + 0x16) +#define CS42L42_ADC_OVFL_SHIFT 0 +#define CS42L42_ADC_OVFL_MASK (1 << CS42L42_ADC_OVFL_SHIFT) +#define CS42L42_ADC_OVFL_VAL_MASK CS42L42_ADC_OVFL_MASK + +#define CS42L42_MIXER_INT_MASK (CS42L42_PAGE_13 + 0x17) +#define CS42L42_MIX_CHB_OVFL_SHIFT 0 +#define CS42L42_MIX_CHB_OVFL_MASK (1 << CS42L42_MIX_CHB_OVFL_SHIFT) +#define CS42L42_MIX_CHA_OVFL_SHIFT 1 +#define CS42L42_MIX_CHA_OVFL_MASK (1 << CS42L42_MIX_CHA_OVFL_SHIFT) +#define CS42L42_EQ_OVFL_SHIFT 2 +#define CS42L42_EQ_OVFL_MASK (1 << CS42L42_EQ_OVFL_SHIFT) +#define CS42L42_EQ_BIQUAD_OVFL_SHIFT 3 +#define CS42L42_EQ_BIQUAD_OVFL_MASK (1 << CS42L42_EQ_BIQUAD_OVFL_SHIFT) +#define CS42L42_MIXER_VAL_MASK (CS42L42_MIX_CHB_OVFL_MASK | \ + CS42L42_MIX_CHA_OVFL_MASK | \ + CS42L42_EQ_OVFL_MASK | \ + CS42L42_EQ_BIQUAD_OVFL_MASK) + +#define CS42L42_SRC_INT_MASK (CS42L42_PAGE_13 + 0x18) +#define CS42L42_SRC_ILK_SHIFT 0 +#define CS42L42_SRC_ILK_MASK (1 << CS42L42_SRC_ILK_SHIFT) +#define CS42L42_SRC_OLK_SHIFT 1 +#define CS42L42_SRC_OLK_MASK (1 << CS42L42_SRC_OLK_SHIFT) +#define CS42L42_SRC_IUNLK_SHIFT 2 +#define CS42L42_SRC_IUNLK_MASK (1 << CS42L42_SRC_IUNLK_SHIFT) +#define CS42L42_SRC_OUNLK_SHIFT 3 +#define CS42L42_SRC_OUNLK_MASK (1 << CS42L42_SRC_OUNLK_SHIFT) +#define CS42L42_SRC_VAL_MASK (CS42L42_SRC_ILK_MASK | \ + CS42L42_SRC_OLK_MASK | \ + CS42L42_SRC_IUNLK_MASK | \ + CS42L42_SRC_OUNLK_MASK) + +#define CS42L42_ASP_RX_INT_MASK (CS42L42_PAGE_13 + 0x19) +#define CS42L42_ASPRX_NOLRCK_SHIFT 0 +#define CS42L42_ASPRX_NOLRCK_MASK (1 << CS42L42_ASPRX_NOLRCK_SHIFT) +#define CS42L42_ASPRX_EARLY_SHIFT 1 +#define CS42L42_ASPRX_EARLY_MASK (1 << CS42L42_ASPRX_EARLY_SHIFT) +#define CS42L42_ASPRX_LATE_SHIFT 2 +#define CS42L42_ASPRX_LATE_MASK (1 << CS42L42_ASPRX_LATE_SHIFT) +#define CS42L42_ASPRX_ERROR_SHIFT 3 +#define CS42L42_ASPRX_ERROR_MASK (1 << CS42L42_ASPRX_ERROR_SHIFT) +#define CS42L42_ASPRX_OVLD_SHIFT 4 +#define CS42L42_ASPRX_OVLD_MASK (1 << CS42L42_ASPRX_OVLD_SHIFT) +#define CS42L42_ASP_RX_VAL_MASK (CS42L42_ASPRX_NOLRCK_MASK | \ + CS42L42_ASPRX_EARLY_MASK | \ + CS42L42_ASPRX_LATE_MASK | \ + CS42L42_ASPRX_ERROR_MASK | \ + CS42L42_ASPRX_OVLD_MASK) + +#define CS42L42_ASP_TX_INT_MASK (CS42L42_PAGE_13 + 0x1A) +#define CS42L42_ASPTX_NOLRCK_SHIFT 0 +#define CS42L42_ASPTX_NOLRCK_MASK (1 << CS42L42_ASPTX_NOLRCK_SHIFT) +#define CS42L42_ASPTX_EARLY_SHIFT 1 +#define CS42L42_ASPTX_EARLY_MASK (1 << CS42L42_ASPTX_EARLY_SHIFT) +#define CS42L42_ASPTX_LATE_SHIFT 2 +#define CS42L42_ASPTX_LATE_MASK (1 << CS42L42_ASPTX_LATE_SHIFT) +#define CS42L42_ASPTX_SMERROR_SHIFT 3 +#define CS42L42_ASPTX_SMERROR_MASK (1 << CS42L42_ASPTX_SMERROR_SHIFT) +#define CS42L42_ASP_TX_VAL_MASK (CS42L42_ASPTX_NOLRCK_MASK | \ + CS42L42_ASPTX_EARLY_MASK | \ + CS42L42_ASPTX_LATE_MASK | \ + CS42L42_ASPTX_SMERROR_MASK) + +#define CS42L42_CODEC_INT_MASK (CS42L42_PAGE_13 + 0x1B) +#define CS42L42_PDN_DONE_SHIFT 0 +#define CS42L42_PDN_DONE_MASK (1 << CS42L42_PDN_DONE_SHIFT) +#define CS42L42_HSDET_AUTO_DONE_SHIFT 1 +#define CS42L42_HSDET_AUTO_DONE_MASK (1 << CS42L42_HSDET_AUTO_DONE_SHIFT) +#define CS42L42_CODEC_VAL_MASK (CS42L42_PDN_DONE_MASK | \ + CS42L42_HSDET_AUTO_DONE_MASK) + +#define CS42L42_SRCPL_INT_MASK (CS42L42_PAGE_13 + 0x1C) +#define CS42L42_SRCPL_ADC_LK_SHIFT 0 +#define CS42L42_SRCPL_ADC_LK_MASK (1 << CS42L42_SRCPL_ADC_LK_SHIFT) +#define CS42L42_SRCPL_DAC_LK_SHIFT 2 +#define CS42L42_SRCPL_DAC_LK_MASK (1 << CS42L42_SRCPL_DAC_LK_SHIFT) +#define CS42L42_SRCPL_ADC_UNLK_SHIFT 5 +#define CS42L42_SRCPL_ADC_UNLK_MASK (1 << CS42L42_SRCPL_ADC_UNLK_SHIFT) +#define CS42L42_SRCPL_DAC_UNLK_SHIFT 6 +#define CS42L42_SRCPL_DAC_UNLK_MASK (1 << CS42L42_SRCPL_DAC_UNLK_SHIFT) +#define CS42L42_SRCPL_VAL_MASK (CS42L42_SRCPL_ADC_LK_MASK | \ + CS42L42_SRCPL_DAC_LK_MASK | \ + CS42L42_SRCPL_ADC_UNLK_MASK | \ + CS42L42_SRCPL_DAC_UNLK_MASK) + +#define CS42L42_VPMON_INT_MASK (CS42L42_PAGE_13 + 0x1E) +#define CS42L42_VPMON_SHIFT 0 +#define CS42L42_VPMON_MASK (1 << CS42L42_VPMON_SHIFT) +#define CS42L42_VPMON_VAL_MASK CS42L42_VPMON_MASK + +#define CS42L42_PLL_LOCK_INT_MASK (CS42L42_PAGE_13 + 0x1F) +#define CS42L42_PLL_LOCK_SHIFT 0 +#define CS42L42_PLL_LOCK_MASK (1 << CS42L42_PLL_LOCK_SHIFT) +#define CS42L42_PLL_LOCK_VAL_MASK CS42L42_PLL_LOCK_MASK + +#define CS42L42_TSRS_PLUG_INT_MASK (CS42L42_PAGE_13 + 0x20) +#define CS42L42_RS_PLUG_SHIFT 0 +#define CS42L42_RS_PLUG_MASK (1 << CS42L42_RS_PLUG_SHIFT) +#define CS42L42_RS_UNPLUG_SHIFT 1 +#define CS42L42_RS_UNPLUG_MASK (1 << CS42L42_RS_UNPLUG_SHIFT) +#define CS42L42_TS_PLUG_SHIFT 2 +#define CS42L42_TS_PLUG_MASK (1 << CS42L42_TS_PLUG_SHIFT) +#define CS42L42_TS_UNPLUG_SHIFT 3 +#define CS42L42_TS_UNPLUG_MASK (1 << CS42L42_TS_UNPLUG_SHIFT) +#define CS42L42_TSRS_PLUG_VAL_MASK (CS42L42_RS_PLUG_MASK | \ + CS42L42_RS_UNPLUG_MASK | \ + CS42L42_TS_PLUG_MASK | \ + CS42L42_TS_UNPLUG_MASK) +#define CS42L42_TS_PLUG 3 +#define CS42L42_TS_UNPLUG 0 +#define CS42L42_TS_TRANS 1 + +/* Page 0x15 Fractional-N PLL Registers */ +#define CS42L42_PLL_CTL1 (CS42L42_PAGE_15 + 0x01) +#define CS42L42_PLL_START_SHIFT 0 +#define CS42L42_PLL_START_MASK (1 << CS42L42_PLL_START_SHIFT) + +#define CS42L42_PLL_DIV_FRAC0 (CS42L42_PAGE_15 + 0x02) +#define CS42L42_PLL_DIV_FRAC_SHIFT 0 +#define CS42L42_PLL_DIV_FRAC_MASK (0xff << CS42L42_PLL_DIV_FRAC_SHIFT) + +#define CS42L42_PLL_DIV_FRAC1 (CS42L42_PAGE_15 + 0x03) +#define CS42L42_PLL_DIV_FRAC2 (CS42L42_PAGE_15 + 0x04) + +#define CS42L42_PLL_DIV_INT (CS42L42_PAGE_15 + 0x05) +#define CS42L42_PLL_DIV_INT_SHIFT 0 +#define CS42L42_PLL_DIV_INT_MASK (0xff << CS42L42_PLL_DIV_INT_SHIFT) + +#define CS42L42_PLL_CTL3 (CS42L42_PAGE_15 + 0x08) +#define CS42L42_PLL_DIVOUT_SHIFT 0 +#define CS42L42_PLL_DIVOUT_MASK (0xff << CS42L42_PLL_DIVOUT_SHIFT) + +#define CS42L42_PLL_CAL_RATIO (CS42L42_PAGE_15 + 0x0A) +#define CS42L42_PLL_CAL_RATIO_SHIFT 0 +#define CS42L42_PLL_CAL_RATIO_MASK (0xff << CS42L42_PLL_CAL_RATIO_SHIFT) + +#define CS42L42_PLL_CTL4 (CS42L42_PAGE_15 + 0x1B) +#define CS42L42_PLL_MODE_SHIFT 0 +#define CS42L42_PLL_MODE_MASK (3 << CS42L42_PLL_MODE_SHIFT) + +/* Page 0x19 HP Load Detect Registers */ +#define CS42L42_LOAD_DET_RCSTAT (CS42L42_PAGE_19 + 0x25) +#define CS42L42_RLA_STAT_SHIFT 0 +#define CS42L42_RLA_STAT_MASK (3 << CS42L42_RLA_STAT_SHIFT) +#define CS42L42_RLA_STAT_15_OHM 0 + +#define CS42L42_LOAD_DET_DONE (CS42L42_PAGE_19 + 0x26) +#define CS42L42_HPLOAD_DET_DONE_SHIFT 0 +#define CS42L42_HPLOAD_DET_DONE_MASK (1 << CS42L42_HPLOAD_DET_DONE_SHIFT) + +#define CS42L42_LOAD_DET_EN (CS42L42_PAGE_19 + 0x27) +#define CS42L42_HP_LD_EN_SHIFT 0 +#define CS42L42_HP_LD_EN_MASK (1 << CS42L42_HP_LD_EN_SHIFT) + +/* Page 0x1B Headset Interface Registers */ +#define CS42L42_HSBIAS_SC_AUTOCTL (CS42L42_PAGE_1B + 0x70) +#define CS42L42_HSBIAS_SENSE_TRIP_SHIFT 0 +#define CS42L42_HSBIAS_SENSE_TRIP_MASK (7 << \ + CS42L42_HSBIAS_SENSE_TRIP_SHIFT) +#define CS42L42_TIP_SENSE_EN_SHIFT 5 +#define CS42L42_TIP_SENSE_EN_MASK (1 << \ + CS42L42_TIP_SENSE_EN_SHIFT) +#define CS42L42_AUTO_HSBIAS_HIZ_SHIFT 6 +#define CS42L42_AUTO_HSBIAS_HIZ_MASK (1 << \ + CS42L42_AUTO_HSBIAS_HIZ_SHIFT) +#define CS42L42_HSBIAS_SENSE_EN_SHIFT 7 +#define CS42L42_HSBIAS_SENSE_EN_MASK (1 << \ + CS42L42_HSBIAS_SENSE_EN_SHIFT) + +#define CS42L42_WAKE_CTL (CS42L42_PAGE_1B + 0x71) +#define CS42L42_WAKEB_CLEAR_SHIFT 0 +#define CS42L42_WAKEB_CLEAR_MASK (1 << CS42L42_WAKEB_CLEAR_SHIFT) +#define CS42L42_WAKEB_MODE_SHIFT 5 +#define CS42L42_WAKEB_MODE_MASK (1 << CS42L42_WAKEB_MODE_SHIFT) +#define CS42L42_M_HP_WAKE_SHIFT 6 +#define CS42L42_M_HP_WAKE_MASK (1 << CS42L42_M_HP_WAKE_SHIFT) +#define CS42L42_M_MIC_WAKE_SHIFT 7 +#define CS42L42_M_MIC_WAKE_MASK (1 << CS42L42_M_MIC_WAKE_SHIFT) + +#define CS42L42_ADC_DISABLE_MUTE (CS42L42_PAGE_1B + 0x72) +#define CS42L42_ADC_DISABLE_S0_MUTE_SHIFT 7 +#define CS42L42_ADC_DISABLE_S0_MUTE_MASK (1 << \ + CS42L42_ADC_DISABLE_S0_MUTE_SHIFT) + +#define CS42L42_TIPSENSE_CTL (CS42L42_PAGE_1B + 0x73) +#define CS42L42_TIP_SENSE_DEBOUNCE_SHIFT 0 +#define CS42L42_TIP_SENSE_DEBOUNCE_MASK (3 << \ + CS42L42_TIP_SENSE_DEBOUNCE_SHIFT) +#define CS42L42_TIP_SENSE_INV_SHIFT 5 +#define CS42L42_TIP_SENSE_INV_MASK (1 << \ + CS42L42_TIP_SENSE_INV_SHIFT) +#define CS42L42_TIP_SENSE_CTRL_SHIFT 6 +#define CS42L42_TIP_SENSE_CTRL_MASK (3 << \ + CS42L42_TIP_SENSE_CTRL_SHIFT) + +#define CS42L42_MISC_DET_CTL (CS42L42_PAGE_1B + 0x74) +#define CS42L42_PDN_MIC_LVL_DET_SHIFT 0 +#define CS42L42_PDN_MIC_LVL_DET_MASK (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT) +#define CS42L42_HSBIAS_CTL_SHIFT 1 +#define CS42L42_HSBIAS_CTL_MASK (3 << CS42L42_HSBIAS_CTL_SHIFT) +#define CS42L42_DETECT_MODE_SHIFT 3 +#define CS42L42_DETECT_MODE_MASK (3 << CS42L42_DETECT_MODE_SHIFT) + +#define CS42L42_MIC_DET_CTL1 (CS42L42_PAGE_1B + 0x75) +#define CS42L42_HS_DET_LEVEL_SHIFT 0 +#define CS42L42_HS_DET_LEVEL_MASK (0x3F << CS42L42_HS_DET_LEVEL_SHIFT) +#define CS42L42_EVENT_STAT_SEL_SHIFT 6 +#define CS42L42_EVENT_STAT_SEL_MASK (1 << CS42L42_EVENT_STAT_SEL_SHIFT) +#define CS42L42_LATCH_TO_VP_SHIFT 7 +#define CS42L42_LATCH_TO_VP_MASK (1 << CS42L42_LATCH_TO_VP_SHIFT) + +#define CS42L42_MIC_DET_CTL2 (CS42L42_PAGE_1B + 0x76) +#define CS42L42_DEBOUNCE_TIME_SHIFT 5 +#define CS42L42_DEBOUNCE_TIME_MASK (0x07 << CS42L42_DEBOUNCE_TIME_SHIFT) + +#define CS42L42_DET_STATUS1 (CS42L42_PAGE_1B + 0x77) +#define CS42L42_HSBIAS_HIZ_MODE_SHIFT 6 +#define CS42L42_HSBIAS_HIZ_MODE_MASK (1 << CS42L42_HSBIAS_HIZ_MODE_SHIFT) +#define CS42L42_TIP_SENSE_SHIFT 7 +#define CS42L42_TIP_SENSE_MASK (1 << CS42L42_TIP_SENSE_SHIFT) + +#define CS42L42_DET_STATUS2 (CS42L42_PAGE_1B + 0x78) +#define CS42L42_SHORT_TRUE_SHIFT 0 +#define CS42L42_SHORT_TRUE_MASK (1 << CS42L42_SHORT_TRUE_SHIFT) +#define CS42L42_HS_TRUE_SHIFT 1 +#define CS42L42_HS_TRUE_MASK (1 << CS42L42_HS_TRUE_SHIFT) + +#define CS42L42_DET_INT1_MASK (CS42L42_PAGE_1B + 0x79) +#define CS42L42_TIP_SENSE_UNPLUG_SHIFT 5 +#define CS42L42_TIP_SENSE_UNPLUG_MASK (1 << CS42L42_TIP_SENSE_UNPLUG_SHIFT) +#define CS42L42_TIP_SENSE_PLUG_SHIFT 6 +#define CS42L42_TIP_SENSE_PLUG_MASK (1 << CS42L42_TIP_SENSE_PLUG_SHIFT) +#define CS42L42_HSBIAS_SENSE_SHIFT 7 +#define CS42L42_HSBIAS_SENSE_MASK (1 << CS42L42_HSBIAS_SENSE_SHIFT) +#define CS42L42_DET_INT_VAL1_MASK (CS42L42_TIP_SENSE_UNPLUG_MASK | \ + CS42L42_TIP_SENSE_PLUG_MASK | \ + CS42L42_HSBIAS_SENSE_MASK) + +#define CS42L42_DET_INT2_MASK (CS42L42_PAGE_1B + 0x7A) +#define CS42L42_M_SHORT_DET_SHIFT 0 +#define CS42L42_M_SHORT_DET_MASK (1 << \ + CS42L42_M_SHORT_DET_SHIFT) +#define CS42L42_M_SHORT_RLS_SHIFT 1 +#define CS42L42_M_SHORT_RLS_MASK (1 << \ + CS42L42_M_SHORT_RLS_SHIFT) +#define CS42L42_M_HSBIAS_HIZ_SHIFT 2 +#define CS42L42_M_HSBIAS_HIZ_MASK (1 << \ + CS42L42_M_HSBIAS_HIZ_SHIFT) +#define CS42L42_M_DETECT_FT_SHIFT 6 +#define CS42L42_M_DETECT_FT_MASK (1 << \ + CS42L42_M_DETECT_FT_SHIFT) +#define CS42L42_M_DETECT_TF_SHIFT 7 +#define CS42L42_M_DETECT_TF_MASK (1 << \ + CS42L42_M_DETECT_TF_SHIFT) +#define CS42L42_DET_INT_VAL2_MASK (CS42L42_M_SHORT_DET_MASK | \ + CS42L42_M_SHORT_RLS_MASK | \ + CS42L42_M_HSBIAS_HIZ_MASK | \ + CS42L42_M_DETECT_FT_MASK | \ + CS42L42_M_DETECT_TF_MASK) + +/* Page 0x1C Headset Bias Registers */ +#define CS42L42_HS_BIAS_CTL (CS42L42_PAGE_1C + 0x03) +#define CS42L42_HSBIAS_RAMP_SHIFT 0 +#define CS42L42_HSBIAS_RAMP_MASK (3 << CS42L42_HSBIAS_RAMP_SHIFT) +#define CS42L42_HSBIAS_PD_SHIFT 4 +#define CS42L42_HSBIAS_PD_MASK (1 << CS42L42_HSBIAS_PD_SHIFT) +#define CS42L42_HSBIAS_CAPLESS_SHIFT 7 +#define CS42L42_HSBIAS_CAPLESS_MASK (1 << CS42L42_HSBIAS_CAPLESS_SHIFT) + +/* Page 0x1D ADC Registers */ +#define CS42L42_ADC_CTL (CS42L42_PAGE_1D + 0x01) +#define CS42L42_ADC_NOTCH_DIS_SHIFT 5 +#define CS42L42_ADC_FORCE_WEAK_VCM_SHIFT 4 +#define CS42L42_ADC_INV_SHIFT 2 +#define CS42L42_ADC_DIG_BOOST_SHIFT 0 + +#define CS42L42_ADC_VOLUME (CS42L42_PAGE_1D + 0x03) +#define CS42L42_ADC_VOL_SHIFT 0 + +#define CS42L42_ADC_WNF_HPF_CTL (CS42L42_PAGE_1D + 0x04) +#define CS42L42_ADC_WNF_CF_SHIFT 4 +#define CS42L42_ADC_WNF_EN_SHIFT 3 +#define CS42L42_ADC_HPF_CF_SHIFT 1 +#define CS42L42_ADC_HPF_EN_SHIFT 0 + +/* Page 0x1F DAC Registers */ +#define CS42L42_DAC_CTL1 (CS42L42_PAGE_1F + 0x01) +#define CS42L42_DACB_INV_SHIFT 1 +#define CS42L42_DACA_INV_SHIFT 0 + +#define CS42L42_DAC_CTL2 (CS42L42_PAGE_1F + 0x06) +#define CS42L42_HPOUT_PULLDOWN_SHIFT 4 +#define CS42L42_HPOUT_PULLDOWN_MASK (15 << CS42L42_HPOUT_PULLDOWN_SHIFT) +#define CS42L42_HPOUT_LOAD_SHIFT 3 +#define CS42L42_HPOUT_LOAD_MASK (1 << CS42L42_HPOUT_LOAD_SHIFT) +#define CS42L42_HPOUT_CLAMP_SHIFT 2 +#define CS42L42_HPOUT_CLAMP_MASK (1 << CS42L42_HPOUT_CLAMP_SHIFT) +#define CS42L42_DAC_HPF_EN_SHIFT 1 +#define CS42L42_DAC_HPF_EN_MASK (1 << CS42L42_DAC_HPF_EN_SHIFT) +#define CS42L42_DAC_MON_EN_SHIFT 0 +#define CS42L42_DAC_MON_EN_MASK (1 << CS42L42_DAC_MON_EN_SHIFT) + +/* Page 0x20 HP CTL Registers */ +#define CS42L42_HP_CTL (CS42L42_PAGE_20 + 0x01) +#define CS42L42_HP_ANA_BMUTE_SHIFT 3 +#define CS42L42_HP_ANA_BMUTE_MASK (1 << CS42L42_HP_ANA_BMUTE_SHIFT) +#define CS42L42_HP_ANA_AMUTE_SHIFT 2 +#define CS42L42_HP_ANA_AMUTE_MASK (1 << CS42L42_HP_ANA_AMUTE_SHIFT) +#define CS42L42_HP_FULL_SCALE_VOL_SHIFT 1 +#define CS42L42_HP_FULL_SCALE_VOL_MASK (1 << CS42L42_HP_FULL_SCALE_VOL_SHIFT) + +/* Page 0x21 Class H Registers */ +#define CS42L42_CLASSH_CTL (CS42L42_PAGE_21 + 0x01) + +/* Page 0x23 Mixer Volume Registers */ +#define CS42L42_MIXER_CHA_VOL (CS42L42_PAGE_23 + 0x01) +#define CS42L42_MIXER_ADC_VOL (CS42L42_PAGE_23 + 0x02) + +#define CS42L42_MIXER_CHB_VOL (CS42L42_PAGE_23 + 0x03) +#define CS42L42_MIXER_CH_VOL_SHIFT 0 +#define CS42L42_MIXER_CH_VOL_MASK (0x3f << CS42L42_MIXER_CH_VOL_SHIFT) + +/* Page 0x24 EQ Registers */ +#define CS42L42_EQ_COEF_IN0 (CS42L42_PAGE_24 + 0x01) +#define CS42L42_EQ_COEF_IN1 (CS42L42_PAGE_24 + 0x02) +#define CS42L42_EQ_COEF_IN2 (CS42L42_PAGE_24 + 0x03) +#define CS42L42_EQ_COEF_IN3 (CS42L42_PAGE_24 + 0x04) +#define CS42L42_EQ_COEF_RW (CS42L42_PAGE_24 + 0x06) +#define CS42L42_EQ_COEF_OUT0 (CS42L42_PAGE_24 + 0x07) +#define CS42L42_EQ_COEF_OUT1 (CS42L42_PAGE_24 + 0x08) +#define CS42L42_EQ_COEF_OUT2 (CS42L42_PAGE_24 + 0x09) +#define CS42L42_EQ_COEF_OUT3 (CS42L42_PAGE_24 + 0x0A) +#define CS42L42_EQ_INIT_STAT (CS42L42_PAGE_24 + 0x0B) +#define CS42L42_EQ_START_FILT (CS42L42_PAGE_24 + 0x0C) +#define CS42L42_EQ_MUTE_CTL (CS42L42_PAGE_24 + 0x0E) + +/* Page 0x25 Audio Port Registers */ +#define CS42L42_SP_RX_CH_SEL (CS42L42_PAGE_25 + 0x01) + +#define CS42L42_SP_RX_ISOC_CTL (CS42L42_PAGE_25 + 0x02) +#define CS42L42_SP_RX_RSYNC_SHIFT 6 +#define CS42L42_SP_RX_RSYNC_MASK (1 << CS42L42_SP_RX_RSYNC_SHIFT) +#define CS42L42_SP_RX_NSB_POS_SHIFT 3 +#define CS42L42_SP_RX_NSB_POS_MASK (7 << CS42L42_SP_RX_NSB_POS_SHIFT) +#define CS42L42_SP_RX_NFS_NSBB_SHIFT 2 +#define CS42L42_SP_RX_NFS_NSBB_MASK (1 << CS42L42_SP_RX_NFS_NSBB_SHIFT) +#define CS42L42_SP_RX_ISOC_MODE_SHIFT 0 +#define CS42L42_SP_RX_ISOC_MODE_MASK (3 << CS42L42_SP_RX_ISOC_MODE_SHIFT) + +#define CS42L42_SP_RX_FS (CS42L42_PAGE_25 + 0x03) +#define CS42l42_SPDIF_CH_SEL (CS42L42_PAGE_25 + 0x04) +#define CS42L42_SP_TX_ISOC_CTL (CS42L42_PAGE_25 + 0x05) +#define CS42L42_SP_TX_FS (CS42L42_PAGE_25 + 0x06) +#define CS42L42_SPDIF_SW_CTL1 (CS42L42_PAGE_25 + 0x07) + +/* Page 0x26 SRC Registers */ +#define CS42L42_SRC_SDIN_FS (CS42L42_PAGE_26 + 0x01) +#define CS42L42_SRC_SDIN_FS_SHIFT 0 +#define CS42L42_SRC_SDIN_FS_MASK (0x1f << CS42L42_SRC_SDIN_FS_SHIFT) + +#define CS42L42_SRC_SDOUT_FS (CS42L42_PAGE_26 + 0x09) + +/* Page 0x28 S/PDIF Registers */ +#define CS42L42_SPDIF_CTL1 (CS42L42_PAGE_28 + 0x01) +#define CS42L42_SPDIF_CTL2 (CS42L42_PAGE_28 + 0x02) +#define CS42L42_SPDIF_CTL3 (CS42L42_PAGE_28 + 0x03) +#define CS42L42_SPDIF_CTL4 (CS42L42_PAGE_28 + 0x04) + +/* Page 0x29 Serial Port TX Registers */ +#define CS42L42_ASP_TX_SZ_EN (CS42L42_PAGE_29 + 0x01) +#define CS42L42_ASP_TX_CH_EN (CS42L42_PAGE_29 + 0x02) +#define CS42L42_ASP_TX_CH_AP_RES (CS42L42_PAGE_29 + 0x03) +#define CS42L42_ASP_TX_CH1_BIT_MSB (CS42L42_PAGE_29 + 0x04) +#define CS42L42_ASP_TX_CH1_BIT_LSB (CS42L42_PAGE_29 + 0x05) +#define CS42L42_ASP_TX_HIZ_DLY_CFG (CS42L42_PAGE_29 + 0x06) +#define CS42L42_ASP_TX_CH2_BIT_MSB (CS42L42_PAGE_29 + 0x0A) +#define CS42L42_ASP_TX_CH2_BIT_LSB (CS42L42_PAGE_29 + 0x0B) + +/* Page 0x2A Serial Port RX Registers */ +#define CS42L42_ASP_RX_DAI0_EN (CS42L42_PAGE_2A + 0x01) +#define CS42L42_ASP_RX0_CH_EN_SHIFT 2 +#define CS42L42_ASP_RX0_CH_EN_MASK (0xf << CS42L42_ASP_RX0_CH_EN_SHIFT) +#define CS42L42_ASP_RX0_CH1_EN 1 +#define CS42L42_ASP_RX0_CH2_EN 2 +#define CS42L42_ASP_RX0_CH3_EN 4 +#define CS42L42_ASP_RX0_CH4_EN 8 + +#define CS42L42_ASP_RX_DAI0_CH1_AP_RES (CS42L42_PAGE_2A + 0x02) +#define CS42L42_ASP_RX_DAI0_CH1_BIT_MSB (CS42L42_PAGE_2A + 0x03) +#define CS42L42_ASP_RX_DAI0_CH1_BIT_LSB (CS42L42_PAGE_2A + 0x04) +#define CS42L42_ASP_RX_DAI0_CH2_AP_RES (CS42L42_PAGE_2A + 0x05) +#define CS42L42_ASP_RX_DAI0_CH2_BIT_MSB (CS42L42_PAGE_2A + 0x06) +#define CS42L42_ASP_RX_DAI0_CH2_BIT_LSB (CS42L42_PAGE_2A + 0x07) +#define CS42L42_ASP_RX_DAI0_CH3_AP_RES (CS42L42_PAGE_2A + 0x08) +#define CS42L42_ASP_RX_DAI0_CH3_BIT_MSB (CS42L42_PAGE_2A + 0x09) +#define CS42L42_ASP_RX_DAI0_CH3_BIT_LSB (CS42L42_PAGE_2A + 0x0A) +#define CS42L42_ASP_RX_DAI0_CH4_AP_RES (CS42L42_PAGE_2A + 0x0B) +#define CS42L42_ASP_RX_DAI0_CH4_BIT_MSB (CS42L42_PAGE_2A + 0x0C) +#define CS42L42_ASP_RX_DAI0_CH4_BIT_LSB (CS42L42_PAGE_2A + 0x0D) +#define CS42L42_ASP_RX_DAI1_CH1_AP_RES (CS42L42_PAGE_2A + 0x0E) +#define CS42L42_ASP_RX_DAI1_CH1_BIT_MSB (CS42L42_PAGE_2A + 0x0F) +#define CS42L42_ASP_RX_DAI1_CH1_BIT_LSB (CS42L42_PAGE_2A + 0x10) +#define CS42L42_ASP_RX_DAI1_CH2_AP_RES (CS42L42_PAGE_2A + 0x11) +#define CS42L42_ASP_RX_DAI1_CH2_BIT_MSB (CS42L42_PAGE_2A + 0x12) +#define CS42L42_ASP_RX_DAI1_CH2_BIT_LSB (CS42L42_PAGE_2A + 0x13) + +#define CS42L42_ASP_RX_CH_AP_SHIFT 6 +#define CS42L42_ASP_RX_CH_AP_MASK (1 << CS42L42_ASP_RX_CH_AP_SHIFT) +#define CS42L42_ASP_RX_CH_AP_LOW 0 +#define CS42L42_ASP_RX_CH_AP_HI 1 +#define CS42L42_ASP_RX_CH_RES_SHIFT 0 +#define CS42L42_ASP_RX_CH_RES_MASK (3 << CS42L42_ASP_RX_CH_RES_SHIFT) +#define CS42L42_ASP_RX_CH_RES_32 3 +#define CS42L42_ASP_RX_CH_RES_16 1 +#define CS42L42_ASP_RX_CH_BIT_ST_SHIFT 0 +#define CS42L42_ASP_RX_CH_BIT_ST_MASK (0xff << CS42L42_ASP_RX_CH_BIT_ST_SHIFT) + +/* Page 0x30 ID Registers */ +#define CS42L42_SUB_REVID (CS42L42_PAGE_30 + 0x14) +#define CS42L42_MAX_REGISTER (CS42L42_PAGE_30 + 0x14) + +/* Defines for fracturing values spread across multiple registers */ +#define CS42L42_FRAC0_VAL(val) ((val) & 0x0000ff) +#define CS42L42_FRAC1_VAL(val) (((val) & 0x00ff00) >> 8) +#define CS42L42_FRAC2_VAL(val) (((val) & 0xff0000) >> 16) + +#define CS42L42_NUM_SUPPLIES 5 + +static const char *const cs42l42_supply_names[CS42L42_NUM_SUPPLIES] = { + "VA", + "VP", + "VCP", + "VD_FILT", + "VL", +}; + +struct cs42l42_private { + struct regmap *regmap; + struct snd_soc_codec *codec; + struct regulator_bulk_data supplies[CS42L42_NUM_SUPPLIES]; + struct gpio_desc *reset_gpio; + struct completion pdn_done; + u32 sclk; + u32 srate; + u32 swidth; + u8 plug_state; + u8 hs_type; + u8 ts_inv; + u8 ts_dbnc_rise; + u8 ts_dbnc_fall; + u8 btn_det_init_dbnce; + u8 btn_det_event_dbnce; + u8 bias_thresholds[CS42L42_NUM_BIASES]; + u8 hs_bias_ramp_rate; + u8 hs_bias_ramp_time; +}; + +#endif /* __CS42L42_H__ */ diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index 35488f14e237..96cfe38943cd 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -507,12 +507,14 @@ static int cs42l51_codec_probe(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_device_cs42l51 = { .probe = cs42l51_codec_probe, - .controls = cs42l51_snd_controls, - .num_controls = ARRAY_SIZE(cs42l51_snd_controls), - .dapm_widgets = cs42l51_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(cs42l51_dapm_widgets), - .dapm_routes = cs42l51_routes, - .num_dapm_routes = ARRAY_SIZE(cs42l51_routes), + .component_driver = { + .controls = cs42l51_snd_controls, + .num_controls = ARRAY_SIZE(cs42l51_snd_controls), + .dapm_widgets = cs42l51_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs42l51_dapm_widgets), + .dapm_routes = cs42l51_routes, + .num_dapm_routes = ARRAY_SIZE(cs42l51_routes), + }, }; const struct regmap_config cs42l51_regmap = { diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index 47b97fcefb0b..0d9c4a57301b 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -1056,13 +1056,14 @@ static const struct snd_soc_codec_driver soc_codec_dev_cs42l52 = { .set_bias_level = cs42l52_set_bias_level, .suspend_bias_off = true, - .dapm_widgets = cs42l52_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(cs42l52_dapm_widgets), - .dapm_routes = cs42l52_audio_map, - .num_dapm_routes = ARRAY_SIZE(cs42l52_audio_map), - - .controls = cs42l52_snd_controls, - .num_controls = ARRAY_SIZE(cs42l52_snd_controls), + .component_driver = { + .controls = cs42l52_snd_controls, + .num_controls = ARRAY_SIZE(cs42l52_snd_controls), + .dapm_widgets = cs42l52_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs42l52_dapm_widgets), + .dapm_routes = cs42l52_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs42l52_audio_map), + }, }; /* Current and threshold powerup sequence Pg37 */ diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c index eec1ff853b98..cb6ca85f1536 100644 --- a/sound/soc/codecs/cs42l56.c +++ b/sound/soc/codecs/cs42l56.c @@ -64,8 +64,6 @@ struct cs42l56_private { }; static const struct reg_default cs42l56_reg_defaults[] = { - { 1, 0x56 }, /* r01 - ID 1 */ - { 2, 0x04 }, /* r02 - ID 2 */ { 3, 0x7f }, /* r03 - Power Ctl 1 */ { 4, 0xff }, /* r04 - Power Ctl 2 */ { 5, 0x00 }, /* ro5 - Clocking Ctl 1 */ @@ -1121,13 +1119,14 @@ static const struct snd_soc_codec_driver soc_codec_dev_cs42l56 = { .set_bias_level = cs42l56_set_bias_level, .suspend_bias_off = true, - .dapm_widgets = cs42l56_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(cs42l56_dapm_widgets), - .dapm_routes = cs42l56_audio_map, - .num_dapm_routes = ARRAY_SIZE(cs42l56_audio_map), - - .controls = cs42l56_snd_controls, - .num_controls = ARRAY_SIZE(cs42l56_snd_controls), + .component_driver = { + .controls = cs42l56_snd_controls, + .num_controls = ARRAY_SIZE(cs42l56_snd_controls), + .dapm_widgets = cs42l56_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs42l56_dapm_widgets), + .dapm_routes = cs42l56_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs42l56_audio_map), + }, }; static const struct regmap_config cs42l56_regmap = { @@ -1261,8 +1260,6 @@ static int cs42l56_i2c_probe(struct i2c_client *i2c_client, return ret; } - regcache_cache_bypass(cs42l56->regmap, true); - ret = regmap_read(cs42l56->regmap, CS42L56_CHIP_ID_1, ®); devid = reg & CS42L56_CHIP_ID_MASK; if (devid != CS42L56_DEVID) { @@ -1278,23 +1275,25 @@ static int cs42l56_i2c_probe(struct i2c_client *i2c_client, dev_info(&i2c_client->dev, "Alpha Rev %X Metal Rev %X\n", alpha_rev, metal_rev); - regcache_cache_bypass(cs42l56->regmap, false); - if (cs42l56->pdata.ain1a_ref_cfg) regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX, - CS42L56_AIN1A_REF_MASK, 1); + CS42L56_AIN1A_REF_MASK, + CS42L56_AIN1A_REF_MASK); if (cs42l56->pdata.ain1b_ref_cfg) regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX, - CS42L56_AIN1B_REF_MASK, 1); + CS42L56_AIN1B_REF_MASK, + CS42L56_AIN1B_REF_MASK); if (cs42l56->pdata.ain2a_ref_cfg) regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX, - CS42L56_AIN2A_REF_MASK, 1); + CS42L56_AIN2A_REF_MASK, + CS42L56_AIN2A_REF_MASK); if (cs42l56->pdata.ain2b_ref_cfg) regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX, - CS42L56_AIN2B_REF_MASK, 1); + CS42L56_AIN2B_REF_MASK, + CS42L56_AIN2B_REF_MASK); if (cs42l56->pdata.micbias_lvl) regmap_update_bits(cs42l56->regmap, CS42L56_GAIN_BIAS_CTL, diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c index 42a8fd4e1f9b..3df2c473ab88 100644 --- a/sound/soc/codecs/cs42l73.c +++ b/sound/soc/codecs/cs42l73.c @@ -794,7 +794,7 @@ struct cs42l73_mclk_div { u8 mmcc; }; -static struct cs42l73_mclk_div cs42l73_mclk_coeffs[] = { +static const struct cs42l73_mclk_div cs42l73_mclk_coeffs[] = { /* MCLK, Sample Rate, xMMCC[5:0] */ {5644800, 11025, 0x30}, {5644800, 22050, 0x20}, @@ -844,7 +844,7 @@ struct cs42l73_mclkx_div { u8 mclkdiv; }; -static struct cs42l73_mclkx_div cs42l73_mclkx_coeffs[] = { +static const struct cs42l73_mclkx_div cs42l73_mclkx_coeffs[] = { {5644800, 1, 0}, /* 5644800 */ {6000000, 1, 0}, /* 6000000 */ {6144000, 1, 0}, /* 6144000 */ @@ -1257,13 +1257,14 @@ static const struct snd_soc_codec_driver soc_codec_dev_cs42l73 = { .set_bias_level = cs42l73_set_bias_level, .suspend_bias_off = true, - .dapm_widgets = cs42l73_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(cs42l73_dapm_widgets), - .dapm_routes = cs42l73_audio_map, - .num_dapm_routes = ARRAY_SIZE(cs42l73_audio_map), - - .controls = cs42l73_snd_controls, - .num_controls = ARRAY_SIZE(cs42l73_snd_controls), + .component_driver = { + .controls = cs42l73_snd_controls, + .num_controls = ARRAY_SIZE(cs42l73_snd_controls), + .dapm_widgets = cs42l73_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs42l73_dapm_widgets), + .dapm_routes = cs42l73_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs42l73_audio_map), + }, }; static const struct regmap_config cs42l73_regmap = { @@ -1336,8 +1337,6 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client, gpio_set_value_cansleep(cs42l73->pdata.reset_gpio, 1); } - regcache_cache_bypass(cs42l73->regmap, true); - /* initialize codec */ ret = regmap_read(cs42l73->regmap, CS42L73_DEVID_AB, ®); devid = (reg & 0xFF) << 12; @@ -1365,8 +1364,6 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client, dev_info(&i2c_client->dev, "Cirrus Logic CS42L73, Revision: %02X\n", reg & 0xFF); - regcache_cache_bypass(cs42l73->regmap, false); - ret = snd_soc_register_codec(&i2c_client->dev, &soc_codec_dev_cs42l73, cs42l73_dai, ARRAY_SIZE(cs42l73_dai)); diff --git a/sound/soc/codecs/cs42xx8.c b/sound/soc/codecs/cs42xx8.c index 1179101b2b05..c1785bd4ff19 100644 --- a/sound/soc/codecs/cs42xx8.c +++ b/sound/soc/codecs/cs42xx8.c @@ -321,7 +321,6 @@ static struct snd_soc_dai_driver cs42xx8_dai = { }; static const struct reg_default cs42xx8_reg[] = { - { 0x01, 0x01 }, /* Chip I.D. and Revision Register */ { 0x02, 0x00 }, /* Power Control */ { 0x03, 0xF0 }, /* Functional Mode */ { 0x04, 0x46 }, /* Interface Formats */ @@ -411,12 +410,14 @@ static const struct snd_soc_codec_driver cs42xx8_driver = { .probe = cs42xx8_codec_probe, .idle_bias_off = true, - .controls = cs42xx8_snd_controls, - .num_controls = ARRAY_SIZE(cs42xx8_snd_controls), - .dapm_widgets = cs42xx8_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(cs42xx8_dapm_widgets), - .dapm_routes = cs42xx8_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(cs42xx8_dapm_routes), + .component_driver = { + .controls = cs42xx8_snd_controls, + .num_controls = ARRAY_SIZE(cs42xx8_snd_controls), + .dapm_widgets = cs42xx8_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs42xx8_dapm_widgets), + .dapm_routes = cs42xx8_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs42xx8_dapm_routes), + }, }; const struct cs42xx8_driver_data cs42448_data = { @@ -496,13 +497,6 @@ int cs42xx8_probe(struct device *dev, struct regmap *regmap) /* Make sure hardware reset done */ msleep(5); - /* - * We haven't marked the chip revision as volatile due to - * sharing a register with the right input volume; explicitly - * bypass the cache to read it. - */ - regcache_cache_bypass(cs42xx8->regmap, true); - /* Validate the chip ID */ ret = regmap_read(cs42xx8->regmap, CS42XX8_CHIPID, &val); if (ret < 0) { @@ -521,8 +515,6 @@ int cs42xx8_probe(struct device *dev, struct regmap *regmap) dev_info(dev, "found device, revision %X\n", val & CS42XX8_CHIPID_REV_ID_MASK); - regcache_cache_bypass(cs42xx8->regmap, false); - cs42xx8_dai.name = cs42xx8->drvdata->name; /* Each adc supports stereo input */ diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c index 0ac8fc5ed4ae..231ca935cdf3 100644 --- a/sound/soc/codecs/cs4349.c +++ b/sound/soc/codecs/cs4349.c @@ -256,13 +256,14 @@ static struct snd_soc_dai_driver cs4349_dai = { }; static struct snd_soc_codec_driver soc_codec_dev_cs4349 = { - .controls = cs4349_snd_controls, - .num_controls = ARRAY_SIZE(cs4349_snd_controls), - - .dapm_widgets = cs4349_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(cs4349_dapm_widgets), - .dapm_routes = cs4349_routes, - .num_dapm_routes = ARRAY_SIZE(cs4349_routes), + .component_driver = { + .controls = cs4349_snd_controls, + .num_controls = ARRAY_SIZE(cs4349_snd_controls), + .dapm_widgets = cs4349_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs4349_dapm_widgets), + .dapm_routes = cs4349_routes, + .num_dapm_routes = ARRAY_SIZE(cs4349_routes), + }, }; static const struct regmap_config cs4349_regmap = { diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c index 954a4f5d3338..73559ae864b6 100644 --- a/sound/soc/codecs/cs47l24.c +++ b/sound/soc/codecs/cs47l24.c @@ -335,9 +335,11 @@ static const struct snd_kcontrol_new cs47l24_aec_loopback_mux = static const struct snd_soc_dapm_widget cs47l24_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, - ARIZONA_SYSCLK_ENA_SHIFT, 0, NULL, 0), + ARIZONA_SYSCLK_ENA_SHIFT, 0, arizona_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1, - ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0), + ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK, ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK, @@ -746,6 +748,16 @@ static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = { { "IN2L", NULL, "SYSCLK" }, { "IN2R", NULL, "SYSCLK" }, + { "ASRC1L", NULL, "SYSCLK" }, + { "ASRC1R", NULL, "SYSCLK" }, + { "ASRC2L", NULL, "SYSCLK" }, + { "ASRC2R", NULL, "SYSCLK" }, + + { "ASRC1L", NULL, "ASYNCCLK" }, + { "ASRC1R", NULL, "ASYNCCLK" }, + { "ASRC2L", NULL, "ASYNCCLK" }, + { "ASRC2R", NULL, "ASYNCCLK" }, + { "MICBIAS1", NULL, "MICVDD" }, { "MICBIAS2", NULL, "MICVDD" }, @@ -804,7 +816,6 @@ static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = { { "AIF3 Capture", NULL, "SYSCLK" }, { "Voice Control DSP", NULL, "DSP3" }, - { "Voice Control DSP", NULL, "SYSCLK" }, { "IN1L PGA", NULL, "IN1L" }, { "IN1R PGA", NULL, "IN1R" }, @@ -813,7 +824,6 @@ static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = { { "IN2R PGA", NULL, "IN2R" }, { "Audio Trace DSP", NULL, "DSP2" }, - { "Audio Trace DSP", NULL, "SYSCLK" }, ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"), ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"), @@ -1056,7 +1066,7 @@ static struct snd_soc_dai_driver cs47l24_dai[] = { static int cs47l24_open(struct snd_compr_stream *stream) { struct snd_soc_pcm_runtime *rtd = stream->private_data; - struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(rtd->codec); + struct cs47l24_priv *priv = snd_soc_platform_get_drvdata(rtd->platform); struct arizona *arizona = priv->core.arizona; int n_adsp; @@ -1105,8 +1115,8 @@ static irqreturn_t cs47l24_adsp2_irq(int irq, void *data) static int cs47l24_codec_probe(struct snd_soc_codec *codec) { struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec); - struct arizona *arizona = priv->core.arizona; int ret; priv->core.arizona->dapm = dapm; @@ -1116,14 +1126,6 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec) arizona_init_mono(codec); arizona_init_notifiers(codec); - ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, - "ADSP2 Compressed IRQ", cs47l24_adsp2_irq, - priv); - if (ret != 0) { - dev_err(codec->dev, "Failed to request DSP IRQ: %d\n", ret); - return ret; - } - ret = wm_adsp2_codec_probe(&priv->core.adsp[1], codec); if (ret) goto err_adsp2_codec_probe; @@ -1137,7 +1139,7 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec) if (ret) goto err_adsp2_codec_probe; - snd_soc_dapm_disable_pin(dapm, "HAPTICS"); + snd_soc_component_disable_pin(component, "HAPTICS"); return 0; @@ -1151,17 +1153,12 @@ err_adsp2_codec_probe: static int cs47l24_codec_remove(struct snd_soc_codec *codec) { struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec); - struct arizona *arizona = priv->core.arizona; wm_adsp2_codec_remove(&priv->core.adsp[1], codec); wm_adsp2_codec_remove(&priv->core.adsp[2], codec); priv->core.arizona->dapm = NULL; - arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv); - - arizona_free_spk(codec); - return 0; } @@ -1190,12 +1187,14 @@ static struct snd_soc_codec_driver soc_codec_dev_cs47l24 = { .set_sysclk = arizona_set_sysclk, .set_pll = cs47l24_set_fll, - .controls = cs47l24_snd_controls, - .num_controls = ARRAY_SIZE(cs47l24_snd_controls), - .dapm_widgets = cs47l24_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(cs47l24_dapm_widgets), - .dapm_routes = cs47l24_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(cs47l24_dapm_routes), + .component_driver = { + .controls = cs47l24_snd_controls, + .num_controls = ARRAY_SIZE(cs47l24_snd_controls), + .dapm_widgets = cs47l24_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs47l24_dapm_widgets), + .dapm_routes = cs47l24_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs47l24_dapm_routes), + }, }; static struct snd_compr_ops cs47l24_compr_ops = { @@ -1275,25 +1274,47 @@ static int cs47l24_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_idle(&pdev->dev); + ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, + "ADSP2 Compressed IRQ", cs47l24_adsp2_irq, + cs47l24); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret); + return ret; + } + + ret = arizona_init_spk_irqs(arizona); + if (ret < 0) + goto err_dsp_irq; + ret = snd_soc_register_platform(&pdev->dev, &cs47l24_compr_platform); if (ret < 0) { dev_err(&pdev->dev, "Failed to register platform: %d\n", ret); - return ret; + goto err_spk_irqs; } ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24, cs47l24_dai, ARRAY_SIZE(cs47l24_dai)); if (ret < 0) { dev_err(&pdev->dev, "Failed to register codec: %d\n", ret); - snd_soc_unregister_platform(&pdev->dev); + goto err_platform; } return ret; + +err_platform: + snd_soc_unregister_platform(&pdev->dev); +err_spk_irqs: + arizona_free_spk_irqs(arizona); +err_dsp_irq: + arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, cs47l24); + + return ret; } static int cs47l24_remove(struct platform_device *pdev) { struct cs47l24_priv *cs47l24 = platform_get_drvdata(pdev); + struct arizona *arizona = cs47l24->core.arizona; snd_soc_unregister_platform(&pdev->dev); snd_soc_unregister_codec(&pdev->dev); @@ -1302,6 +1323,10 @@ static int cs47l24_remove(struct platform_device *pdev) wm_adsp2_remove(&cs47l24->core.adsp[1]); wm_adsp2_remove(&cs47l24->core.adsp[2]); + arizona_free_spk_irqs(arizona); + + arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, cs47l24); + return 0; } diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c index 2c0d9c430a8c..cb47fb595ff4 100644 --- a/sound/soc/codecs/cs53l30.c +++ b/sound/soc/codecs/cs53l30.c @@ -466,7 +466,7 @@ struct cs53l30_mclk_div { u8 mclk_int_scale; }; -static struct cs53l30_mclk_div cs53l30_mclk_coeffs[] = { +static const struct cs53l30_mclk_div cs53l30_mclk_coeffs[] = { /* NOTE: Enable MCLK_INT_SCALE to save power. */ /* MCLK, Sample Rate, asp_rate, internal_fs_ratio, mclk_int_scale */ @@ -511,7 +511,7 @@ struct cs53l30_mclkx_div { u8 mclkdiv; }; -static struct cs53l30_mclkx_div cs53l30_mclkx_coeffs[] = { +static const struct cs53l30_mclkx_div cs53l30_mclkx_coeffs[] = { {5644800, 1, CS53L30_MCLK_DIV_BY_1}, {6000000, 1, CS53L30_MCLK_DIV_BY_1}, {6144000, 1, CS53L30_MCLK_DIV_BY_1}, @@ -897,13 +897,14 @@ static struct snd_soc_codec_driver cs53l30_driver = { .set_bias_level = cs53l30_set_bias_level, .idle_bias_off = true, - .dapm_widgets = cs53l30_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(cs53l30_dapm_widgets), - .dapm_routes = cs53l30_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(cs53l30_dapm_routes), - - .controls = cs53l30_snd_controls, - .num_controls = ARRAY_SIZE(cs53l30_snd_controls), + .component_driver = { + .controls = cs53l30_snd_controls, + .num_controls = ARRAY_SIZE(cs53l30_snd_controls), + .dapm_widgets = cs53l30_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs53l30_dapm_widgets), + .dapm_routes = cs53l30_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs53l30_dapm_routes), + }, }; static struct regmap_config cs53l30_regmap = { @@ -999,8 +1000,8 @@ static int cs53l30_i2c_probe(struct i2c_client *client, /* Check if MCLK provided */ cs53l30->mclk = devm_clk_get(dev, "mclk"); if (IS_ERR(cs53l30->mclk)) { - if (PTR_ERR(cs53l30->mclk) == -EPROBE_DEFER) { - ret = -EPROBE_DEFER; + if (PTR_ERR(cs53l30->mclk) != -ENOENT) { + ret = PTR_ERR(cs53l30->mclk); goto error; } /* Otherwise mark the mclk pointer to NULL */ diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c index fb3885fe0afb..2c12471e42a6 100644 --- a/sound/soc/codecs/cx20442.c +++ b/sound/soc/codecs/cx20442.c @@ -407,10 +407,12 @@ static struct snd_soc_codec_driver cx20442_codec_dev = { .reg_word_size = sizeof(u8), .read = cx20442_read_reg_cache, .write = cx20442_write, - .dapm_widgets = cx20442_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(cx20442_dapm_widgets), - .dapm_routes = cx20442_audio_map, - .num_dapm_routes = ARRAY_SIZE(cx20442_audio_map), + .component_driver = { + .dapm_widgets = cx20442_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cx20442_dapm_widgets), + .dapm_routes = cx20442_audio_map, + .num_dapm_routes = ARRAY_SIZE(cx20442_audio_map), + }, }; static int cx20442_platform_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c index af23a61b7b28..17053dfc94cf 100644 --- a/sound/soc/codecs/da7210.c +++ b/sound/soc/codecs/da7210.c @@ -1167,13 +1167,14 @@ static int da7210_probe(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_dev_da7210 = { .probe = da7210_probe, - .controls = da7210_snd_controls, - .num_controls = ARRAY_SIZE(da7210_snd_controls), - - .dapm_widgets = da7210_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(da7210_dapm_widgets), - .dapm_routes = da7210_audio_map, - .num_dapm_routes = ARRAY_SIZE(da7210_audio_map), + .component_driver = { + .controls = da7210_snd_controls, + .num_controls = ARRAY_SIZE(da7210_snd_controls), + .dapm_widgets = da7210_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(da7210_dapm_widgets), + .dapm_routes = da7210_audio_map, + .num_dapm_routes = ARRAY_SIZE(da7210_audio_map), + }, }; #if IS_ENABLED(CONFIG_I2C) diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index bcf1834c5648..12da55882c06 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -750,11 +750,18 @@ static int da7213_dai_event(struct snd_soc_dapm_widget *w, snd_soc_update_bits(codec, DA7213_PC_COUNT, DA7213_PC_FREERUN_MASK, 0); - /* Slave mode, if SRM not enabled no need for status checks */ + /* If SRM not enabled then nothing more to do */ pll_ctrl = snd_soc_read(codec, DA7213_PLL_CTRL); if (!(pll_ctrl & DA7213_PLL_SRM_EN)) return 0; + /* Assist 32KHz mode PLL lock */ + if (pll_ctrl & DA7213_PLL_32K_MODE) { + snd_soc_write(codec, 0xF0, 0x8B); + snd_soc_write(codec, 0xF2, 0x03); + snd_soc_write(codec, 0xF0, 0x00); + } + /* Check SRM has locked */ do { pll_status = snd_soc_read(codec, DA7213_PLL_STATUS); @@ -771,6 +778,14 @@ static int da7213_dai_event(struct snd_soc_dapm_widget *w, return 0; case SND_SOC_DAPM_POST_PMD: + /* Revert 32KHz PLL lock udpates if applied previously */ + pll_ctrl = snd_soc_read(codec, DA7213_PLL_CTRL); + if (pll_ctrl & DA7213_PLL_32K_MODE) { + snd_soc_write(codec, 0xF0, 0x8B); + snd_soc_write(codec, 0xF2, 0x01); + snd_soc_write(codec, 0xF0, 0x00); + } + /* PC free-running */ snd_soc_update_bits(codec, DA7213_PC_COUNT, DA7213_PC_FREERUN_MASK, @@ -1297,10 +1312,13 @@ static int da7213_set_dai_sysclk(struct snd_soc_dai *codec_dai, switch (clk_id) { case DA7213_CLKSRC_MCLK: - da7213->mclk_squarer_en = false; + snd_soc_update_bits(codec, DA7213_PLL_CTRL, + DA7213_PLL_MCLK_SQR_EN, 0); break; case DA7213_CLKSRC_MCLK_SQR: - da7213->mclk_squarer_en = true; + snd_soc_update_bits(codec, DA7213_PLL_CTRL, + DA7213_PLL_MCLK_SQR_EN, + DA7213_PLL_MCLK_SQR_EN); break; default: dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id); @@ -1324,7 +1342,7 @@ static int da7213_set_dai_sysclk(struct snd_soc_dai *codec_dai, return 0; } -/* Supported PLL input frequencies are 5MHz - 54MHz. */ +/* Supported PLL input frequencies are 32KHz, 5MHz - 54MHz. */ static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, int source, unsigned int fref, unsigned int fout) { @@ -1336,22 +1354,26 @@ static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, u32 freq_ref; u64 frac_div; - /* Reset PLL configuration */ - snd_soc_write(codec, DA7213_PLL_CTRL, 0); - - pll_ctrl = 0; - /* Workout input divider based on MCLK rate */ if (da7213->mclk_rate == 32768) { + if (!da7213->master) { + dev_err(codec->dev, + "32KHz only valid if codec is clock master\n"); + return -EINVAL; + } + /* 32KHz PLL Mode */ indiv_bits = DA7213_PLL_INDIV_9_TO_18_MHZ; indiv = DA7213_PLL_INDIV_9_TO_18_MHZ_VAL; + source = DA7213_SYSCLK_PLL_32KHZ; freq_ref = 3750000; - pll_ctrl |= DA7213_PLL_32K_MODE; + } else { - /* 5 - 54MHz MCLK */ if (da7213->mclk_rate < 5000000) { - goto pll_err; + dev_err(codec->dev, + "PLL input clock %d below valid range\n", + da7213->mclk_rate); + return -EINVAL; } else if (da7213->mclk_rate <= 9000000) { indiv_bits = DA7213_PLL_INDIV_5_TO_9_MHZ; indiv = DA7213_PLL_INDIV_5_TO_9_MHZ_VAL; @@ -1365,32 +1387,44 @@ static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, indiv_bits = DA7213_PLL_INDIV_36_TO_54_MHZ; indiv = DA7213_PLL_INDIV_36_TO_54_MHZ_VAL; } else { - goto pll_err; + dev_err(codec->dev, + "PLL input clock %d above valid range\n", + da7213->mclk_rate); + return -EINVAL; } freq_ref = (da7213->mclk_rate / indiv); } - pll_ctrl |= indiv_bits; + pll_ctrl = indiv_bits; - /* PLL Bypass mode */ - if (source == DA7213_SYSCLK_MCLK) { - snd_soc_write(codec, DA7213_PLL_CTRL, pll_ctrl); + /* Configure PLL */ + switch (source) { + case DA7213_SYSCLK_MCLK: + snd_soc_update_bits(codec, DA7213_PLL_CTRL, + DA7213_PLL_INDIV_MASK | + DA7213_PLL_MODE_MASK, pll_ctrl); return 0; - } + case DA7213_SYSCLK_PLL: + break; + case DA7213_SYSCLK_PLL_SRM: + pll_ctrl |= DA7213_PLL_SRM_EN; + fout = DA7213_PLL_FREQ_OUT_94310400; + break; + case DA7213_SYSCLK_PLL_32KHZ: + if (da7213->mclk_rate != 32768) { + dev_err(codec->dev, + "32KHz mode only valid with 32KHz MCLK\n"); + return -EINVAL; + } - /* - * If Codec is slave and SRM enabled, - * freq_out is (98304000 + 90316800)/2 = 94310400 - */ - if (!da7213->master && da7213->srm_en) { + pll_ctrl |= DA7213_PLL_32K_MODE | DA7213_PLL_SRM_EN; fout = DA7213_PLL_FREQ_OUT_94310400; - pll_ctrl |= DA7213_PLL_SRM_EN; + break; + default: + dev_err(codec->dev, "Invalid PLL config\n"); + return -EINVAL; } - /* Enable MCLK squarer if required */ - if (da7213->mclk_squarer_en) - pll_ctrl |= DA7213_PLL_MCLK_SQR_EN; - /* Calculate dividers for PLL */ pll_integer = fout / freq_ref; frac_div = (u64)(fout % freq_ref) * 8192ULL; @@ -1405,14 +1439,19 @@ static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, /* Enable PLL */ pll_ctrl |= DA7213_PLL_EN; - snd_soc_write(codec, DA7213_PLL_CTRL, pll_ctrl); + snd_soc_update_bits(codec, DA7213_PLL_CTRL, + DA7213_PLL_INDIV_MASK | DA7213_PLL_MODE_MASK, + pll_ctrl); + + /* Assist 32KHz mode PLL lock */ + if (source == DA7213_SYSCLK_PLL_32KHZ) { + snd_soc_write(codec, 0xF0, 0x8B); + snd_soc_write(codec, 0xF1, 0x03); + snd_soc_write(codec, 0xF1, 0x01); + snd_soc_write(codec, 0xF0, 0x00); + } return 0; - -pll_err: - dev_err(codec_dai->dev, "Unsupported PLL input frequency %d\n", - da7213->mclk_rate); - return -EINVAL; } /* DAI operations */ @@ -1454,11 +1493,10 @@ static int da7213_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_ON: - case SND_SOC_BIAS_PREPARE: break; - case SND_SOC_BIAS_STANDBY: - if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { - /* MCLK */ + case SND_SOC_BIAS_PREPARE: + /* Enable MCLK for transition to ON state */ + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) { if (da7213->mclk) { ret = clk_prepare_enable(da7213->mclk); if (ret) { @@ -1467,21 +1505,24 @@ static int da7213_set_bias_level(struct snd_soc_codec *codec, return ret; } } - + } + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { /* Enable VMID reference & master bias */ snd_soc_update_bits(codec, DA7213_REFERENCES, DA7213_VMID_EN | DA7213_BIAS_EN, DA7213_VMID_EN | DA7213_BIAS_EN); + } else { + /* Remove MCLK */ + if (da7213->mclk) + clk_disable_unprepare(da7213->mclk); } break; case SND_SOC_BIAS_OFF: /* Disable VMID reference & master bias */ snd_soc_update_bits(codec, DA7213_REFERENCES, DA7213_VMID_EN | DA7213_BIAS_EN, 0); - - /* MCLK */ - if (da7213->mclk) - clk_disable_unprepare(da7213->mclk); break; } return 0; @@ -1605,9 +1646,6 @@ static int da7213_probe(struct snd_soc_codec *codec) DA7213_ALC_CALIB_MODE_MAN, 0); da7213->alc_calib_auto = true; - /* Default to using SRM for slave mode */ - da7213->srm_en = true; - /* Default PC counter to free-running */ snd_soc_update_bits(codec, DA7213_PC_COUNT, DA7213_PC_FREERUN_MASK, DA7213_PC_FREERUN_MASK); @@ -1740,13 +1778,14 @@ static struct snd_soc_codec_driver soc_codec_dev_da7213 = { .probe = da7213_probe, .set_bias_level = da7213_set_bias_level, - .controls = da7213_snd_controls, - .num_controls = ARRAY_SIZE(da7213_snd_controls), - - .dapm_widgets = da7213_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(da7213_dapm_widgets), - .dapm_routes = da7213_audio_map, - .num_dapm_routes = ARRAY_SIZE(da7213_audio_map), + .component_driver = { + .controls = da7213_snd_controls, + .num_controls = ARRAY_SIZE(da7213_snd_controls), + .dapm_widgets = da7213_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(da7213_dapm_widgets), + .dapm_routes = da7213_audio_map, + .num_dapm_routes = ARRAY_SIZE(da7213_audio_map), + }, }; static const struct regmap_config da7213_regmap_config = { diff --git a/sound/soc/codecs/da7213.h b/sound/soc/codecs/da7213.h index fbb7a356a501..16ef56f77cd4 100644 --- a/sound/soc/codecs/da7213.h +++ b/sound/soc/codecs/da7213.h @@ -172,6 +172,7 @@ #define DA7213_PLL_32K_MODE (0x1 << 5) #define DA7213_PLL_SRM_EN (0x1 << 6) #define DA7213_PLL_EN (0x1 << 7) +#define DA7213_PLL_MODE_MASK (0x7 << 5) /* DA7213_DAI_CLK_MODE = 0x28 */ #define DA7213_DAI_BCLKS_PER_WCLK_32 (0x0 << 0) @@ -499,8 +500,6 @@ #define DA7213_ALC_AVG_ITERATIONS 5 /* PLL related */ -#define DA7213_SYSCLK_MCLK 0 -#define DA7213_SYSCLK_PLL 1 #define DA7213_PLL_FREQ_OUT_90316800 90316800 #define DA7213_PLL_FREQ_OUT_98304000 98304000 #define DA7213_PLL_FREQ_OUT_94310400 94310400 @@ -515,6 +514,13 @@ enum da7213_clk_src { DA7213_CLKSRC_MCLK_SQR, }; +enum da7213_sys_clk { + DA7213_SYSCLK_MCLK = 0, + DA7213_SYSCLK_PLL, + DA7213_SYSCLK_PLL_SRM, + DA7213_SYSCLK_PLL_32KHZ +}; + /* Codec private data */ struct da7213_priv { struct regmap *regmap; @@ -522,8 +528,6 @@ struct da7213_priv { unsigned int mclk_rate; int clk_src; bool master; - bool mclk_squarer_en; - bool srm_en; bool alc_calib_auto; bool alc_en; struct da7213_platform_data *pdata; diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c index 99ce23e113bf..c69e97654fc6 100644 --- a/sound/soc/codecs/da7218.c +++ b/sound/soc/codecs/da7218.c @@ -1819,7 +1819,7 @@ static int da7218_set_dai_sysclk(struct snd_soc_dai *codec_dai, if (da7218->mclk_rate == freq) return 0; - if (((freq < 2000000) && (freq != 32768)) || (freq > 54000000)) { + if ((freq < 2000000) || (freq > 54000000)) { dev_err(codec_dai->dev, "Unsupported MCLK value %d\n", freq); return -EINVAL; @@ -1866,11 +1866,8 @@ static int da7218_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, u32 freq_ref; u64 frac_div; - /* Verify 32KHz, 2MHz - 54MHz MCLK provided, and set input divider */ - if (da7218->mclk_rate == 32768) { - indiv_bits = DA7218_PLL_INDIV_9_TO_18_MHZ; - indiv = DA7218_PLL_INDIV_9_TO_18_MHZ_VAL; - } else if (da7218->mclk_rate < 2000000) { + /* Verify 2MHz - 54MHz MCLK provided, and set input divider */ + if (da7218->mclk_rate < 2000000) { dev_err(codec->dev, "PLL input clock %d below valid range\n", da7218->mclk_rate); return -EINVAL; @@ -1911,9 +1908,6 @@ static int da7218_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, case DA7218_SYSCLK_PLL_SRM: pll_ctrl |= DA7218_PLL_MODE_SRM; break; - case DA7218_SYSCLK_PLL_32KHZ: - pll_ctrl |= DA7218_PLL_MODE_32KHZ; - break; default: dev_err(codec->dev, "Invalid PLL config\n"); return -EINVAL; @@ -2589,20 +2583,22 @@ static int da7218_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_ON: - case SND_SOC_BIAS_PREPARE: break; - case SND_SOC_BIAS_STANDBY: - if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { - /* MCLK */ + case SND_SOC_BIAS_PREPARE: + /* Enable MCLK for transition to ON state */ + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) { if (da7218->mclk) { ret = clk_prepare_enable(da7218->mclk); if (ret) { - dev_err(codec->dev, - "Failed to enable mclk\n"); + dev_err(codec->dev, "Failed to enable mclk\n"); return ret; } } + } + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { /* Master bias */ snd_soc_update_bits(codec, DA7218_REFERENCES, DA7218_BIAS_EN_MASK, @@ -2612,6 +2608,10 @@ static int da7218_set_bias_level(struct snd_soc_codec *codec, snd_soc_update_bits(codec, DA7218_LDO_CTRL, DA7218_LDO_EN_MASK, DA7218_LDO_EN_MASK); + } else { + /* Remove MCLK */ + if (da7218->mclk) + clk_disable_unprepare(da7218->mclk); } break; case SND_SOC_BIAS_OFF: @@ -2625,10 +2625,6 @@ static int da7218_set_bias_level(struct snd_soc_codec *codec, snd_soc_update_bits(codec, DA7218_REFERENCES, DA7218_BIAS_EN_MASK, 0); } - - /* MCLK */ - if (da7218->mclk) - clk_disable_unprepare(da7218->mclk); break; } @@ -3045,13 +3041,14 @@ static struct snd_soc_codec_driver soc_codec_dev_da7218 = { .resume = da7218_resume, .set_bias_level = da7218_set_bias_level, - .controls = da7218_snd_controls, - .num_controls = ARRAY_SIZE(da7218_snd_controls), - - .dapm_widgets = da7218_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(da7218_dapm_widgets), - .dapm_routes = da7218_audio_map, - .num_dapm_routes = ARRAY_SIZE(da7218_audio_map), + .component_driver = { + .controls = da7218_snd_controls, + .num_controls = ARRAY_SIZE(da7218_snd_controls), + .dapm_widgets = da7218_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(da7218_dapm_widgets), + .dapm_routes = da7218_audio_map, + .num_dapm_routes = ARRAY_SIZE(da7218_audio_map), + }, }; diff --git a/sound/soc/codecs/da7218.h b/sound/soc/codecs/da7218.h index 477cd37723cf..4f7ec21069a4 100644 --- a/sound/soc/codecs/da7218.h +++ b/sound/soc/codecs/da7218.h @@ -888,7 +888,6 @@ #define DA7218_PLL_MODE_BYPASS (0x0 << 6) #define DA7218_PLL_MODE_NORMAL (0x1 << 6) #define DA7218_PLL_MODE_SRM (0x2 << 6) -#define DA7218_PLL_MODE_32KHZ (0x3 << 6) /* DA7218_PLL_FRAC_TOP = 0x92 */ #define DA7218_PLL_FBDIV_FRAC_TOP_SHIFT 0 @@ -1371,7 +1370,6 @@ enum da7218_sys_clk { DA7218_SYSCLK_MCLK = 0, DA7218_SYSCLK_PLL, DA7218_SYSCLK_PLL_SRM, - DA7218_SYSCLK_PLL_32KHZ }; enum da7218_dev_id { diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c index f0057cd223a4..6274d79c1353 100644 --- a/sound/soc/codecs/da7219-aad.c +++ b/sound/soc/codecs/da7219-aad.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/clk.h> #include <linux/i2c.h> #include <linux/property.h> #include <linux/pm_wakeirq.h> @@ -114,13 +115,38 @@ static void da7219_aad_hptest_work(struct work_struct *work) struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); u16 tonegen_freq_hptest; - u8 accdet_cfg8; - int report = 0; + u8 pll_srm_sts, gain_ramp_ctrl, accdet_cfg8; + int report = 0, ret = 0; /* Lock DAPM and any Kcontrols that are affected by this test */ snd_soc_dapm_mutex_lock(dapm); mutex_lock(&da7219->lock); + /* Ensure MCLK is available for HP test procedure */ + if (da7219->mclk) { + ret = clk_prepare_enable(da7219->mclk); + if (ret) { + dev_err(codec->dev, "Failed to enable mclk - %d\n", ret); + mutex_unlock(&da7219->lock); + snd_soc_dapm_mutex_unlock(dapm); + return; + } + } + + /* + * If MCLK not present, then we're using the internal oscillator and + * require different frequency settings to achieve the same result. + */ + pll_srm_sts = snd_soc_read(codec, DA7219_PLL_SRM_STS); + if (pll_srm_sts & DA7219_PLL_SRM_STS_MCLK) + tonegen_freq_hptest = cpu_to_le16(DA7219_AAD_HPTEST_RAMP_FREQ); + else + tonegen_freq_hptest = cpu_to_le16(DA7219_AAD_HPTEST_RAMP_FREQ_INT_OSC); + + /* Ensure gain ramping at fastest rate */ + gain_ramp_ctrl = snd_soc_read(codec, DA7219_GAIN_RAMP_CTRL); + snd_soc_write(codec, DA7219_GAIN_RAMP_CTRL, DA7219_GAIN_RAMP_RATE_X8); + /* Bypass cache so it saves current settings */ regcache_cache_bypass(da7219->regmap, true); @@ -178,14 +204,29 @@ static void da7219_aad_hptest_work(struct work_struct *work) snd_soc_update_bits(codec, DA7219_MIXOUT_R_CTRL, DA7219_MIXOUT_R_AMP_EN_MASK, DA7219_MIXOUT_R_AMP_EN_MASK); - snd_soc_write(codec, DA7219_HP_L_CTRL, - DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK); - snd_soc_write(codec, DA7219_HP_R_CTRL, - DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK); + snd_soc_update_bits(codec, DA7219_HP_L_CTRL, + DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK, + DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK); + snd_soc_update_bits(codec, DA7219_HP_R_CTRL, + DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK, + DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK); + msleep(DA7219_SETTLING_DELAY); + snd_soc_update_bits(codec, DA7219_HP_L_CTRL, + DA7219_HP_L_AMP_MUTE_EN_MASK | + DA7219_HP_L_AMP_MIN_GAIN_EN_MASK, 0); + snd_soc_update_bits(codec, DA7219_HP_R_CTRL, + DA7219_HP_R_AMP_MUTE_EN_MASK | + DA7219_HP_R_AMP_MIN_GAIN_EN_MASK, 0); + + /* + * If we're running from the internal oscillator then give audio paths + * time to settle before running test. + */ + if (!(pll_srm_sts & DA7219_PLL_SRM_STS_MCLK)) + msleep(DA7219_AAD_HPTEST_INT_OSC_PATH_DELAY); /* Configure & start Tone Generator */ snd_soc_write(codec, DA7219_TONE_GEN_ON_PER, DA7219_BEEP_ON_PER_MASK); - tonegen_freq_hptest = cpu_to_le16(DA7219_AAD_HPTEST_RAMP_FREQ); regmap_raw_write(da7219->regmap, DA7219_TONE_GEN_FREQ1_L, &tonegen_freq_hptest, sizeof(tonegen_freq_hptest)); snd_soc_update_bits(codec, DA7219_TONE_GEN_CFG2, @@ -212,6 +253,7 @@ static void da7219_aad_hptest_work(struct work_struct *work) regcache_mark_dirty(da7219->regmap); regcache_sync_region(da7219->regmap, DA7219_HP_L_CTRL, DA7219_HP_R_CTRL); + msleep(DA7219_SETTLING_DELAY); regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_CTRL, DA7219_MIXOUT_R_CTRL); regcache_sync_region(da7219->regmap, DA7219_DROUTING_ST_OUTFILT_1L, @@ -244,12 +286,26 @@ static void da7219_aad_hptest_work(struct work_struct *work) snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_8, DA7219_HPTEST_EN_MASK, 0); + /* + * If we're running from the internal oscillator then give audio paths + * time to settle before allowing headphones to be driven as required. + */ + if (!(pll_srm_sts & DA7219_PLL_SRM_STS_MCLK)) + msleep(DA7219_AAD_HPTEST_INT_OSC_PATH_DELAY); + + /* Restore gain ramping rate */ + snd_soc_write(codec, DA7219_GAIN_RAMP_CTRL, gain_ramp_ctrl); + /* Drive Headphones/lineout */ snd_soc_update_bits(codec, DA7219_HP_L_CTRL, DA7219_HP_L_AMP_OE_MASK, DA7219_HP_L_AMP_OE_MASK); snd_soc_update_bits(codec, DA7219_HP_R_CTRL, DA7219_HP_R_AMP_OE_MASK, DA7219_HP_R_AMP_OE_MASK); + /* Remove MCLK, if previously enabled */ + if (da7219->mclk) + clk_disable_unprepare(da7219->mclk); + mutex_unlock(&da7219->lock); snd_soc_dapm_mutex_unlock(dapm); @@ -751,6 +807,62 @@ static void da7219_aad_handle_pdata(struct snd_soc_codec *codec) /* + * Suspend/Resume + */ + +void da7219_aad_suspend(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + struct da7219_aad_priv *da7219_aad = da7219->aad; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + u8 micbias_ctrl; + + if (da7219_aad->jack) { + /* Disable jack detection during suspend */ + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1, + DA7219_ACCDET_EN_MASK, 0); + + /* + * If we have a 4-pole jack inserted, then micbias will be + * enabled. We can disable micbias here, and keep a note to + * re-enable it on resume. If jack removal occurred during + * suspend then this will be dealt with through the IRQ handler. + */ + if (da7219_aad->jack_inserted) { + micbias_ctrl = snd_soc_read(codec, DA7219_MICBIAS_CTRL); + if (micbias_ctrl & DA7219_MICBIAS1_EN_MASK) { + snd_soc_dapm_disable_pin(dapm, "Mic Bias"); + snd_soc_dapm_sync(dapm); + da7219_aad->micbias_resume_enable = true; + } + } + } +} + +void da7219_aad_resume(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + struct da7219_aad_priv *da7219_aad = da7219->aad; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + if (da7219_aad->jack) { + /* Re-enable micbias if previously enabled for 4-pole jack */ + if (da7219_aad->jack_inserted && + da7219_aad->micbias_resume_enable) { + snd_soc_dapm_force_enable_pin(dapm, "Mic Bias"); + snd_soc_dapm_sync(dapm); + da7219_aad->micbias_resume_enable = false; + } + + /* Re-enable jack detection */ + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1, + DA7219_ACCDET_EN_MASK, + DA7219_ACCDET_EN_MASK); + } +} + + +/* * Init/Exit */ diff --git a/sound/soc/codecs/da7219-aad.h b/sound/soc/codecs/da7219-aad.h index 4fccf677cd06..117a3d7ccd31 100644 --- a/sound/soc/codecs/da7219-aad.h +++ b/sound/soc/codecs/da7219-aad.h @@ -176,8 +176,10 @@ #define DA7219_AAD_MICBIAS_CHK_DELAY 10 #define DA7219_AAD_MICBIAS_CHK_RETRIES 5 -#define DA7219_AAD_HPTEST_RAMP_FREQ 0x28 -#define DA7219_AAD_HPTEST_PERIOD 65 +#define DA7219_AAD_HPTEST_RAMP_FREQ 0x28 +#define DA7219_AAD_HPTEST_RAMP_FREQ_INT_OSC 0x4D +#define DA7219_AAD_HPTEST_PERIOD 65 +#define DA7219_AAD_HPTEST_INT_OSC_PATH_DELAY 20 enum da7219_aad_event_regs { DA7219_AAD_IRQ_REG_A = 0, @@ -199,12 +201,17 @@ struct da7219_aad_priv { struct work_struct hptest_work; struct snd_soc_jack *jack; + bool micbias_resume_enable; bool jack_inserted; }; /* AAD control */ void da7219_aad_jack_det(struct snd_soc_codec *codec, struct snd_soc_jack *jack); +/* Suspend/Resume */ +void da7219_aad_suspend(struct snd_soc_codec *codec); +void da7219_aad_resume(struct snd_soc_codec *codec); + /* Init/Exit */ int da7219_aad_init(struct snd_soc_codec *codec); void da7219_aad_exit(struct snd_soc_codec *codec); diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index 50ea94317cb3..99601627f83c 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -801,7 +801,7 @@ static int da7219_dai_event(struct snd_soc_dapm_widget *w, ++i; msleep(50); } - } while ((i < DA7219_SRM_CHECK_RETRIES) & (!srm_lock)); + } while ((i < DA7219_SRM_CHECK_RETRIES) && (!srm_lock)); if (!srm_lock) dev_warn(codec->dev, "SRM failed to lock\n"); @@ -823,6 +823,85 @@ static int da7219_dai_event(struct snd_soc_dapm_widget *w, } } +static int da7219_settling_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + case SND_SOC_DAPM_POST_PMD: + msleep(DA7219_SETTLING_DELAY); + break; + default: + break; + } + + return 0; +} + +static int da7219_mixout_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u8 hp_ctrl, min_gain_mask; + + switch (w->reg) { + case DA7219_MIXOUT_L_CTRL: + hp_ctrl = DA7219_HP_L_CTRL; + min_gain_mask = DA7219_HP_L_AMP_MIN_GAIN_EN_MASK; + break; + case DA7219_MIXOUT_R_CTRL: + hp_ctrl = DA7219_HP_R_CTRL; + min_gain_mask = DA7219_HP_R_AMP_MIN_GAIN_EN_MASK; + break; + default: + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMD: + /* Enable minimum gain on HP to avoid pops */ + snd_soc_update_bits(codec, hp_ctrl, min_gain_mask, + min_gain_mask); + + msleep(DA7219_MIN_GAIN_DELAY); + + break; + case SND_SOC_DAPM_POST_PMU: + /* Remove minimum gain on HP */ + snd_soc_update_bits(codec, hp_ctrl, min_gain_mask, 0); + + break; + } + + return 0; +} + +static int da7219_gain_ramp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + case SND_SOC_DAPM_PRE_PMD: + /* Ensure nominal gain ramping for DAPM sequence */ + da7219->gain_ramp_ctrl = + snd_soc_read(codec, DA7219_GAIN_RAMP_CTRL); + snd_soc_write(codec, DA7219_GAIN_RAMP_CTRL, + DA7219_GAIN_RAMP_RATE_NOMINAL); + break; + case SND_SOC_DAPM_POST_PMU: + case SND_SOC_DAPM_POST_PMD: + /* Restore previous gain ramp settings */ + snd_soc_write(codec, DA7219_GAIN_RAMP_CTRL, + da7219->gain_ramp_ctrl); + break; + } + + return 0; +} + /* * DAPM Widgets @@ -880,7 +959,8 @@ static const struct snd_soc_dapm_widget da7219_dapm_widgets[] = { SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), /* DAI */ - SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, DA7219_DAI_TDM_CTRL, + DA7219_DAI_OE_SHIFT, DA7219_NO_INVERT), SND_SOC_DAPM_AIF_IN("DAIIN", "Playback", 0, SND_SOC_NOPM, 0, 0), /* Output Muxes */ @@ -906,30 +986,46 @@ static const struct snd_soc_dapm_widget da7219_dapm_widgets[] = { ARRAY_SIZE(da7219_st_out_filtr_mix_controls)), /* DACs */ - SND_SOC_DAPM_DAC("DACL", NULL, DA7219_DAC_L_CTRL, DA7219_DAC_L_EN_SHIFT, - DA7219_NO_INVERT), - SND_SOC_DAPM_DAC("DACR", NULL, DA7219_DAC_R_CTRL, DA7219_DAC_R_EN_SHIFT, - DA7219_NO_INVERT), + SND_SOC_DAPM_DAC_E("DACL", NULL, DA7219_DAC_L_CTRL, + DA7219_DAC_L_EN_SHIFT, DA7219_NO_INVERT, + da7219_settling_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("DACR", NULL, DA7219_DAC_R_CTRL, + DA7219_DAC_R_EN_SHIFT, DA7219_NO_INVERT, + da7219_settling_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), /* Output PGAs */ - SND_SOC_DAPM_PGA("Mixout Left PGA", DA7219_MIXOUT_L_CTRL, - DA7219_MIXOUT_L_AMP_EN_SHIFT, DA7219_NO_INVERT, - NULL, 0), - SND_SOC_DAPM_PGA("Mixout Right PGA", DA7219_MIXOUT_R_CTRL, - DA7219_MIXOUT_R_AMP_EN_SHIFT, DA7219_NO_INVERT, - NULL, 0), - SND_SOC_DAPM_PGA("Headphone Left PGA", DA7219_HP_L_CTRL, - DA7219_HP_L_AMP_EN_SHIFT, DA7219_NO_INVERT, NULL, 0), - SND_SOC_DAPM_PGA("Headphone Right PGA", DA7219_HP_R_CTRL, - DA7219_HP_R_AMP_EN_SHIFT, DA7219_NO_INVERT, NULL, 0), + SND_SOC_DAPM_PGA_E("Mixout Left PGA", DA7219_MIXOUT_L_CTRL, + DA7219_MIXOUT_L_AMP_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0, da7219_mixout_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_E("Mixout Right PGA", DA7219_MIXOUT_R_CTRL, + DA7219_MIXOUT_R_AMP_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0, da7219_mixout_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY_S("Headphone Left PGA", 1, DA7219_HP_L_CTRL, + DA7219_HP_L_AMP_EN_SHIFT, DA7219_NO_INVERT, + da7219_settling_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("Headphone Right PGA", 1, DA7219_HP_R_CTRL, + DA7219_HP_R_AMP_EN_SHIFT, DA7219_NO_INVERT, + da7219_settling_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), /* Output Supplies */ - SND_SOC_DAPM_SUPPLY("Charge Pump", DA7219_CP_CTRL, DA7219_CP_EN_SHIFT, - DA7219_NO_INVERT, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("Charge Pump", 0, DA7219_CP_CTRL, + DA7219_CP_EN_SHIFT, DA7219_NO_INVERT, + da7219_settling_event, + SND_SOC_DAPM_POST_PMU), /* Outputs */ SND_SOC_DAPM_OUTPUT("HPL"), SND_SOC_DAPM_OUTPUT("HPR"), + + /* Pre/Post Power */ + SND_SOC_DAPM_PRE("Pre Power Gain Ramp", da7219_gain_ramp_event), + SND_SOC_DAPM_POST("Post Power Gain Ramp", da7219_gain_ramp_event), }; @@ -1002,8 +1098,8 @@ static const struct snd_soc_dapm_route da7219_audio_map[] = { {"Mixout Left PGA", NULL, "DACL"}, {"Mixout Right PGA", NULL, "DACR"}, - {"Headphone Left PGA", NULL, "Mixout Left PGA"}, - {"Headphone Right PGA", NULL, "Mixout Right PGA"}, + {"HPL", NULL, "Mixout Left PGA"}, + {"HPR", NULL, "Mixout Right PGA"}, {"HPL", NULL, "Headphone Left PGA"}, {"HPR", NULL, "Headphone Right PGA"}, @@ -1482,6 +1578,8 @@ static struct da7219_pdata *da7219_fw_to_pdata(struct snd_soc_codec *codec) if (!pdata) return NULL; + pdata->wakeup_source = device_property_read_bool(dev, "wakeup-source"); + if (device_property_read_u32(dev, "dlg,micbias-lvl", &of_val32) >= 0) pdata->micbias_lvl = da7219_fw_micbias_lvl(dev, of_val32); else @@ -1508,11 +1606,10 @@ static int da7219_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_ON: - case SND_SOC_BIAS_PREPARE: break; - case SND_SOC_BIAS_STANDBY: - if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { - /* MCLK */ + case SND_SOC_BIAS_PREPARE: + /* Enable MCLK for transition to ON state */ + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) { if (da7219->mclk) { ret = clk_prepare_enable(da7219->mclk); if (ret) { @@ -1521,22 +1618,28 @@ static int da7219_set_bias_level(struct snd_soc_codec *codec, return ret; } } + } + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) /* Master bias */ snd_soc_update_bits(codec, DA7219_REFERENCES, DA7219_BIAS_EN_MASK, DA7219_BIAS_EN_MASK); + + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE) { + /* Remove MCLK */ + if (da7219->mclk) + clk_disable_unprepare(da7219->mclk); } break; case SND_SOC_BIAS_OFF: - /* Only disable master bias if jack detection not active */ - if (!da7219->aad->jack) + /* Only disable master bias if we're not a wake-up source */ + if (!da7219->wakeup_source) snd_soc_update_bits(codec, DA7219_REFERENCES, DA7219_BIAS_EN_MASK, 0); - /* MCLK */ - if (da7219->mclk) - clk_disable_unprepare(da7219->mclk); break; } @@ -1599,6 +1702,8 @@ static void da7219_handle_pdata(struct snd_soc_codec *codec) if (pdata) { u8 micbias_lvl = 0; + da7219->wakeup_source = pdata->wakeup_source; + /* Mic Bias voltages */ switch (pdata->micbias_lvl) { case DA7219_MICBIAS_1_6V: @@ -1702,6 +1807,14 @@ static int da7219_probe(struct snd_soc_codec *codec) DA7219_HP_R_AMP_RAMP_EN_MASK, DA7219_HP_R_AMP_RAMP_EN_MASK); + /* Default minimum gain on HP to avoid pops during DAPM sequencing */ + snd_soc_update_bits(codec, DA7219_HP_L_CTRL, + DA7219_HP_L_AMP_MIN_GAIN_EN_MASK, + DA7219_HP_L_AMP_MIN_GAIN_EN_MASK); + snd_soc_update_bits(codec, DA7219_HP_R_CTRL, + DA7219_HP_R_AMP_MIN_GAIN_EN_MASK, + DA7219_HP_R_AMP_MIN_GAIN_EN_MASK); + /* Default infinite tone gen, start/stop by Kcontrol */ snd_soc_write(codec, DA7219_TONE_GEN_CYCLES, DA7219_BEEP_CYCLES_MASK); @@ -1733,11 +1846,11 @@ static int da7219_suspend(struct snd_soc_codec *codec) { struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); - snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); + /* Suspend AAD if we're not a wake-up source */ + if (!da7219->wakeup_source) + da7219_aad_suspend(codec); - /* Put device into standby mode if jack detection disabled */ - if (!da7219->aad->jack) - snd_soc_write(codec, DA7219_SYSTEM_ACTIVE, 0); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } @@ -1746,13 +1859,12 @@ static int da7219_resume(struct snd_soc_codec *codec) { struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); - /* Put device into active mode if previously pushed to standby */ - if (!da7219->aad->jack) - snd_soc_write(codec, DA7219_SYSTEM_ACTIVE, - DA7219_SYSTEM_ACTIVE_MASK); - snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); + /* Resume AAD if previously suspended */ + if (!da7219->wakeup_source) + da7219_aad_resume(codec); + return 0; } #else @@ -1767,13 +1879,14 @@ static struct snd_soc_codec_driver soc_codec_dev_da7219 = { .resume = da7219_resume, .set_bias_level = da7219_set_bias_level, - .controls = da7219_snd_controls, - .num_controls = ARRAY_SIZE(da7219_snd_controls), - - .dapm_widgets = da7219_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(da7219_dapm_widgets), - .dapm_routes = da7219_audio_map, - .num_dapm_routes = ARRAY_SIZE(da7219_audio_map), + .component_driver = { + .controls = da7219_snd_controls, + .num_controls = ARRAY_SIZE(da7219_snd_controls), + .dapm_widgets = da7219_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(da7219_dapm_widgets), + .dapm_routes = da7219_audio_map, + .num_dapm_routes = ARRAY_SIZE(da7219_audio_map), + }, }; @@ -1921,7 +2034,8 @@ static int da7219_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct da7219_priv *da7219; - int ret; + unsigned int system_active, system_status; + int i, ret; da7219 = devm_kzalloc(&i2c->dev, sizeof(struct da7219_priv), GFP_KERNEL); @@ -1937,6 +2051,37 @@ static int da7219_i2c_probe(struct i2c_client *i2c, return ret; } + regcache_cache_bypass(da7219->regmap, true); + + /* Disable audio paths if still active from previous start */ + regmap_read(da7219->regmap, DA7219_SYSTEM_ACTIVE, &system_active); + if (system_active) { + regmap_write(da7219->regmap, DA7219_GAIN_RAMP_CTRL, + DA7219_GAIN_RAMP_RATE_NOMINAL); + regmap_write(da7219->regmap, DA7219_SYSTEM_MODES_INPUT, 0x00); + regmap_write(da7219->regmap, DA7219_SYSTEM_MODES_OUTPUT, 0x01); + + for (i = 0; i < DA7219_SYS_STAT_CHECK_RETRIES; ++i) { + regmap_read(da7219->regmap, DA7219_SYSTEM_STATUS, + &system_status); + if (!system_status) + break; + + msleep(DA7219_SYS_STAT_CHECK_DELAY); + } + } + + /* Soft reset codec */ + regmap_write_bits(da7219->regmap, DA7219_ACCDET_CONFIG_1, + DA7219_ACCDET_EN_MASK, 0); + regmap_write_bits(da7219->regmap, DA7219_CIF_CTRL, + DA7219_CIF_REG_SOFT_RESET_MASK, + DA7219_CIF_REG_SOFT_RESET_MASK); + regmap_write_bits(da7219->regmap, DA7219_SYSTEM_ACTIVE, + DA7219_SYSTEM_ACTIVE_MASK, 0); + + regcache_cache_bypass(da7219->regmap, false); + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_da7219, &da7219_dai, 1); if (ret < 0) { diff --git a/sound/soc/codecs/da7219.h b/sound/soc/codecs/da7219.h index ff2a2f02ce40..6baba7455fa1 100644 --- a/sound/soc/codecs/da7219.h +++ b/sound/soc/codecs/da7219.h @@ -224,6 +224,7 @@ #define DA7219_PLL_SRM_STATE_MASK (0xF << 0) #define DA7219_PLL_SRM_STATUS_SHIFT 4 #define DA7219_PLL_SRM_STATUS_MASK (0xF << 4) +#define DA7219_PLL_SRM_STS_MCLK (0x1 << 4) #define DA7219_PLL_SRM_STS_SRM_LOCK (0x1 << 7) /* DA7219_DIG_ROUTING_DAI = 0x2A */ @@ -576,6 +577,8 @@ /* DA7219_GAIN_RAMP_CTRL = 0x92 */ #define DA7219_GAIN_RAMP_RATE_SHIFT 0 #define DA7219_GAIN_RAMP_RATE_MASK (0x3 << 0) +#define DA7219_GAIN_RAMP_RATE_X8 (0x0 << 0) +#define DA7219_GAIN_RAMP_RATE_NOMINAL (0x1 << 0) #define DA7219_GAIN_RAMP_RATE_MAX 4 /* DA7219_PC_COUNT = 0x94 */ @@ -770,6 +773,14 @@ /* SRM */ #define DA7219_SRM_CHECK_RETRIES 8 +/* System Controller */ +#define DA7219_SYS_STAT_CHECK_RETRIES 6 +#define DA7219_SYS_STAT_CHECK_DELAY 50 + +/* Power up/down Delays */ +#define DA7219_SETTLING_DELAY 40 +#define DA7219_MIN_GAIN_DELAY 30 + enum da7219_clk_src { DA7219_CLKSRC_MCLK = 0, DA7219_CLKSRC_MCLK_SQR, @@ -796,6 +807,7 @@ struct da7219_priv { struct da7219_aad_priv *aad; struct da7219_pdata *pdata; + bool wakeup_source; struct regulator_bulk_data supplies[DA7219_NUM_SUPPLIES]; struct regmap *regmap; struct mutex lock; @@ -806,6 +818,7 @@ struct da7219_priv { bool master; bool alc_en; + u8 gain_ramp_ctrl; }; #endif /* __DA7219_H */ diff --git a/sound/soc/codecs/da732x.c b/sound/soc/codecs/da732x.c index 461506a4ca6a..c1cc1c1c28f2 100644 --- a/sound/soc/codecs/da732x.c +++ b/sound/soc/codecs/da732x.c @@ -1501,12 +1501,14 @@ static int da732x_set_bias_level(struct snd_soc_codec *codec, static struct snd_soc_codec_driver soc_codec_dev_da732x = { .set_bias_level = da732x_set_bias_level, - .controls = da732x_snd_controls, - .num_controls = ARRAY_SIZE(da732x_snd_controls), - .dapm_widgets = da732x_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(da732x_dapm_widgets), - .dapm_routes = da732x_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(da732x_dapm_routes), + .component_driver = { + .controls = da732x_snd_controls, + .num_controls = ARRAY_SIZE(da732x_snd_controls), + .dapm_widgets = da732x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(da732x_dapm_widgets), + .dapm_routes = da732x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(da732x_dapm_routes), + }, .set_pll = da732x_set_dai_pll, }; diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c index 0b2ede8db978..4efb5f897a0c 100644 --- a/sound/soc/codecs/da9055.c +++ b/sound/soc/codecs/da9055.c @@ -1455,13 +1455,14 @@ static struct snd_soc_codec_driver soc_codec_dev_da9055 = { .probe = da9055_probe, .set_bias_level = da9055_set_bias_level, - .controls = da9055_snd_controls, - .num_controls = ARRAY_SIZE(da9055_snd_controls), - - .dapm_widgets = da9055_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(da9055_dapm_widgets), - .dapm_routes = da9055_audio_map, - .num_dapm_routes = ARRAY_SIZE(da9055_audio_map), + .component_driver = { + .controls = da9055_snd_controls, + .num_controls = ARRAY_SIZE(da9055_snd_controls), + .dapm_widgets = da9055_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(da9055_dapm_widgets), + .dapm_routes = da9055_audio_map, + .num_dapm_routes = ARRAY_SIZE(da9055_audio_map), + }, }; static const struct regmap_config da9055_regmap_config = { diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c index fde53251c047..c82b9dc41e9a 100644 --- a/sound/soc/codecs/dmic.c +++ b/sound/soc/codecs/dmic.c @@ -51,10 +51,12 @@ static const struct snd_soc_dapm_route intercon[] = { }; static struct snd_soc_codec_driver soc_dmic = { - .dapm_widgets = dmic_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(dmic_dapm_widgets), - .dapm_routes = intercon, - .num_dapm_routes = ARRAY_SIZE(intercon), + .component_driver = { + .dapm_widgets = dmic_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(dmic_dapm_widgets), + .dapm_routes = intercon, + .num_dapm_routes = ARRAY_SIZE(intercon), + }, }; static int dmic_dev_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index 2086d7107622..37722194b107 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -823,12 +823,14 @@ static struct snd_soc_codec_driver es8328_codec_driver = { .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), + .component_driver = { + .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) diff --git a/sound/soc/codecs/es8328.h b/sound/soc/codecs/es8328.h index 1a736e72a929..8930322d712b 100644 --- a/sound/soc/codecs/es8328.h +++ b/sound/soc/codecs/es8328.h @@ -278,43 +278,6 @@ int es8328_probe(struct device *dev, struct regmap *regmap); #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 diff --git a/sound/soc/codecs/gtm601.c b/sound/soc/codecs/gtm601.c index 0b80052996d3..926b1a4e37d4 100644 --- a/sound/soc/codecs/gtm601.c +++ b/sound/soc/codecs/gtm601.c @@ -52,10 +52,12 @@ static struct snd_soc_dai_driver gtm601_dai = { }; static const struct snd_soc_codec_driver soc_codec_dev_gtm601 = { - .dapm_widgets = gtm601_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(gtm601_dapm_widgets), - .dapm_routes = gtm601_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(gtm601_dapm_routes), + .component_driver = { + .dapm_widgets = gtm601_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(gtm601_dapm_widgets), + .dapm_routes = gtm601_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(gtm601_dapm_routes), + }, }; static int gtm601_platform_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 4e181b270d95..0c6228a0bf95 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -614,7 +614,7 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, (!pin->eld.eld_valid)) { dev_warn(&hdac->hdac.dev, - "Failed: montior present? %d ELD valid?: %d for pin: %d\n", + "Failed: monitor present? %d ELD valid?: %d for pin: %d\n", pin->eld.monitor_present, pin->eld.eld_valid, pin->nid); return 0; @@ -1368,7 +1368,7 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev, return hdac_hdmi_init_dai_map(edev); } -static void hdac_hdmi_eld_notify_cb(void *aptr, int port) +static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe) { struct hdac_ext_device *edev = aptr; struct hdac_hdmi_priv *hdmi = edev->private_data; diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index f27d115626db..90b5948e0ff3 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -24,6 +24,15 @@ #include <drm/drm_crtc.h> /* This is only to get MAX_ELD_BYTES */ +struct hdmi_device { + struct device *dev; + struct list_head list; + int cnt; +}; +#define pos_to_hdmi_device(pos) container_of((pos), struct hdmi_device, list) +LIST_HEAD(hdmi_device_list); + +#define DAI_NAME_SIZE 16 struct hdmi_codec_priv { struct hdmi_codec_pdata hcd; struct snd_soc_dai_driver *daidrv; @@ -320,7 +329,6 @@ static const struct snd_soc_dai_ops hdmi_dai_ops = { SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE) static struct snd_soc_dai_driver hdmi_i2s_dai = { - .name = "i2s-hifi", .id = DAI_ID_I2S, .playback = { .stream_name = "Playback", @@ -334,7 +342,6 @@ static struct snd_soc_dai_driver hdmi_i2s_dai = { }; static const struct snd_soc_dai_driver hdmi_spdif_dai = { - .name = "spdif-hifi", .id = DAI_ID_SPDIF, .playback = { .stream_name = "Playback", @@ -346,13 +353,42 @@ static const struct snd_soc_dai_driver hdmi_spdif_dai = { .ops = &hdmi_dai_ops, }; +static char hdmi_dai_name[][DAI_NAME_SIZE] = { + "hdmi-hifi.0", + "hdmi-hifi.1", + "hdmi-hifi.2", + "hdmi-hifi.3", +}; + +static int hdmi_of_xlate_dai_name(struct snd_soc_component *component, + struct of_phandle_args *args, + const char **dai_name) +{ + int id; + + if (args->args_count) + id = args->args[0]; + else + id = 0; + + if (id < ARRAY_SIZE(hdmi_dai_name)) { + *dai_name = hdmi_dai_name[id]; + return 0; + } + + return -EAGAIN; +} + static struct snd_soc_codec_driver hdmi_codec = { - .controls = hdmi_controls, - .num_controls = ARRAY_SIZE(hdmi_controls), - .dapm_widgets = hdmi_widgets, - .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets), - .dapm_routes = hdmi_routes, - .num_dapm_routes = ARRAY_SIZE(hdmi_routes), + .component_driver = { + .controls = hdmi_controls, + .num_controls = ARRAY_SIZE(hdmi_controls), + .dapm_widgets = hdmi_widgets, + .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets), + .dapm_routes = hdmi_routes, + .num_dapm_routes = ARRAY_SIZE(hdmi_routes), + .of_xlate_dai_name = hdmi_of_xlate_dai_name, + }, }; static int hdmi_codec_probe(struct platform_device *pdev) @@ -360,6 +396,8 @@ static int hdmi_codec_probe(struct platform_device *pdev) struct hdmi_codec_pdata *hcd = pdev->dev.platform_data; struct device *dev = &pdev->dev; struct hdmi_codec_priv *hcp; + struct hdmi_device *hd; + struct list_head *pos; int dai_count, i = 0; int ret; @@ -381,6 +419,31 @@ static int hdmi_codec_probe(struct platform_device *pdev) if (!hcp) return -ENOMEM; + hd = NULL; + list_for_each(pos, &hdmi_device_list) { + struct hdmi_device *tmp = pos_to_hdmi_device(pos); + + if (tmp->dev == dev->parent) { + hd = tmp; + break; + } + } + + if (!hd) { + hd = devm_kzalloc(dev, sizeof(*hd), GFP_KERNEL); + if (!hd) + return -ENOMEM; + + hd->dev = dev->parent; + + list_add_tail(&hd->list, &hdmi_device_list); + } + + if (hd->cnt >= ARRAY_SIZE(hdmi_dai_name)) { + dev_err(dev, "too many hdmi codec are deteced\n"); + return -EINVAL; + } + hcp->hcd = *hcd; mutex_init(&hcp->current_stream_lock); @@ -393,11 +456,14 @@ static int hdmi_codec_probe(struct platform_device *pdev) hcp->daidrv[i] = hdmi_i2s_dai; hcp->daidrv[i].playback.channels_max = hcd->max_i2s_channels; + hcp->daidrv[i].name = hdmi_dai_name[hd->cnt++]; i++; } - if (hcd->spdif) + if (hcd->spdif) { hcp->daidrv[i] = hdmi_spdif_dai; + hcp->daidrv[i].name = hdmi_dai_name[hd->cnt++]; + } ret = snd_soc_register_codec(dev, &hdmi_codec, hcp->daidrv, dai_count); diff --git a/sound/soc/codecs/inno_rk3036.c b/sound/soc/codecs/inno_rk3036.c index 9b6e8840a1b5..b918ba5c8ce5 100644 --- a/sound/soc/codecs/inno_rk3036.c +++ b/sound/soc/codecs/inno_rk3036.c @@ -380,12 +380,14 @@ static struct snd_soc_codec_driver rk3036_codec_driver = { .probe = rk3036_codec_probe, .remove = rk3036_codec_remove, .set_bias_level = rk3036_codec_set_bias_level, - .controls = rk3036_codec_dapm_controls, - .num_controls = ARRAY_SIZE(rk3036_codec_dapm_controls), - .dapm_routes = rk3036_codec_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(rk3036_codec_dapm_routes), - .dapm_widgets = rk3036_codec_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(rk3036_codec_dapm_widgets), + .component_driver = { + .controls = rk3036_codec_dapm_controls, + .num_controls = ARRAY_SIZE(rk3036_codec_dapm_controls), + .dapm_routes = rk3036_codec_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rk3036_codec_dapm_routes), + .dapm_widgets = rk3036_codec_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rk3036_codec_dapm_widgets), + }, }; static const struct regmap_config rk3036_codec_regmap_config = { diff --git a/sound/soc/codecs/isabelle.c b/sound/soc/codecs/isabelle.c index be448373d39a..a4b0eded984a 100644 --- a/sound/soc/codecs/isabelle.c +++ b/sound/soc/codecs/isabelle.c @@ -1089,12 +1089,14 @@ static struct snd_soc_dai_driver isabelle_dai[] = { static struct snd_soc_codec_driver soc_codec_dev_isabelle = { .set_bias_level = isabelle_set_bias_level, - .controls = isabelle_snd_controls, - .num_controls = ARRAY_SIZE(isabelle_snd_controls), - .dapm_widgets = isabelle_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(isabelle_dapm_widgets), - .dapm_routes = isabelle_intercon, - .num_dapm_routes = ARRAY_SIZE(isabelle_intercon), + .component_driver = { + .controls = isabelle_snd_controls, + .num_controls = ARRAY_SIZE(isabelle_snd_controls), + .dapm_widgets = isabelle_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(isabelle_dapm_widgets), + .dapm_routes = isabelle_intercon, + .num_dapm_routes = ARRAY_SIZE(isabelle_intercon), + }, .idle_bias_off = true, }; diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c index 1f5ab99956ed..0290fab383da 100644 --- a/sound/soc/codecs/jz4740.c +++ b/sound/soc/codecs/jz4740.c @@ -298,12 +298,14 @@ static struct snd_soc_codec_driver soc_codec_dev_jz4740_codec = { .set_bias_level = jz4740_codec_set_bias_level, .suspend_bias_off = true, - .controls = jz4740_codec_controls, - .num_controls = ARRAY_SIZE(jz4740_codec_controls), - .dapm_widgets = jz4740_codec_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(jz4740_codec_dapm_widgets), - .dapm_routes = jz4740_codec_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(jz4740_codec_dapm_routes), + .component_driver = { + .controls = jz4740_codec_controls, + .num_controls = ARRAY_SIZE(jz4740_codec_controls), + .dapm_widgets = jz4740_codec_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(jz4740_codec_dapm_widgets), + .dapm_routes = jz4740_codec_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(jz4740_codec_dapm_routes), + }, }; static const struct regmap_config jz4740_codec_regmap_config = { diff --git a/sound/soc/codecs/l3.c b/sound/soc/codecs/l3.c index 5353af58862c..a10ea3c716c6 100644 --- a/sound/soc/codecs/l3.c +++ b/sound/soc/codecs/l3.c @@ -20,6 +20,8 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/delay.h> +#include <linux/device.h> +#include <linux/gpio.h> #include <sound/l3.h> @@ -32,11 +34,11 @@ static void sendbyte(struct l3_pins *adap, unsigned int byte) int i; for (i = 0; i < 8; i++) { - adap->setclk(0); + adap->setclk(adap, 0); udelay(adap->data_hold); - adap->setdat(byte & 1); + adap->setdat(adap, byte & 1); udelay(adap->data_setup); - adap->setclk(1); + adap->setclk(adap, 1); udelay(adap->clock_high); byte >>= 1; } @@ -55,10 +57,10 @@ static void sendbytes(struct l3_pins *adap, const u8 *buf, for (i = 0; i < len; i++) { if (i) { udelay(adap->mode_hold); - adap->setmode(0); + adap->setmode(adap, 0); udelay(adap->mode); } - adap->setmode(1); + adap->setmode(adap, 1); udelay(adap->mode_setup); sendbyte(adap, buf[i]); } @@ -66,26 +68,71 @@ static void sendbytes(struct l3_pins *adap, const u8 *buf, int l3_write(struct l3_pins *adap, u8 addr, u8 *data, int len) { - adap->setclk(1); - adap->setdat(1); - adap->setmode(1); + adap->setclk(adap, 1); + adap->setdat(adap, 1); + adap->setmode(adap, 1); udelay(adap->mode); - adap->setmode(0); + adap->setmode(adap, 0); udelay(adap->mode_setup); sendbyte(adap, addr); udelay(adap->mode_hold); sendbytes(adap, data, len); - adap->setclk(1); - adap->setdat(1); - adap->setmode(0); + adap->setclk(adap, 1); + adap->setdat(adap, 1); + adap->setmode(adap, 0); return len; } EXPORT_SYMBOL_GPL(l3_write); + +static void l3_set_clk(struct l3_pins *adap, int val) +{ + gpio_set_value(adap->gpio_clk, val); +} + +static void l3_set_data(struct l3_pins *adap, int val) +{ + gpio_set_value(adap->gpio_data, val); +} + +static void l3_set_mode(struct l3_pins *adap, int val) +{ + gpio_set_value(adap->gpio_mode, val); +} + +int l3_set_gpio_ops(struct device *dev, struct l3_pins *adap) +{ + int ret; + + if (!adap->use_gpios) + return -EINVAL; + + ret = devm_gpio_request_one(dev, adap->gpio_data, + GPIOF_OUT_INIT_LOW, "l3_data"); + if (ret < 0) + return ret; + adap->setdat = l3_set_data; + + ret = devm_gpio_request_one(dev, adap->gpio_clk, + GPIOF_OUT_INIT_LOW, "l3_clk"); + if (ret < 0) + return ret; + adap->setclk = l3_set_clk; + + ret = devm_gpio_request_one(dev, adap->gpio_mode, + GPIOF_OUT_INIT_LOW, "l3_mode"); + if (ret < 0) + return ret; + adap->setmode = l3_set_mode; + + return 0; +} +EXPORT_SYMBOL_GPL(l3_set_gpio_ops); + MODULE_DESCRIPTION("L3 bit-banging driver"); MODULE_AUTHOR("Christian Pellegrin <chripell@evolware.org>"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c index 9af5640e3446..8d413c2677cc 100644 --- a/sound/soc/codecs/lm49453.c +++ b/sound/soc/codecs/lm49453.c @@ -1391,12 +1391,14 @@ static struct snd_soc_dai_driver lm49453_dai[] = { static struct snd_soc_codec_driver soc_codec_dev_lm49453 = { .set_bias_level = lm49453_set_bias_level, - .controls = lm49453_snd_controls, - .num_controls = ARRAY_SIZE(lm49453_snd_controls), - .dapm_widgets = lm49453_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(lm49453_dapm_widgets), - .dapm_routes = lm49453_audio_map, - .num_dapm_routes = ARRAY_SIZE(lm49453_audio_map), + .component_driver = { + .controls = lm49453_snd_controls, + .num_controls = ARRAY_SIZE(lm49453_snd_controls), + .dapm_widgets = lm49453_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(lm49453_dapm_widgets), + .dapm_routes = lm49453_audio_map, + .num_dapm_routes = ARRAY_SIZE(lm49453_audio_map), + }, .idle_bias_off = true, }; diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index fc22804cabc5..72f77455582e 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -1704,12 +1704,14 @@ static struct snd_soc_codec_driver soc_codec_dev_max98088 = { .set_bias_level = max98088_set_bias_level, .suspend_bias_off = true, - .controls = max98088_snd_controls, - .num_controls = ARRAY_SIZE(max98088_snd_controls), - .dapm_widgets = max98088_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(max98088_dapm_widgets), - .dapm_routes = max98088_audio_map, - .num_dapm_routes = ARRAY_SIZE(max98088_audio_map), + .component_driver = { + .controls = max98088_snd_controls, + .num_controls = ARRAY_SIZE(max98088_snd_controls), + .dapm_widgets = max98088_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98088_dapm_widgets), + .dapm_routes = max98088_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98088_audio_map), + }, }; static int max98088_i2c_probe(struct i2c_client *i2c, diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index 3577003f39cf..6f8a757876ed 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -2108,12 +2108,14 @@ static struct snd_soc_codec_driver soc_codec_dev_max98095 = { .suspend = max98095_suspend, .resume = max98095_resume, .set_bias_level = max98095_set_bias_level, - .controls = max98095_snd_controls, - .num_controls = ARRAY_SIZE(max98095_snd_controls), - .dapm_widgets = max98095_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(max98095_dapm_widgets), - .dapm_routes = max98095_audio_map, - .num_dapm_routes = ARRAY_SIZE(max98095_audio_map), + .component_driver = { + .controls = max98095_snd_controls, + .num_controls = ARRAY_SIZE(max98095_snd_controls), + .dapm_widgets = max98095_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98095_dapm_widgets), + .dapm_routes = max98095_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98095_audio_map), + }, }; static int max98095_i2c_probe(struct i2c_client *i2c, diff --git a/sound/soc/codecs/max98357a.c b/sound/soc/codecs/max98357a.c index 5b1dfb1518fb..6a6b68a4cb52 100644 --- a/sound/soc/codecs/max98357a.c +++ b/sound/soc/codecs/max98357a.c @@ -74,10 +74,12 @@ static int max98357a_codec_probe(struct snd_soc_codec *codec) static struct snd_soc_codec_driver max98357a_codec_driver = { .probe = max98357a_codec_probe, - .dapm_widgets = max98357a_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(max98357a_dapm_widgets), - .dapm_routes = max98357a_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(max98357a_dapm_routes), + .component_driver = { + .dapm_widgets = max98357a_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98357a_dapm_widgets), + .dapm_routes = max98357a_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(max98357a_dapm_routes), + }, }; static const struct snd_soc_dai_ops max98357a_dai_ops = { diff --git a/sound/soc/codecs/max98371.c b/sound/soc/codecs/max98371.c index 02352ed8961c..781be9ba8dba 100644 --- a/sound/soc/codecs/max98371.c +++ b/sound/soc/codecs/max98371.c @@ -426,7 +426,6 @@ MODULE_DEVICE_TABLE(of, max98371_of_match); static struct i2c_driver max98371_i2c_driver = { .driver = { .name = "max98371", - .owner = THIS_MODULE, .pm = NULL, .of_match_table = of_match_ptr(max98371_of_match), }, diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c index c14a79d026a1..0610840733d1 100644 --- a/sound/soc/codecs/max9850.c +++ b/sound/soc/codecs/max9850.c @@ -306,12 +306,14 @@ static struct snd_soc_codec_driver soc_codec_dev_max9850 = { .set_bias_level = max9850_set_bias_level, .suspend_bias_off = true, - .controls = max9850_controls, - .num_controls = ARRAY_SIZE(max9850_controls), - .dapm_widgets = max9850_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(max9850_dapm_widgets), - .dapm_routes = max9850_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(max9850_dapm_routes), + .component_driver = { + .controls = max9850_controls, + .num_controls = ARRAY_SIZE(max9850_controls), + .dapm_widgets = max9850_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max9850_dapm_widgets), + .dapm_routes = max9850_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(max9850_dapm_routes), + }, }; static int max9850_i2c_probe(struct i2c_client *i2c, diff --git a/sound/soc/codecs/max9860.c b/sound/soc/codecs/max9860.c index 68074c92a7c0..499bdbfd0a2d 100644 --- a/sound/soc/codecs/max9860.c +++ b/sound/soc/codecs/max9860.c @@ -538,12 +538,14 @@ static struct snd_soc_codec_driver max9860_codec_driver = { .set_bias_level = max9860_set_bias_level, .idle_bias_off = true, - .controls = max9860_controls, - .num_controls = ARRAY_SIZE(max9860_controls), - .dapm_widgets = max9860_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(max9860_dapm_widgets), - .dapm_routes = max9860_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(max9860_dapm_routes), + .component_driver = { + .controls = max9860_controls, + .num_controls = ARRAY_SIZE(max9860_controls), + .dapm_widgets = max9860_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max9860_dapm_widgets), + .dapm_routes = max9860_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(max9860_dapm_routes), + }, }; #ifdef CONFIG_PM diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c index 2a22fddeb6af..42e2e407e287 100644 --- a/sound/soc/codecs/max9867.c +++ b/sound/soc/codecs/max9867.c @@ -38,11 +38,10 @@ static DECLARE_TLV_DB_SCALE(max9860_capture_tlv, -600, 200, 0); static DECLARE_TLV_DB_SCALE(max9860_mic_tlv, 2000, 100, 1); static DECLARE_TLV_DB_SCALE(max9860_adc_left_tlv, -1200, 100, 1); static DECLARE_TLV_DB_SCALE(max9860_adc_right_tlv, -1200, 100, 1); -static const unsigned int max98088_micboost_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(max98088_micboost_tlv, 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0), 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0), -}; +); static const struct snd_kcontrol_new max9867_snd_controls[] = { SOC_DOUBLE_R("Master Playback Volume", MAX9867_LEFTVOL, @@ -417,12 +416,14 @@ static int max9867_probe(struct snd_soc_codec *codec) static struct snd_soc_codec_driver max9867_codec = { .probe = max9867_probe, - .controls = max9867_snd_controls, - .num_controls = ARRAY_SIZE(max9867_snd_controls), - .dapm_routes = max9867_audio_map, - .num_dapm_routes = ARRAY_SIZE(max9867_audio_map), - .dapm_widgets = max9867_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(max9867_dapm_widgets), + .component_driver = { + .controls = max9867_snd_controls, + .num_controls = ARRAY_SIZE(max9867_snd_controls), + .dapm_routes = max9867_audio_map, + .num_dapm_routes = ARRAY_SIZE(max9867_audio_map), + .dapm_widgets = max9867_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max9867_dapm_widgets), + }, }; static bool max9867_volatile_register(struct device *dev, unsigned int reg) diff --git a/sound/soc/codecs/max98925.c b/sound/soc/codecs/max98925.c index 5990de317999..327eaa25c9bd 100644 --- a/sound/soc/codecs/max98925.c +++ b/sound/soc/codecs/max98925.c @@ -540,12 +540,14 @@ static int max98925_probe(struct snd_soc_codec *codec) static const struct snd_soc_codec_driver soc_codec_dev_max98925 = { .probe = max98925_probe, - .controls = max98925_snd_controls, - .num_controls = ARRAY_SIZE(max98925_snd_controls), - .dapm_routes = max98925_audio_map, - .num_dapm_routes = ARRAY_SIZE(max98925_audio_map), - .dapm_widgets = max98925_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(max98925_dapm_widgets), + .component_driver = { + .controls = max98925_snd_controls, + .num_controls = ARRAY_SIZE(max98925_snd_controls), + .dapm_routes = max98925_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98925_audio_map), + .dapm_widgets = max98925_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98925_dapm_widgets), + }, }; static const struct regmap_config max98925_regmap = { diff --git a/sound/soc/codecs/max98926.c b/sound/soc/codecs/max98926.c index 8d14adae5cc5..1eff7e0b092e 100644 --- a/sound/soc/codecs/max98926.c +++ b/sound/soc/codecs/max98926.c @@ -347,7 +347,7 @@ static int max98926_dai_set_fmt(struct snd_soc_dai *codec_dai, max98926_set_sense_data(max98926); break; default: - dev_err(codec->dev, "DAI clock mode unsupported"); + dev_err(codec->dev, "DAI clock mode unsupported\n"); return -EINVAL; } @@ -364,7 +364,7 @@ static int max98926_dai_set_fmt(struct snd_soc_dai *codec_dai, invert = MAX98926_DAI_BCI_MASK | MAX98926_DAI_WCI_MASK; break; default: - dev_err(codec->dev, "DAI invert mode unsupported"); + dev_err(codec->dev, "DAI invert mode unsupported\n"); return -EINVAL; } @@ -408,7 +408,7 @@ static int max98926_dai_hw_params(struct snd_pcm_substream *substream, max98926->ch_size = 32; break; default: - dev_dbg(codec->dev, "format unsupported %d", + dev_dbg(codec->dev, "format unsupported %d\n", params_format(params)); return -EINVAL; } @@ -498,12 +498,14 @@ static int max98926_probe(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_dev_max98926 = { .probe = max98926_probe, - .controls = max98926_snd_controls, - .num_controls = ARRAY_SIZE(max98926_snd_controls), - .dapm_routes = max98926_audio_map, - .num_dapm_routes = ARRAY_SIZE(max98926_audio_map), - .dapm_widgets = max98926_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(max98926_dapm_widgets), + .component_driver = { + .controls = max98926_snd_controls, + .num_controls = ARRAY_SIZE(max98926_snd_controls), + .dapm_routes = max98926_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98926_audio_map), + .dapm_widgets = max98926_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98926_dapm_widgets), + }, }; static const struct regmap_config max98926_regmap = { diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c index 3e770cbe7f0f..90562703dcfd 100644 --- a/sound/soc/codecs/mc13783.c +++ b/sound/soc/codecs/mc13783.c @@ -737,12 +737,14 @@ static struct snd_soc_codec_driver soc_codec_dev_mc13783 = { .probe = mc13783_probe, .remove = mc13783_remove, .get_regmap = mc13783_get_regmap, - .controls = mc13783_control_list, - .num_controls = ARRAY_SIZE(mc13783_control_list), - .dapm_widgets = mc13783_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(mc13783_dapm_widgets), - .dapm_routes = mc13783_routes, - .num_dapm_routes = ARRAY_SIZE(mc13783_routes), + .component_driver = { + .controls = mc13783_control_list, + .num_controls = ARRAY_SIZE(mc13783_control_list), + .dapm_widgets = mc13783_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mc13783_dapm_widgets), + .dapm_routes = mc13783_routes, + .num_dapm_routes = ARRAY_SIZE(mc13783_routes), + }, }; static int __init mc13783_codec_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c index f561c78b9e0e..69e5e18880c5 100644 --- a/sound/soc/codecs/ml26124.c +++ b/sound/soc/codecs/ml26124.c @@ -541,12 +541,14 @@ static struct snd_soc_codec_driver soc_codec_dev_ml26124 = { .probe = ml26124_probe, .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, - .num_dapm_routes = ARRAY_SIZE(ml26124_intercon), - .controls = ml26124_snd_controls, - .num_controls = ARRAY_SIZE(ml26124_snd_controls), + .component_driver = { + .controls = ml26124_snd_controls, + .num_controls = ARRAY_SIZE(ml26124_snd_controls), + .dapm_widgets = ml26124_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ml26124_dapm_widgets), + .dapm_routes = ml26124_intercon, + .num_dapm_routes = ARRAY_SIZE(ml26124_intercon), + }, }; static const struct regmap_config ml26124_i2c_regmap = { diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c new file mode 100644 index 000000000000..d8e8590746af --- /dev/null +++ b/sound/soc/codecs/msm8916-wcd-analog.c @@ -0,0 +1,890 @@ +#include <linux/module.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/regulator/consumer.h> +#include <linux/types.h> +#include <linux/clk.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/tlv.h> + +#define CDC_D_REVISION1 (0xf000) +#define CDC_D_PERPH_SUBTYPE (0xf005) +#define CDC_D_CDC_RST_CTL (0xf046) +#define RST_CTL_DIG_SW_RST_N_MASK BIT(7) +#define RST_CTL_DIG_SW_RST_N_RESET 0 +#define RST_CTL_DIG_SW_RST_N_REMOVE_RESET BIT(7) + +#define CDC_D_CDC_TOP_CLK_CTL (0xf048) +#define TOP_CLK_CTL_A_MCLK_MCLK2_EN_MASK (BIT(2) | BIT(3)) +#define TOP_CLK_CTL_A_MCLK_EN_ENABLE BIT(2) +#define TOP_CLK_CTL_A_MCLK2_EN_ENABLE BIT(3) + +#define CDC_D_CDC_ANA_CLK_CTL (0xf049) +#define ANA_CLK_CTL_EAR_HPHR_CLK_EN_MASK BIT(0) +#define ANA_CLK_CTL_EAR_HPHR_CLK_EN BIT(0) +#define ANA_CLK_CTL_EAR_HPHL_CLK_EN BIT(1) +#define ANA_CLK_CTL_SPKR_CLK_EN_MASK BIT(4) +#define ANA_CLK_CTL_SPKR_CLK_EN BIT(4) +#define ANA_CLK_CTL_TXA_CLK25_EN BIT(5) + +#define CDC_D_CDC_DIG_CLK_CTL (0xf04A) +#define DIG_CLK_CTL_RXD1_CLK_EN BIT(0) +#define DIG_CLK_CTL_RXD2_CLK_EN BIT(1) +#define DIG_CLK_CTL_RXD3_CLK_EN BIT(3) +#define DIG_CLK_CTL_TXD_CLK_EN BIT(4) +#define DIG_CLK_CTL_NCP_CLK_EN_MASK BIT(6) +#define DIG_CLK_CTL_NCP_CLK_EN BIT(6) +#define DIG_CLK_CTL_RXD_PDM_CLK_EN_MASK BIT(7) +#define DIG_CLK_CTL_RXD_PDM_CLK_EN BIT(7) + +#define CDC_D_CDC_CONN_TX1_CTL (0xf050) +#define CONN_TX1_SERIAL_TX1_MUX GENMASK(1, 0) +#define CONN_TX1_SERIAL_TX1_ADC_1 0x0 +#define CONN_TX1_SERIAL_TX1_RX_PDM_LB 0x1 +#define CONN_TX1_SERIAL_TX1_ZERO 0x2 + +#define CDC_D_CDC_CONN_TX2_CTL (0xf051) +#define CONN_TX2_SERIAL_TX2_MUX GENMASK(1, 0) +#define CONN_TX2_SERIAL_TX2_ADC_2 0x0 +#define CONN_TX2_SERIAL_TX2_RX_PDM_LB 0x1 +#define CONN_TX2_SERIAL_TX2_ZERO 0x2 +#define CDC_D_CDC_CONN_HPHR_DAC_CTL (0xf052) +#define CDC_D_CDC_CONN_RX1_CTL (0xf053) +#define CDC_D_CDC_CONN_RX2_CTL (0xf054) +#define CDC_D_CDC_CONN_RX3_CTL (0xf055) +#define CDC_D_CDC_CONN_RX_LB_CTL (0xf056) +#define CDC_D_SEC_ACCESS (0xf0D0) +#define CDC_D_PERPH_RESET_CTL3 (0xf0DA) +#define CDC_D_PERPH_RESET_CTL4 (0xf0DB) +#define CDC_A_REVISION1 (0xf100) +#define CDC_A_REVISION2 (0xf101) +#define CDC_A_REVISION3 (0xf102) +#define CDC_A_REVISION4 (0xf103) +#define CDC_A_PERPH_TYPE (0xf104) +#define CDC_A_PERPH_SUBTYPE (0xf105) +#define CDC_A_INT_RT_STS (0xf110) +#define CDC_A_INT_SET_TYPE (0xf111) +#define CDC_A_INT_POLARITY_HIGH (0xf112) +#define CDC_A_INT_POLARITY_LOW (0xf113) +#define CDC_A_INT_LATCHED_CLR (0xf114) +#define CDC_A_INT_EN_SET (0xf115) +#define CDC_A_INT_EN_CLR (0xf116) +#define CDC_A_INT_LATCHED_STS (0xf118) +#define CDC_A_INT_PENDING_STS (0xf119) +#define CDC_A_INT_MID_SEL (0xf11A) +#define CDC_A_INT_PRIORITY (0xf11B) +#define CDC_A_MICB_1_EN (0xf140) +#define MICB_1_EN_MICB_ENABLE BIT(7) +#define MICB_1_EN_BYP_CAP_MASK BIT(6) +#define MICB_1_EN_NO_EXT_BYP_CAP BIT(6) +#define MICB_1_EN_EXT_BYP_CAP 0 +#define MICB_1_EN_PULL_DOWN_EN_MASK BIT(5) +#define MICB_1_EN_PULL_DOWN_EN_ENABLE BIT(5) +#define MICB_1_EN_OPA_STG2_TAIL_CURR_MASK GENMASK(3, 1) +#define MICB_1_EN_OPA_STG2_TAIL_CURR_1_60UA (0x4) +#define MICB_1_EN_PULL_UP_EN_MASK BIT(4) +#define MICB_1_EN_TX3_GND_SEL_MASK BIT(0) +#define MICB_1_EN_TX3_GND_SEL_TX_GND 0 + +#define CDC_A_MICB_1_VAL (0xf141) +#define MICB_1_VAL_MICB_OUT_VAL_MASK GENMASK(7, 3) +#define MICB_1_VAL_MICB_OUT_VAL_V2P70V ((0x16) << 3) +#define CDC_A_MICB_1_CTL (0xf142) + +#define MICB_1_CTL_CFILT_REF_SEL_MASK BIT(1) +#define MICB_1_CTL_CFILT_REF_SEL_HPF_REF BIT(1) +#define MICB_1_CTL_EXT_PRECHARG_EN_MASK BIT(5) +#define MICB_1_CTL_EXT_PRECHARG_EN_ENABLE BIT(5) +#define MICB_1_CTL_INT_PRECHARG_BYP_MASK BIT(6) +#define MICB_1_CTL_INT_PRECHARG_BYP_EXT_PRECHRG_SEL BIT(6) + +#define CDC_A_MICB_1_INT_RBIAS (0xf143) +#define MICB_1_INT_TX1_INT_RBIAS_EN_MASK BIT(7) +#define MICB_1_INT_TX1_INT_RBIAS_EN_ENABLE BIT(7) +#define MICB_1_INT_TX1_INT_RBIAS_EN_DISABLE 0 + +#define MICB_1_INT_TX1_INT_PULLUP_EN_MASK BIT(6) +#define MICB_1_INT_TX1_INT_PULLUP_EN_TX1N_TO_MICBIAS BIT(6) +#define MICB_1_INT_TX1_INT_PULLUP_EN_TX1N_TO_GND 0 + +#define MICB_1_INT_TX2_INT_RBIAS_EN_MASK BIT(4) +#define MICB_1_INT_TX2_INT_RBIAS_EN_ENABLE BIT(4) +#define MICB_1_INT_TX2_INT_RBIAS_EN_DISABLE 0 +#define MICB_1_INT_TX2_INT_PULLUP_EN_MASK BIT(3) +#define MICB_1_INT_TX2_INT_PULLUP_EN_TX1N_TO_MICBIAS BIT(3) +#define MICB_1_INT_TX2_INT_PULLUP_EN_TX1N_TO_GND 0 + +#define MICB_1_INT_TX3_INT_RBIAS_EN_MASK BIT(1) +#define MICB_1_INT_TX3_INT_RBIAS_EN_ENABLE BIT(1) +#define MICB_1_INT_TX3_INT_RBIAS_EN_DISABLE 0 +#define MICB_1_INT_TX3_INT_PULLUP_EN_MASK BIT(0) +#define MICB_1_INT_TX3_INT_PULLUP_EN_TX1N_TO_MICBIAS BIT(0) +#define MICB_1_INT_TX3_INT_PULLUP_EN_TX1N_TO_GND 0 + +#define CDC_A_MICB_2_EN (0xf144) +#define CDC_A_TX_1_2_ATEST_CTL_2 (0xf145) +#define CDC_A_MASTER_BIAS_CTL (0xf146) +#define CDC_A_TX_1_EN (0xf160) +#define CDC_A_TX_2_EN (0xf161) +#define CDC_A_TX_1_2_TEST_CTL_1 (0xf162) +#define CDC_A_TX_1_2_TEST_CTL_2 (0xf163) +#define CDC_A_TX_1_2_ATEST_CTL (0xf164) +#define CDC_A_TX_1_2_OPAMP_BIAS (0xf165) +#define CDC_A_TX_3_EN (0xf167) +#define CDC_A_NCP_EN (0xf180) +#define CDC_A_NCP_CLK (0xf181) +#define CDC_A_NCP_FBCTRL (0xf183) +#define CDC_A_NCP_FBCTRL_FB_CLK_INV_MASK BIT(5) +#define CDC_A_NCP_FBCTRL_FB_CLK_INV BIT(5) +#define CDC_A_NCP_BIAS (0xf184) +#define CDC_A_NCP_VCTRL (0xf185) +#define CDC_A_NCP_TEST (0xf186) +#define CDC_A_NCP_CLIM_ADDR (0xf187) +#define CDC_A_RX_CLOCK_DIVIDER (0xf190) +#define CDC_A_RX_COM_OCP_CTL (0xf191) +#define CDC_A_RX_COM_OCP_COUNT (0xf192) +#define CDC_A_RX_COM_BIAS_DAC (0xf193) +#define RX_COM_BIAS_DAC_RX_BIAS_EN_MASK BIT(7) +#define RX_COM_BIAS_DAC_RX_BIAS_EN_ENABLE BIT(7) +#define RX_COM_BIAS_DAC_DAC_REF_EN_MASK BIT(0) +#define RX_COM_BIAS_DAC_DAC_REF_EN_ENABLE BIT(0) + +#define CDC_A_RX_HPH_BIAS_PA (0xf194) +#define CDC_A_RX_HPH_BIAS_LDO_OCP (0xf195) +#define CDC_A_RX_HPH_BIAS_CNP (0xf196) +#define CDC_A_RX_HPH_CNP_EN (0xf197) +#define CDC_A_RX_HPH_L_PA_DAC_CTL (0xf19B) +#define RX_HPA_L_PA_DAC_CTL_DATA_RESET_MASK BIT(1) +#define RX_HPA_L_PA_DAC_CTL_DATA_RESET_RESET BIT(1) +#define CDC_A_RX_HPH_R_PA_DAC_CTL (0xf19D) +#define RX_HPH_R_PA_DAC_CTL_DATA_RESET BIT(1) +#define RX_HPH_R_PA_DAC_CTL_DATA_RESET_MASK BIT(1) + +#define CDC_A_RX_EAR_CTL (0xf19E) +#define RX_EAR_CTL_SPK_VBAT_LDO_EN_MASK BIT(0) +#define RX_EAR_CTL_SPK_VBAT_LDO_EN_ENABLE BIT(0) + +#define CDC_A_SPKR_DAC_CTL (0xf1B0) +#define SPKR_DAC_CTL_DAC_RESET_MASK BIT(4) +#define SPKR_DAC_CTL_DAC_RESET_NORMAL 0 + +#define CDC_A_SPKR_DRV_CTL (0xf1B2) +#define SPKR_DRV_CTL_DEF_MASK 0xEF +#define SPKR_DRV_CLASSD_PA_EN_MASK BIT(7) +#define SPKR_DRV_CLASSD_PA_EN_ENABLE BIT(7) +#define SPKR_DRV_CAL_EN BIT(6) +#define SPKR_DRV_SETTLE_EN BIT(5) +#define SPKR_DRV_FW_EN BIT(3) +#define SPKR_DRV_BOOST_SET BIT(2) +#define SPKR_DRV_CMFB_SET BIT(1) +#define SPKR_DRV_GAIN_SET BIT(0) +#define SPKR_DRV_CTL_DEF_VAL (SPKR_DRV_CLASSD_PA_EN_ENABLE | \ + SPKR_DRV_CAL_EN | SPKR_DRV_SETTLE_EN | \ + SPKR_DRV_FW_EN | SPKR_DRV_BOOST_SET | \ + SPKR_DRV_CMFB_SET | SPKR_DRV_GAIN_SET) +#define CDC_A_SPKR_OCP_CTL (0xf1B4) +#define CDC_A_SPKR_PWRSTG_CTL (0xf1B5) +#define SPKR_PWRSTG_CTL_DAC_EN_MASK BIT(0) +#define SPKR_PWRSTG_CTL_DAC_EN BIT(0) +#define SPKR_PWRSTG_CTL_MASK 0xE0 +#define SPKR_PWRSTG_CTL_BBM_MASK BIT(7) +#define SPKR_PWRSTG_CTL_BBM_EN BIT(7) +#define SPKR_PWRSTG_CTL_HBRDGE_EN_MASK BIT(6) +#define SPKR_PWRSTG_CTL_HBRDGE_EN BIT(6) +#define SPKR_PWRSTG_CTL_CLAMP_EN_MASK BIT(5) +#define SPKR_PWRSTG_CTL_CLAMP_EN BIT(5) + +#define CDC_A_SPKR_DRV_DBG (0xf1B7) +#define CDC_A_CURRENT_LIMIT (0xf1C0) +#define CDC_A_BOOST_EN_CTL (0xf1C3) +#define CDC_A_SLOPE_COMP_IP_ZERO (0xf1C4) +#define CDC_A_SEC_ACCESS (0xf1D0) +#define CDC_A_PERPH_RESET_CTL3 (0xf1DA) +#define CDC_A_PERPH_RESET_CTL4 (0xf1DB) + +#define MSM8916_WCD_ANALOG_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000) +#define MSM8916_WCD_ANALOG_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static const char * const supply_names[] = { + "vdd-cdc-io", + "vdd-cdc-tx-rx-cx", +}; + +struct pm8916_wcd_analog_priv { + u16 pmic_rev; + u16 codec_version; + struct clk *mclk; + struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; + bool micbias1_cap_mode; + bool micbias2_cap_mode; +}; + +static const char *const adc2_mux_text[] = { "ZERO", "INP2", "INP3" }; +static const char *const rdac2_mux_text[] = { "ZERO", "RX2", "RX1" }; +static const char *const hph_text[] = { "ZERO", "Switch", }; + +static const struct soc_enum hph_enum = SOC_ENUM_SINGLE_VIRT( + ARRAY_SIZE(hph_text), hph_text); + +static const struct snd_kcontrol_new hphl_mux = SOC_DAPM_ENUM("HPHL", hph_enum); +static const struct snd_kcontrol_new hphr_mux = SOC_DAPM_ENUM("HPHR", hph_enum); + +/* ADC2 MUX */ +static const struct soc_enum adc2_enum = SOC_ENUM_SINGLE_VIRT( + ARRAY_SIZE(adc2_mux_text), adc2_mux_text); + +/* RDAC2 MUX */ +static const struct soc_enum rdac2_mux_enum = SOC_ENUM_SINGLE( + CDC_D_CDC_CONN_HPHR_DAC_CTL, 0, 3, rdac2_mux_text); + +static const struct snd_kcontrol_new spkr_switch[] = { + SOC_DAPM_SINGLE("Switch", CDC_A_SPKR_DAC_CTL, 7, 1, 0) +}; + +static const struct snd_kcontrol_new rdac2_mux = SOC_DAPM_ENUM( + "RDAC2 MUX Mux", rdac2_mux_enum); +static const struct snd_kcontrol_new tx_adc2_mux = SOC_DAPM_ENUM( + "ADC2 MUX Mux", adc2_enum); + +/* Analog Gain control 0 dB to +24 dB in 6 dB steps */ +static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 600, 0); + +static const struct snd_kcontrol_new pm8916_wcd_analog_snd_controls[] = { + SOC_SINGLE_TLV("ADC1 Volume", CDC_A_TX_1_EN, 3, 8, 0, analog_gain), + SOC_SINGLE_TLV("ADC2 Volume", CDC_A_TX_2_EN, 3, 8, 0, analog_gain), + SOC_SINGLE_TLV("ADC3 Volume", CDC_A_TX_3_EN, 3, 8, 0, analog_gain), +}; + +static void pm8916_wcd_analog_micbias_enable(struct snd_soc_codec *codec) +{ + snd_soc_update_bits(codec, CDC_A_MICB_1_CTL, + MICB_1_CTL_EXT_PRECHARG_EN_MASK | + MICB_1_CTL_INT_PRECHARG_BYP_MASK, + MICB_1_CTL_INT_PRECHARG_BYP_EXT_PRECHRG_SEL + | MICB_1_CTL_EXT_PRECHARG_EN_ENABLE); + + snd_soc_write(codec, CDC_A_MICB_1_VAL, MICB_1_VAL_MICB_OUT_VAL_V2P70V); + /* + * Special headset needs MICBIAS as 2.7V so wait for + * 50 msec for the MICBIAS to reach 2.7 volts. + */ + msleep(50); + snd_soc_update_bits(codec, CDC_A_MICB_1_CTL, + MICB_1_CTL_EXT_PRECHARG_EN_MASK | + MICB_1_CTL_INT_PRECHARG_BYP_MASK, 0); + +} + +static int pm8916_wcd_analog_enable_micbias_ext(struct snd_soc_codec + *codec, int event, + int reg, u32 cap_mode) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + pm8916_wcd_analog_micbias_enable(codec); + snd_soc_update_bits(codec, CDC_A_MICB_1_EN, + MICB_1_EN_BYP_CAP_MASK, cap_mode); + break; + } + + return 0; +} + +static int pm8916_wcd_analog_enable_micbias_int(struct snd_soc_codec + *codec, int event, + int reg, u32 cap_mode) +{ + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, CDC_A_MICB_1_INT_RBIAS, + MICB_1_INT_TX2_INT_RBIAS_EN_MASK, + MICB_1_INT_TX2_INT_RBIAS_EN_ENABLE); + snd_soc_update_bits(codec, reg, MICB_1_EN_PULL_DOWN_EN_MASK, 0); + snd_soc_update_bits(codec, CDC_A_MICB_1_EN, + MICB_1_EN_OPA_STG2_TAIL_CURR_MASK, + MICB_1_EN_OPA_STG2_TAIL_CURR_1_60UA); + + break; + case SND_SOC_DAPM_POST_PMU: + pm8916_wcd_analog_micbias_enable(codec); + snd_soc_update_bits(codec, CDC_A_MICB_1_EN, + MICB_1_EN_BYP_CAP_MASK, cap_mode); + break; + } + + return 0; +} + +static int pm8916_wcd_analog_enable_micbias_ext1(struct + snd_soc_dapm_widget + *w, struct snd_kcontrol + *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct pm8916_wcd_analog_priv *wcd = snd_soc_codec_get_drvdata(codec); + + return pm8916_wcd_analog_enable_micbias_ext(codec, event, w->reg, + wcd->micbias1_cap_mode); +} + +static int pm8916_wcd_analog_enable_micbias_ext2(struct + snd_soc_dapm_widget + *w, struct snd_kcontrol + *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct pm8916_wcd_analog_priv *wcd = snd_soc_codec_get_drvdata(codec); + + return pm8916_wcd_analog_enable_micbias_ext(codec, event, w->reg, + wcd->micbias2_cap_mode); + +} + +static int pm8916_wcd_analog_enable_micbias_int1(struct + snd_soc_dapm_widget + *w, struct snd_kcontrol + *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct pm8916_wcd_analog_priv *wcd = snd_soc_codec_get_drvdata(codec); + + return pm8916_wcd_analog_enable_micbias_int(codec, event, w->reg, + wcd->micbias1_cap_mode); +} + +static int pm8916_wcd_analog_enable_micbias_int2(struct + snd_soc_dapm_widget + *w, struct snd_kcontrol + *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct pm8916_wcd_analog_priv *wcd = snd_soc_codec_get_drvdata(codec); + + return pm8916_wcd_analog_enable_micbias_int(codec, event, w->reg, + wcd->micbias2_cap_mode); +} + +static int pm8916_wcd_analog_enable_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 adc_reg = CDC_A_TX_1_2_TEST_CTL_2; + u8 init_bit_shift; + + if (w->reg == CDC_A_TX_1_EN) + init_bit_shift = 5; + else + init_bit_shift = 4; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (w->reg == CDC_A_TX_2_EN) + snd_soc_update_bits(codec, CDC_A_MICB_1_CTL, + MICB_1_CTL_CFILT_REF_SEL_MASK, + MICB_1_CTL_CFILT_REF_SEL_HPF_REF); + /* + * Add delay of 10 ms to give sufficient time for the voltage + * to shoot up and settle so that the txfe init does not + * happen when the input voltage is changing too much. + */ + usleep_range(10000, 10010); + snd_soc_update_bits(codec, adc_reg, 1 << init_bit_shift, + 1 << init_bit_shift); + switch (w->reg) { + case CDC_A_TX_1_EN: + snd_soc_update_bits(codec, CDC_D_CDC_CONN_TX1_CTL, + CONN_TX1_SERIAL_TX1_MUX, + CONN_TX1_SERIAL_TX1_ADC_1); + break; + case CDC_A_TX_2_EN: + case CDC_A_TX_3_EN: + snd_soc_update_bits(codec, CDC_D_CDC_CONN_TX2_CTL, + CONN_TX2_SERIAL_TX2_MUX, + CONN_TX2_SERIAL_TX2_ADC_2); + break; + } + break; + case SND_SOC_DAPM_POST_PMU: + /* + * Add delay of 12 ms before deasserting the init + * to reduce the tx pop + */ + usleep_range(12000, 12010); + snd_soc_update_bits(codec, adc_reg, 1 << init_bit_shift, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + switch (w->reg) { + case CDC_A_TX_1_EN: + snd_soc_update_bits(codec, CDC_D_CDC_CONN_TX1_CTL, + CONN_TX1_SERIAL_TX1_MUX, + CONN_TX1_SERIAL_TX1_ZERO); + break; + case CDC_A_TX_2_EN: + snd_soc_update_bits(codec, CDC_A_MICB_1_CTL, + MICB_1_CTL_CFILT_REF_SEL_MASK, 0); + case CDC_A_TX_3_EN: + snd_soc_update_bits(codec, CDC_D_CDC_CONN_TX2_CTL, + CONN_TX2_SERIAL_TX2_MUX, + CONN_TX2_SERIAL_TX2_ZERO); + break; + } + + + break; + } + return 0; +} + +static int pm8916_wcd_analog_enable_spk_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, CDC_A_SPKR_PWRSTG_CTL, + SPKR_PWRSTG_CTL_DAC_EN_MASK | + SPKR_PWRSTG_CTL_BBM_MASK | + SPKR_PWRSTG_CTL_HBRDGE_EN_MASK | + SPKR_PWRSTG_CTL_CLAMP_EN_MASK, + SPKR_PWRSTG_CTL_DAC_EN| + SPKR_PWRSTG_CTL_BBM_EN | + SPKR_PWRSTG_CTL_HBRDGE_EN | + SPKR_PWRSTG_CTL_CLAMP_EN); + + snd_soc_update_bits(codec, CDC_A_RX_EAR_CTL, + RX_EAR_CTL_SPK_VBAT_LDO_EN_MASK, + RX_EAR_CTL_SPK_VBAT_LDO_EN_ENABLE); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, CDC_A_SPKR_DRV_CTL, + SPKR_DRV_CTL_DEF_MASK, + SPKR_DRV_CTL_DEF_VAL); + snd_soc_update_bits(codec, w->reg, + SPKR_DRV_CLASSD_PA_EN_MASK, + SPKR_DRV_CLASSD_PA_EN_ENABLE); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, CDC_A_SPKR_PWRSTG_CTL, + SPKR_PWRSTG_CTL_DAC_EN_MASK| + SPKR_PWRSTG_CTL_BBM_MASK | + SPKR_PWRSTG_CTL_HBRDGE_EN_MASK | + SPKR_PWRSTG_CTL_CLAMP_EN_MASK, 0); + + snd_soc_update_bits(codec, CDC_A_SPKR_DAC_CTL, + SPKR_DAC_CTL_DAC_RESET_MASK, + SPKR_DAC_CTL_DAC_RESET_NORMAL); + snd_soc_update_bits(codec, CDC_A_RX_EAR_CTL, + RX_EAR_CTL_SPK_VBAT_LDO_EN_MASK, 0); + break; + } + return 0; +} + +static const struct reg_default wcd_reg_defaults_2_0[] = { + {CDC_A_RX_COM_OCP_CTL, 0xD1}, + {CDC_A_RX_COM_OCP_COUNT, 0xFF}, + {CDC_D_SEC_ACCESS, 0xA5}, + {CDC_D_PERPH_RESET_CTL3, 0x0F}, + {CDC_A_TX_1_2_OPAMP_BIAS, 0x4F}, + {CDC_A_NCP_FBCTRL, 0x28}, + {CDC_A_SPKR_DRV_CTL, 0x69}, + {CDC_A_SPKR_DRV_DBG, 0x01}, + {CDC_A_BOOST_EN_CTL, 0x5F}, + {CDC_A_SLOPE_COMP_IP_ZERO, 0x88}, + {CDC_A_SEC_ACCESS, 0xA5}, + {CDC_A_PERPH_RESET_CTL3, 0x0F}, + {CDC_A_CURRENT_LIMIT, 0x82}, + {CDC_A_SPKR_DAC_CTL, 0x03}, + {CDC_A_SPKR_OCP_CTL, 0xE1}, + {CDC_A_MASTER_BIAS_CTL, 0x30}, +}; + +static int pm8916_wcd_analog_probe(struct snd_soc_codec *codec) +{ + struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(codec->dev); + int err, reg; + + err = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); + if (err != 0) { + dev_err(codec->dev, "failed to enable regulators (%d)\n", err); + return err; + } + + snd_soc_codec_set_drvdata(codec, priv); + priv->pmic_rev = snd_soc_read(codec, CDC_D_REVISION1); + priv->codec_version = snd_soc_read(codec, CDC_D_PERPH_SUBTYPE); + + dev_info(codec->dev, "PMIC REV: %d\t CODEC Version: %d\n", + priv->pmic_rev, priv->codec_version); + + snd_soc_write(codec, CDC_D_PERPH_RESET_CTL4, 0x01); + snd_soc_write(codec, CDC_A_PERPH_RESET_CTL4, 0x01); + + for (reg = 0; reg < ARRAY_SIZE(wcd_reg_defaults_2_0); reg++) + snd_soc_write(codec, wcd_reg_defaults_2_0[reg].reg, + wcd_reg_defaults_2_0[reg].def); + + return 0; +} + +static int pm8916_wcd_analog_remove(struct snd_soc_codec *codec) +{ + struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(codec->dev); + + return regulator_bulk_disable(ARRAY_SIZE(priv->supplies), + priv->supplies); +} + +static const struct snd_soc_dapm_route pm8916_wcd_analog_audio_map[] = { + + {"PDM_RX1", NULL, "PDM Playback"}, + {"PDM_RX2", NULL, "PDM Playback"}, + {"PDM_RX3", NULL, "PDM Playback"}, + {"PDM Capture", NULL, "PDM_TX"}, + + /* ADC Connections */ + {"PDM_TX", NULL, "ADC2"}, + {"PDM_TX", NULL, "ADC3"}, + {"ADC2", NULL, "ADC2 MUX"}, + {"ADC3", NULL, "ADC2 MUX"}, + {"ADC2 MUX", "INP2", "ADC2_INP2"}, + {"ADC2 MUX", "INP3", "ADC2_INP3"}, + + {"PDM_TX", NULL, "ADC1"}, + {"ADC1", NULL, "AMIC1"}, + {"ADC2_INP2", NULL, "AMIC2"}, + {"ADC2_INP3", NULL, "AMIC3"}, + + /* RDAC Connections */ + {"HPHR DAC", NULL, "RDAC2 MUX"}, + {"RDAC2 MUX", "RX1", "PDM_RX1"}, + {"RDAC2 MUX", "RX2", "PDM_RX2"}, + {"HPHL DAC", NULL, "PDM_RX1"}, + {"PDM_RX1", NULL, "RXD1_CLK"}, + {"PDM_RX2", NULL, "RXD2_CLK"}, + {"PDM_RX3", NULL, "RXD3_CLK"}, + + {"PDM_RX1", NULL, "RXD_PDM_CLK"}, + {"PDM_RX2", NULL, "RXD_PDM_CLK"}, + {"PDM_RX3", NULL, "RXD_PDM_CLK"}, + + {"ADC1", NULL, "TXD_CLK"}, + {"ADC2", NULL, "TXD_CLK"}, + {"ADC3", NULL, "TXD_CLK"}, + + {"ADC1", NULL, "TXA_CLK25"}, + {"ADC2", NULL, "TXA_CLK25"}, + {"ADC3", NULL, "TXA_CLK25"}, + + {"PDM_RX1", NULL, "A_MCLK2"}, + {"PDM_RX2", NULL, "A_MCLK2"}, + {"PDM_RX3", NULL, "A_MCLK2"}, + + {"PDM_TX", NULL, "A_MCLK2"}, + {"A_MCLK2", NULL, "A_MCLK"}, + + /* Headset (RX MIX1 and RX MIX2) */ + {"HEADPHONE", NULL, "HPHL PA"}, + {"HEADPHONE", NULL, "HPHR PA"}, + + {"HPHL PA", NULL, "EAR_HPHL_CLK"}, + {"HPHR PA", NULL, "EAR_HPHR_CLK"}, + + {"CP", NULL, "NCP_CLK"}, + + {"HPHL PA", NULL, "HPHL"}, + {"HPHR PA", NULL, "HPHR"}, + {"HPHL PA", NULL, "CP"}, + {"HPHL PA", NULL, "RX_BIAS"}, + {"HPHR PA", NULL, "CP"}, + {"HPHR PA", NULL, "RX_BIAS"}, + {"HPHL", "Switch", "HPHL DAC"}, + {"HPHR", "Switch", "HPHR DAC"}, + + {"RX_BIAS", NULL, "DAC_REF"}, + + {"SPK_OUT", NULL, "SPK PA"}, + {"SPK PA", NULL, "RX_BIAS"}, + {"SPK PA", NULL, "SPKR_CLK"}, + {"SPK PA", NULL, "SPK DAC"}, + {"SPK DAC", "Switch", "PDM_RX3"}, + + {"MIC BIAS Internal1", NULL, "INT_LDO_H"}, + {"MIC BIAS Internal2", NULL, "INT_LDO_H"}, + {"MIC BIAS External1", NULL, "INT_LDO_H"}, + {"MIC BIAS External2", NULL, "INT_LDO_H"}, + {"MIC BIAS Internal1", NULL, "vdd-micbias"}, + {"MIC BIAS Internal2", NULL, "vdd-micbias"}, + {"MIC BIAS External1", NULL, "vdd-micbias"}, + {"MIC BIAS External2", NULL, "vdd-micbias"}, +}; + +static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = { + + SND_SOC_DAPM_AIF_IN("PDM_RX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("PDM_RX2", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("PDM_RX3", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("PDM_TX", NULL, 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_INPUT("AMIC3"), + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_OUTPUT("HEADPHONE"), + + /* RX stuff */ + SND_SOC_DAPM_SUPPLY("INT_LDO_H", SND_SOC_NOPM, 1, 0, NULL, 0), + + SND_SOC_DAPM_PGA("HPHL PA", CDC_A_RX_HPH_CNP_EN, 5, 0, NULL, 0), + SND_SOC_DAPM_MUX("HPHL", SND_SOC_NOPM, 0, 0, &hphl_mux), + SND_SOC_DAPM_MIXER("HPHL DAC", CDC_A_RX_HPH_L_PA_DAC_CTL, 3, 0, NULL, + 0), + SND_SOC_DAPM_PGA("HPHR PA", CDC_A_RX_HPH_CNP_EN, 4, 0, NULL, 0), + SND_SOC_DAPM_MUX("HPHR", SND_SOC_NOPM, 0, 0, &hphr_mux), + SND_SOC_DAPM_MIXER("HPHR DAC", CDC_A_RX_HPH_R_PA_DAC_CTL, 3, 0, NULL, + 0), + SND_SOC_DAPM_MIXER("SPK DAC", SND_SOC_NOPM, 0, 0, + spkr_switch, ARRAY_SIZE(spkr_switch)), + + /* Speaker */ + SND_SOC_DAPM_OUTPUT("SPK_OUT"), + SND_SOC_DAPM_PGA_E("SPK PA", CDC_A_SPKR_DRV_CTL, + 6, 0, NULL, 0, + pm8916_wcd_analog_enable_spk_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_REGULATOR_SUPPLY("vdd-micbias", 0, 0), + SND_SOC_DAPM_SUPPLY("CP", CDC_A_NCP_EN, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DAC_REF", CDC_A_RX_COM_BIAS_DAC, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RX_BIAS", CDC_A_RX_COM_BIAS_DAC, 7, 0, NULL, 0), + + /* TX */ + SND_SOC_DAPM_SUPPLY("MIC BIAS Internal1", CDC_A_MICB_1_EN, 7, 0, + pm8916_wcd_analog_enable_micbias_int1, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS Internal2", CDC_A_MICB_2_EN, 7, 0, + pm8916_wcd_analog_enable_micbias_int2, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("MIC BIAS External1", CDC_A_MICB_1_EN, 7, 0, + pm8916_wcd_analog_enable_micbias_ext1, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS External2", CDC_A_MICB_2_EN, 7, 0, + pm8916_wcd_analog_enable_micbias_ext2, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("ADC1", NULL, CDC_A_TX_1_EN, 7, 0, + pm8916_wcd_analog_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC2_INP2", NULL, CDC_A_TX_2_EN, 7, 0, + pm8916_wcd_analog_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC2_INP3", NULL, CDC_A_TX_3_EN, 7, 0, + pm8916_wcd_analog_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("ADC3", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, &tx_adc2_mux), + SND_SOC_DAPM_MUX("RDAC2 MUX", SND_SOC_NOPM, 0, 0, &rdac2_mux), + + /* Analog path clocks */ + SND_SOC_DAPM_SUPPLY("EAR_HPHR_CLK", CDC_D_CDC_ANA_CLK_CTL, 0, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY("EAR_HPHL_CLK", CDC_D_CDC_ANA_CLK_CTL, 1, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY("SPKR_CLK", CDC_D_CDC_ANA_CLK_CTL, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("TXA_CLK25", CDC_D_CDC_ANA_CLK_CTL, 5, 0, NULL, 0), + + /* Digital path clocks */ + + SND_SOC_DAPM_SUPPLY("RXD1_CLK", CDC_D_CDC_DIG_CLK_CTL, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RXD2_CLK", CDC_D_CDC_DIG_CLK_CTL, 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RXD3_CLK", CDC_D_CDC_DIG_CLK_CTL, 2, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("TXD_CLK", CDC_D_CDC_DIG_CLK_CTL, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("NCP_CLK", CDC_D_CDC_DIG_CLK_CTL, 6, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RXD_PDM_CLK", CDC_D_CDC_DIG_CLK_CTL, 7, 0, NULL, + 0), + + /* System Clock source */ + SND_SOC_DAPM_SUPPLY("A_MCLK", CDC_D_CDC_TOP_CLK_CTL, 2, 0, NULL, 0), + /* TX ADC and RX DAC Clock source. */ + SND_SOC_DAPM_SUPPLY("A_MCLK2", CDC_D_CDC_TOP_CLK_CTL, 3, 0, NULL, 0), +}; + +static struct regmap *pm8916_get_regmap(struct device *dev) +{ + return dev_get_regmap(dev->parent, NULL); +} + +static int pm8916_wcd_analog_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_update_bits(dai->codec, CDC_D_CDC_RST_CTL, + RST_CTL_DIG_SW_RST_N_MASK, + RST_CTL_DIG_SW_RST_N_REMOVE_RESET); + + return 0; +} + +static void pm8916_wcd_analog_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_update_bits(dai->codec, CDC_D_CDC_RST_CTL, + RST_CTL_DIG_SW_RST_N_MASK, 0); +} + +static struct snd_soc_dai_ops pm8916_wcd_analog_dai_ops = { + .startup = pm8916_wcd_analog_startup, + .shutdown = pm8916_wcd_analog_shutdown, +}; + +static struct snd_soc_dai_driver pm8916_wcd_analog_dai[] = { + [0] = { + .name = "pm8916_wcd_analog_pdm_rx", + .id = 0, + .playback = { + .stream_name = "PDM Playback", + .rates = MSM8916_WCD_ANALOG_RATES, + .formats = MSM8916_WCD_ANALOG_FORMATS, + .channels_min = 1, + .channels_max = 3, + }, + .ops = &pm8916_wcd_analog_dai_ops, + }, + [1] = { + .name = "pm8916_wcd_analog_pdm_tx", + .id = 1, + .capture = { + .stream_name = "PDM Capture", + .rates = MSM8916_WCD_ANALOG_RATES, + .formats = MSM8916_WCD_ANALOG_FORMATS, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &pm8916_wcd_analog_dai_ops, + }, +}; + +static struct snd_soc_codec_driver pm8916_wcd_analog = { + .probe = pm8916_wcd_analog_probe, + .remove = pm8916_wcd_analog_remove, + .get_regmap = pm8916_get_regmap, + .component_driver = { + .controls = pm8916_wcd_analog_snd_controls, + .num_controls = ARRAY_SIZE(pm8916_wcd_analog_snd_controls), + .dapm_widgets = pm8916_wcd_analog_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pm8916_wcd_analog_dapm_widgets), + .dapm_routes = pm8916_wcd_analog_audio_map, + .num_dapm_routes = ARRAY_SIZE(pm8916_wcd_analog_audio_map), + }, +}; + +static int pm8916_wcd_analog_parse_dt(struct device *dev, + struct pm8916_wcd_analog_priv *priv) +{ + + if (of_property_read_bool(dev->of_node, "qcom,micbias1-ext-cap")) + priv->micbias1_cap_mode = MICB_1_EN_EXT_BYP_CAP; + else + priv->micbias1_cap_mode = MICB_1_EN_NO_EXT_BYP_CAP; + + if (of_property_read_bool(dev->of_node, "qcom,micbias2-ext-cap")) + priv->micbias2_cap_mode = MICB_1_EN_EXT_BYP_CAP; + else + priv->micbias2_cap_mode = MICB_1_EN_NO_EXT_BYP_CAP; + + return 0; +} + +static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev) +{ + struct pm8916_wcd_analog_priv *priv; + struct device *dev = &pdev->dev; + int ret, i; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + ret = pm8916_wcd_analog_parse_dt(dev, priv); + if (ret < 0) + return ret; + + priv->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(priv->mclk)) { + dev_err(dev, "failed to get mclk\n"); + return PTR_ERR(priv->mclk); + } + + for (i = 0; i < ARRAY_SIZE(supply_names); i++) + priv->supplies[i].supply = supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(priv->supplies), + priv->supplies); + if (ret) { + dev_err(dev, "Failed to get regulator supplies %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(priv->mclk); + if (ret < 0) { + dev_err(dev, "failed to enable mclk %d\n", ret); + return ret; + } + + dev_set_drvdata(dev, priv); + + return snd_soc_register_codec(dev, &pm8916_wcd_analog, + pm8916_wcd_analog_dai, + ARRAY_SIZE(pm8916_wcd_analog_dai)); +} + +static int pm8916_wcd_analog_spmi_remove(struct platform_device *pdev) +{ + struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(&pdev->dev); + + snd_soc_unregister_codec(&pdev->dev); + clk_disable_unprepare(priv->mclk); + + return 0; +} + +static const struct of_device_id pm8916_wcd_analog_spmi_match_table[] = { + { .compatible = "qcom,pm8916-wcd-analog-codec", }, + { } +}; + +static struct platform_driver pm8916_wcd_analog_spmi_driver = { + .driver = { + .name = "qcom,pm8916-wcd-spmi-codec", + .of_match_table = pm8916_wcd_analog_spmi_match_table, + }, + .probe = pm8916_wcd_analog_spmi_probe, + .remove = pm8916_wcd_analog_spmi_remove, +}; + +module_platform_driver(pm8916_wcd_analog_spmi_driver); + +MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org>"); +MODULE_DESCRIPTION("PMIC PM8916 WCD Analog Codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/msm8916-wcd-digital.c b/sound/soc/codecs/msm8916-wcd-digital.c new file mode 100644 index 000000000000..f690442af8c9 --- /dev/null +++ b/sound/soc/codecs/msm8916-wcd-digital.c @@ -0,0 +1,923 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#include <linux/module.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/types.h> +#include <linux/clk.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> +#include <sound/soc.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/tlv.h> + +#define LPASS_CDC_CLK_RX_RESET_CTL (0x000) +#define LPASS_CDC_CLK_TX_RESET_B1_CTL (0x004) +#define CLK_RX_RESET_B1_CTL_TX1_RESET_MASK BIT(0) +#define CLK_RX_RESET_B1_CTL_TX2_RESET_MASK BIT(1) +#define LPASS_CDC_CLK_DMIC_B1_CTL (0x008) +#define DMIC_B1_CTL_DMIC0_CLK_SEL_MASK GENMASK(3, 1) +#define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV2 (0x0 << 1) +#define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV3 (0x1 << 1) +#define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV4 (0x2 << 1) +#define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV6 (0x3 << 1) +#define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV16 (0x4 << 1) +#define DMIC_B1_CTL_DMIC0_CLK_EN_MASK BIT(0) +#define DMIC_B1_CTL_DMIC0_CLK_EN_ENABLE BIT(0) + +#define LPASS_CDC_CLK_RX_I2S_CTL (0x00C) +#define RX_I2S_CTL_RX_I2S_MODE_MASK BIT(5) +#define RX_I2S_CTL_RX_I2S_MODE_16 BIT(5) +#define RX_I2S_CTL_RX_I2S_MODE_32 0 +#define RX_I2S_CTL_RX_I2S_FS_RATE_MASK GENMASK(2, 0) +#define RX_I2S_CTL_RX_I2S_FS_RATE_F_8_KHZ 0x0 +#define RX_I2S_CTL_RX_I2S_FS_RATE_F_16_KHZ 0x1 +#define RX_I2S_CTL_RX_I2S_FS_RATE_F_32_KHZ 0x2 +#define RX_I2S_CTL_RX_I2S_FS_RATE_F_48_KHZ 0x3 +#define RX_I2S_CTL_RX_I2S_FS_RATE_F_96_KHZ 0x4 +#define RX_I2S_CTL_RX_I2S_FS_RATE_F_192_KHZ 0x5 +#define LPASS_CDC_CLK_TX_I2S_CTL (0x010) +#define TX_I2S_CTL_TX_I2S_MODE_MASK BIT(5) +#define TX_I2S_CTL_TX_I2S_MODE_16 BIT(5) +#define TX_I2S_CTL_TX_I2S_MODE_32 0 +#define TX_I2S_CTL_TX_I2S_FS_RATE_MASK GENMASK(2, 0) +#define TX_I2S_CTL_TX_I2S_FS_RATE_F_8_KHZ 0x0 +#define TX_I2S_CTL_TX_I2S_FS_RATE_F_16_KHZ 0x1 +#define TX_I2S_CTL_TX_I2S_FS_RATE_F_32_KHZ 0x2 +#define TX_I2S_CTL_TX_I2S_FS_RATE_F_48_KHZ 0x3 +#define TX_I2S_CTL_TX_I2S_FS_RATE_F_96_KHZ 0x4 +#define TX_I2S_CTL_TX_I2S_FS_RATE_F_192_KHZ 0x5 + +#define LPASS_CDC_CLK_OTHR_RESET_B1_CTL (0x014) +#define LPASS_CDC_CLK_TX_CLK_EN_B1_CTL (0x018) +#define LPASS_CDC_CLK_OTHR_CTL (0x01C) +#define LPASS_CDC_CLK_RX_B1_CTL (0x020) +#define LPASS_CDC_CLK_MCLK_CTL (0x024) +#define MCLK_CTL_MCLK_EN_MASK BIT(0) +#define MCLK_CTL_MCLK_EN_ENABLE BIT(0) +#define MCLK_CTL_MCLK_EN_DISABLE 0 +#define LPASS_CDC_CLK_PDM_CTL (0x028) +#define LPASS_CDC_CLK_PDM_CTL_PDM_EN_MASK BIT(0) +#define LPASS_CDC_CLK_PDM_CTL_PDM_EN BIT(0) +#define LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_MASK BIT(1) +#define LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_FB BIT(1) +#define LPASS_CDC_CLK_PDM_CTL_PDM_CLK_PDM_CLK 0 + +#define LPASS_CDC_CLK_SD_CTL (0x02C) +#define LPASS_CDC_RX1_B1_CTL (0x040) +#define LPASS_CDC_RX2_B1_CTL (0x060) +#define LPASS_CDC_RX3_B1_CTL (0x080) +#define LPASS_CDC_RX1_B2_CTL (0x044) +#define LPASS_CDC_RX2_B2_CTL (0x064) +#define LPASS_CDC_RX3_B2_CTL (0x084) +#define LPASS_CDC_RX1_B3_CTL (0x048) +#define LPASS_CDC_RX2_B3_CTL (0x068) +#define LPASS_CDC_RX3_B3_CTL (0x088) +#define LPASS_CDC_RX1_B4_CTL (0x04C) +#define LPASS_CDC_RX2_B4_CTL (0x06C) +#define LPASS_CDC_RX3_B4_CTL (0x08C) +#define LPASS_CDC_RX1_B5_CTL (0x050) +#define LPASS_CDC_RX2_B5_CTL (0x070) +#define LPASS_CDC_RX3_B5_CTL (0x090) +#define LPASS_CDC_RX1_B6_CTL (0x054) +#define RXn_B6_CTL_MUTE_MASK BIT(0) +#define RXn_B6_CTL_MUTE_ENABLE BIT(0) +#define RXn_B6_CTL_MUTE_DISABLE 0 +#define LPASS_CDC_RX2_B6_CTL (0x074) +#define LPASS_CDC_RX3_B6_CTL (0x094) +#define LPASS_CDC_RX1_VOL_CTL_B1_CTL (0x058) +#define LPASS_CDC_RX2_VOL_CTL_B1_CTL (0x078) +#define LPASS_CDC_RX3_VOL_CTL_B1_CTL (0x098) +#define LPASS_CDC_RX1_VOL_CTL_B2_CTL (0x05C) +#define LPASS_CDC_RX2_VOL_CTL_B2_CTL (0x07C) +#define LPASS_CDC_RX3_VOL_CTL_B2_CTL (0x09C) +#define LPASS_CDC_TOP_GAIN_UPDATE (0x0A0) +#define LPASS_CDC_TOP_CTL (0x0A4) +#define TOP_CTL_DIG_MCLK_FREQ_MASK BIT(0) +#define TOP_CTL_DIG_MCLK_FREQ_F_12_288MHZ 0 +#define TOP_CTL_DIG_MCLK_FREQ_F_9_6MHZ BIT(0) + +#define LPASS_CDC_DEBUG_DESER1_CTL (0x0E0) +#define LPASS_CDC_DEBUG_DESER2_CTL (0x0E4) +#define LPASS_CDC_DEBUG_B1_CTL_CFG (0x0E8) +#define LPASS_CDC_DEBUG_B2_CTL_CFG (0x0EC) +#define LPASS_CDC_DEBUG_B3_CTL_CFG (0x0F0) +#define LPASS_CDC_IIR1_GAIN_B1_CTL (0x100) +#define LPASS_CDC_IIR2_GAIN_B1_CTL (0x140) +#define LPASS_CDC_IIR1_GAIN_B2_CTL (0x104) +#define LPASS_CDC_IIR2_GAIN_B2_CTL (0x144) +#define LPASS_CDC_IIR1_GAIN_B3_CTL (0x108) +#define LPASS_CDC_IIR2_GAIN_B3_CTL (0x148) +#define LPASS_CDC_IIR1_GAIN_B4_CTL (0x10C) +#define LPASS_CDC_IIR2_GAIN_B4_CTL (0x14C) +#define LPASS_CDC_IIR1_GAIN_B5_CTL (0x110) +#define LPASS_CDC_IIR2_GAIN_B5_CTL (0x150) +#define LPASS_CDC_IIR1_GAIN_B6_CTL (0x114) +#define LPASS_CDC_IIR2_GAIN_B6_CTL (0x154) +#define LPASS_CDC_IIR1_GAIN_B7_CTL (0x118) +#define LPASS_CDC_IIR2_GAIN_B7_CTL (0x158) +#define LPASS_CDC_IIR1_GAIN_B8_CTL (0x11C) +#define LPASS_CDC_IIR2_GAIN_B8_CTL (0x15C) +#define LPASS_CDC_IIR1_CTL (0x120) +#define LPASS_CDC_IIR2_CTL (0x160) +#define LPASS_CDC_IIR1_GAIN_TIMER_CTL (0x124) +#define LPASS_CDC_IIR2_GAIN_TIMER_CTL (0x164) +#define LPASS_CDC_IIR1_COEF_B1_CTL (0x128) +#define LPASS_CDC_IIR2_COEF_B1_CTL (0x168) +#define LPASS_CDC_IIR1_COEF_B2_CTL (0x12C) +#define LPASS_CDC_IIR2_COEF_B2_CTL (0x16C) +#define LPASS_CDC_CONN_RX1_B1_CTL (0x180) +#define LPASS_CDC_CONN_RX1_B2_CTL (0x184) +#define LPASS_CDC_CONN_RX1_B3_CTL (0x188) +#define LPASS_CDC_CONN_RX2_B1_CTL (0x18C) +#define LPASS_CDC_CONN_RX2_B2_CTL (0x190) +#define LPASS_CDC_CONN_RX2_B3_CTL (0x194) +#define LPASS_CDC_CONN_RX3_B1_CTL (0x198) +#define LPASS_CDC_CONN_RX3_B2_CTL (0x19C) +#define LPASS_CDC_CONN_TX_B1_CTL (0x1A0) +#define LPASS_CDC_CONN_EQ1_B1_CTL (0x1A8) +#define LPASS_CDC_CONN_EQ1_B2_CTL (0x1AC) +#define LPASS_CDC_CONN_EQ1_B3_CTL (0x1B0) +#define LPASS_CDC_CONN_EQ1_B4_CTL (0x1B4) +#define LPASS_CDC_CONN_EQ2_B1_CTL (0x1B8) +#define LPASS_CDC_CONN_EQ2_B2_CTL (0x1BC) +#define LPASS_CDC_CONN_EQ2_B3_CTL (0x1C0) +#define LPASS_CDC_CONN_EQ2_B4_CTL (0x1C4) +#define LPASS_CDC_CONN_TX_I2S_SD1_CTL (0x1C8) +#define LPASS_CDC_TX1_VOL_CTL_TIMER (0x280) +#define LPASS_CDC_TX2_VOL_CTL_TIMER (0x2A0) +#define LPASS_CDC_TX1_VOL_CTL_GAIN (0x284) +#define LPASS_CDC_TX2_VOL_CTL_GAIN (0x2A4) +#define LPASS_CDC_TX1_VOL_CTL_CFG (0x288) +#define TX_VOL_CTL_CFG_MUTE_EN_MASK BIT(0) +#define TX_VOL_CTL_CFG_MUTE_EN_ENABLE BIT(0) + +#define LPASS_CDC_TX2_VOL_CTL_CFG (0x2A8) +#define LPASS_CDC_TX1_MUX_CTL (0x28C) +#define TX_MUX_CTL_CUT_OFF_FREQ_MASK GENMASK(5, 4) +#define TX_MUX_CTL_CUT_OFF_FREQ_SHIFT 4 +#define TX_MUX_CTL_CF_NEG_3DB_4HZ (0x0 << 4) +#define TX_MUX_CTL_CF_NEG_3DB_75HZ (0x1 << 4) +#define TX_MUX_CTL_CF_NEG_3DB_150HZ (0x2 << 4) +#define TX_MUX_CTL_HPF_BP_SEL_MASK BIT(3) +#define TX_MUX_CTL_HPF_BP_SEL_BYPASS BIT(3) +#define TX_MUX_CTL_HPF_BP_SEL_NO_BYPASS 0 + +#define LPASS_CDC_TX2_MUX_CTL (0x2AC) +#define LPASS_CDC_TX1_CLK_FS_CTL (0x290) +#define LPASS_CDC_TX2_CLK_FS_CTL (0x2B0) +#define LPASS_CDC_TX1_DMIC_CTL (0x294) +#define LPASS_CDC_TX2_DMIC_CTL (0x2B4) +#define TXN_DMIC_CTL_CLK_SEL_MASK GENMASK(2, 0) +#define TXN_DMIC_CTL_CLK_SEL_DIV2 0x0 +#define TXN_DMIC_CTL_CLK_SEL_DIV3 0x1 +#define TXN_DMIC_CTL_CLK_SEL_DIV4 0x2 +#define TXN_DMIC_CTL_CLK_SEL_DIV6 0x3 +#define TXN_DMIC_CTL_CLK_SEL_DIV16 0x4 + +#define MSM8916_WCD_DIGITAL_RATES (SNDRV_PCM_RATE_8000 | \ + SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_48000) +#define MSM8916_WCD_DIGITAL_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +struct msm8916_wcd_digital_priv { + struct clk *ahbclk, *mclk; +}; + +static const unsigned long rx_gain_reg[] = { + LPASS_CDC_RX1_VOL_CTL_B2_CTL, + LPASS_CDC_RX2_VOL_CTL_B2_CTL, + LPASS_CDC_RX3_VOL_CTL_B2_CTL, +}; + +static const unsigned long tx_gain_reg[] = { + LPASS_CDC_TX1_VOL_CTL_GAIN, + LPASS_CDC_TX2_VOL_CTL_GAIN, +}; + +static const char *const rx_mix1_text[] = { + "ZERO", "IIR1", "IIR2", "RX1", "RX2", "RX3" +}; + +static const char *const dec_mux_text[] = { + "ZERO", "ADC1", "ADC2", "ADC3", "DMIC1", "DMIC2" +}; +static const char *const rx_mix2_text[] = { "ZERO", "IIR1", "IIR2" }; +static const char *const adc2_mux_text[] = { "ZERO", "INP2", "INP3" }; + +/* RX1 MIX1 */ +static const struct soc_enum rx_mix1_inp_enum[] = { + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX1_B1_CTL, 0, 6, rx_mix1_text), + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX1_B1_CTL, 3, 6, rx_mix1_text), + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX1_B2_CTL, 0, 6, rx_mix1_text), +}; + +/* RX1 MIX2 */ +static const struct soc_enum rx_mix2_inp1_chain_enum = SOC_ENUM_SINGLE( + LPASS_CDC_CONN_RX1_B3_CTL, 0, 3, rx_mix2_text); + +/* RX2 MIX1 */ +static const struct soc_enum rx2_mix1_inp_enum[] = { + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B1_CTL, 0, 6, rx_mix1_text), + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B1_CTL, 3, 6, rx_mix1_text), + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B1_CTL, 0, 6, rx_mix1_text), +}; + +/* RX2 MIX2 */ +static const struct soc_enum rx2_mix2_inp1_chain_enum = SOC_ENUM_SINGLE( + LPASS_CDC_CONN_RX2_B3_CTL, 0, 3, rx_mix2_text); + +/* RX3 MIX1 */ +static const struct soc_enum rx3_mix1_inp_enum[] = { + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B1_CTL, 0, 6, rx_mix1_text), + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B1_CTL, 3, 6, rx_mix1_text), + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B1_CTL, 0, 6, rx_mix1_text), +}; + +/* DEC */ +static const struct soc_enum dec1_mux_enum = SOC_ENUM_SINGLE( + LPASS_CDC_CONN_TX_B1_CTL, 0, 6, dec_mux_text); +static const struct soc_enum dec2_mux_enum = SOC_ENUM_SINGLE( + LPASS_CDC_CONN_TX_B1_CTL, 3, 6, dec_mux_text); + +/* RDAC2 MUX */ +static const struct snd_kcontrol_new dec1_mux = SOC_DAPM_ENUM( + "DEC1 MUX Mux", dec1_mux_enum); +static const struct snd_kcontrol_new dec2_mux = SOC_DAPM_ENUM( + "DEC2 MUX Mux", dec2_mux_enum); +static const struct snd_kcontrol_new rx_mix1_inp1_mux = SOC_DAPM_ENUM( + "RX1 MIX1 INP1 Mux", rx_mix1_inp_enum[0]); +static const struct snd_kcontrol_new rx_mix1_inp2_mux = SOC_DAPM_ENUM( + "RX1 MIX1 INP2 Mux", rx_mix1_inp_enum[1]); +static const struct snd_kcontrol_new rx_mix1_inp3_mux = SOC_DAPM_ENUM( + "RX1 MIX1 INP3 Mux", rx_mix1_inp_enum[2]); +static const struct snd_kcontrol_new rx2_mix1_inp1_mux = SOC_DAPM_ENUM( + "RX2 MIX1 INP1 Mux", rx2_mix1_inp_enum[0]); +static const struct snd_kcontrol_new rx2_mix1_inp2_mux = SOC_DAPM_ENUM( + "RX2 MIX1 INP2 Mux", rx2_mix1_inp_enum[1]); +static const struct snd_kcontrol_new rx2_mix1_inp3_mux = SOC_DAPM_ENUM( + "RX2 MIX1 INP3 Mux", rx2_mix1_inp_enum[2]); +static const struct snd_kcontrol_new rx3_mix1_inp1_mux = SOC_DAPM_ENUM( + "RX3 MIX1 INP1 Mux", rx3_mix1_inp_enum[0]); +static const struct snd_kcontrol_new rx3_mix1_inp2_mux = SOC_DAPM_ENUM( + "RX3 MIX1 INP2 Mux", rx3_mix1_inp_enum[1]); +static const struct snd_kcontrol_new rx3_mix1_inp3_mux = SOC_DAPM_ENUM( + "RX3 MIX1 INP3 Mux", rx3_mix1_inp_enum[2]); + +/* Digital Gain control -38.4 dB to +38.4 dB in 0.3 dB steps */ +static const DECLARE_TLV_DB_SCALE(digital_gain, -3840, 30, 0); + +/* Cutoff Freq for High Pass Filter at -3dB */ +static const char * const hpf_cutoff_text[] = { + "4Hz", "75Hz", "150Hz", +}; + +static SOC_ENUM_SINGLE_DECL(tx1_hpf_cutoff_enum, LPASS_CDC_TX1_MUX_CTL, 4, + hpf_cutoff_text); +static SOC_ENUM_SINGLE_DECL(tx2_hpf_cutoff_enum, LPASS_CDC_TX2_MUX_CTL, 4, + hpf_cutoff_text); + +/* cut off for dc blocker inside rx chain */ +static const char * const dc_blocker_cutoff_text[] = { + "4Hz", "75Hz", "150Hz", +}; + +static SOC_ENUM_SINGLE_DECL(rx1_dcb_cutoff_enum, LPASS_CDC_RX1_B4_CTL, 0, + dc_blocker_cutoff_text); +static SOC_ENUM_SINGLE_DECL(rx2_dcb_cutoff_enum, LPASS_CDC_RX2_B4_CTL, 0, + dc_blocker_cutoff_text); +static SOC_ENUM_SINGLE_DECL(rx3_dcb_cutoff_enum, LPASS_CDC_RX3_B4_CTL, 0, + dc_blocker_cutoff_text); + +static const struct snd_kcontrol_new msm8916_wcd_digital_snd_controls[] = { + SOC_SINGLE_S8_TLV("RX1 Digital Volume", LPASS_CDC_RX1_VOL_CTL_B2_CTL, + -128, 127, digital_gain), + SOC_SINGLE_S8_TLV("RX2 Digital Volume", LPASS_CDC_RX2_VOL_CTL_B2_CTL, + -128, 127, digital_gain), + SOC_SINGLE_S8_TLV("RX3 Digital Volume", LPASS_CDC_RX3_VOL_CTL_B2_CTL, + -128, 127, digital_gain), + SOC_SINGLE_S8_TLV("TX1 Digital Volume", LPASS_CDC_TX1_VOL_CTL_GAIN, + -128, 127, digital_gain), + SOC_SINGLE_S8_TLV("TX2 Digital Volume", LPASS_CDC_TX2_VOL_CTL_GAIN, + -128, 127, digital_gain), + SOC_ENUM("TX1 HPF Cutoff", tx1_hpf_cutoff_enum), + SOC_ENUM("TX2 HPF Cutoff", tx2_hpf_cutoff_enum), + SOC_SINGLE("TX1 HPF Switch", LPASS_CDC_TX1_MUX_CTL, 3, 1, 0), + SOC_SINGLE("TX2 HPF Switch", LPASS_CDC_TX2_MUX_CTL, 3, 1, 0), + SOC_ENUM("RX1 DCB Cutoff", rx1_dcb_cutoff_enum), + SOC_ENUM("RX2 DCB Cutoff", rx2_dcb_cutoff_enum), + SOC_ENUM("RX3 DCB Cutoff", rx3_dcb_cutoff_enum), + SOC_SINGLE("RX1 DCB Switch", LPASS_CDC_RX1_B5_CTL, 2, 1, 0), + SOC_SINGLE("RX2 DCB Switch", LPASS_CDC_RX2_B5_CTL, 2, 1, 0), + SOC_SINGLE("RX3 DCB Switch", LPASS_CDC_RX3_B5_CTL, 2, 1, 0), + SOC_SINGLE("RX1 Mute Switch", LPASS_CDC_RX1_B6_CTL, 0, 1, 0), + SOC_SINGLE("RX2 Mute Switch", LPASS_CDC_RX2_B6_CTL, 0, 1, 0), + SOC_SINGLE("RX3 Mute Switch", LPASS_CDC_RX3_B6_CTL, 0, 1, 0), +}; + +static int msm8916_wcd_digital_enable_interpolator( + struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* apply the digital gain after the interpolator is enabled */ + usleep_range(10000, 10100); + snd_soc_write(codec, rx_gain_reg[w->shift], + snd_soc_read(codec, rx_gain_reg[w->shift])); + break; + } + return 0; +} + +static int msm8916_wcd_digital_enable_dec(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + unsigned int decimator = w->shift + 1; + u16 dec_reset_reg, tx_vol_ctl_reg, tx_mux_ctl_reg; + u8 dec_hpf_cut_of_freq; + + dec_reset_reg = LPASS_CDC_CLK_TX_RESET_B1_CTL; + tx_vol_ctl_reg = LPASS_CDC_TX1_VOL_CTL_CFG + 32 * (decimator - 1); + tx_mux_ctl_reg = LPASS_CDC_TX1_MUX_CTL + 32 * (decimator - 1); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enable TX digital mute */ + snd_soc_update_bits(codec, tx_vol_ctl_reg, + TX_VOL_CTL_CFG_MUTE_EN_MASK, + TX_VOL_CTL_CFG_MUTE_EN_ENABLE); + dec_hpf_cut_of_freq = snd_soc_read(codec, tx_mux_ctl_reg) & + TX_MUX_CTL_CUT_OFF_FREQ_MASK; + dec_hpf_cut_of_freq >>= TX_MUX_CTL_CUT_OFF_FREQ_SHIFT; + if (dec_hpf_cut_of_freq != TX_MUX_CTL_CF_NEG_3DB_150HZ) { + /* set cut of freq to CF_MIN_3DB_150HZ (0x1) */ + snd_soc_update_bits(codec, tx_mux_ctl_reg, + TX_MUX_CTL_CUT_OFF_FREQ_MASK, + TX_MUX_CTL_CF_NEG_3DB_150HZ); + } + break; + case SND_SOC_DAPM_POST_PMU: + /* enable HPF */ + snd_soc_update_bits(codec, tx_mux_ctl_reg, + TX_MUX_CTL_HPF_BP_SEL_MASK, + TX_MUX_CTL_HPF_BP_SEL_NO_BYPASS); + /* apply the digital gain after the decimator is enabled */ + snd_soc_write(codec, tx_gain_reg[w->shift], + snd_soc_read(codec, tx_gain_reg[w->shift])); + snd_soc_update_bits(codec, tx_vol_ctl_reg, + TX_VOL_CTL_CFG_MUTE_EN_MASK, 0); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, tx_vol_ctl_reg, + TX_VOL_CTL_CFG_MUTE_EN_MASK, + TX_VOL_CTL_CFG_MUTE_EN_ENABLE); + snd_soc_update_bits(codec, tx_mux_ctl_reg, + TX_MUX_CTL_HPF_BP_SEL_MASK, + TX_MUX_CTL_HPF_BP_SEL_BYPASS); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift, + 1 << w->shift); + snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift, 0x0); + snd_soc_update_bits(codec, tx_mux_ctl_reg, + TX_MUX_CTL_HPF_BP_SEL_MASK, + TX_MUX_CTL_HPF_BP_SEL_BYPASS); + snd_soc_update_bits(codec, tx_vol_ctl_reg, + TX_VOL_CTL_CFG_MUTE_EN_MASK, 0); + break; + } + + return 0; +} + +static int msm8916_wcd_digital_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + unsigned int dmic; + int ret; + /* get dmic number out of widget name */ + char *dmic_num = strpbrk(w->name, "12"); + + if (dmic_num == NULL) { + dev_err(codec->dev, "Invalid DMIC\n"); + return -EINVAL; + } + ret = kstrtouint(dmic_num, 10, &dmic); + if (ret < 0 || dmic > 2) { + dev_err(codec->dev, "Invalid DMIC line on the codec\n"); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, LPASS_CDC_CLK_DMIC_B1_CTL, + DMIC_B1_CTL_DMIC0_CLK_SEL_MASK, + DMIC_B1_CTL_DMIC0_CLK_SEL_DIV3); + switch (dmic) { + case 1: + snd_soc_update_bits(codec, LPASS_CDC_TX1_DMIC_CTL, + TXN_DMIC_CTL_CLK_SEL_MASK, + TXN_DMIC_CTL_CLK_SEL_DIV3); + break; + case 2: + snd_soc_update_bits(codec, LPASS_CDC_TX2_DMIC_CTL, + TXN_DMIC_CTL_CLK_SEL_MASK, + TXN_DMIC_CTL_CLK_SEL_DIV3); + break; + } + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget msm8916_wcd_digital_dapm_widgets[] = { + /*RX stuff */ + SND_SOC_DAPM_AIF_IN("I2S RX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("I2S RX2", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("I2S RX3", NULL, 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_OUTPUT("PDM_RX1"), + SND_SOC_DAPM_OUTPUT("PDM_RX2"), + SND_SOC_DAPM_OUTPUT("PDM_RX3"), + + SND_SOC_DAPM_INPUT("LPASS_PDM_TX"), + + SND_SOC_DAPM_MIXER("RX1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX2 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX3 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Interpolator */ + SND_SOC_DAPM_MIXER_E("RX1 INT", LPASS_CDC_CLK_RX_B1_CTL, 0, 0, NULL, + 0, msm8916_wcd_digital_enable_interpolator, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX2 INT", LPASS_CDC_CLK_RX_B1_CTL, 1, 0, NULL, + 0, msm8916_wcd_digital_enable_interpolator, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX3 INT", LPASS_CDC_CLK_RX_B1_CTL, 2, 0, NULL, + 0, msm8916_wcd_digital_enable_interpolator, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("RX1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX1 MIX1 INP3", SND_SOC_NOPM, 0, 0, + &rx_mix1_inp3_mux), + SND_SOC_DAPM_MUX("RX2 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx2_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX2 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx2_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX2 MIX1 INP3", SND_SOC_NOPM, 0, 0, + &rx2_mix1_inp3_mux), + SND_SOC_DAPM_MUX("RX3 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx3_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX3 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx3_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX3 MIX1 INP3", SND_SOC_NOPM, 0, 0, + &rx3_mix1_inp3_mux), + + /* TX */ + SND_SOC_DAPM_MIXER("ADC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("ADC3", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX_E("DEC1 MUX", LPASS_CDC_CLK_TX_CLK_EN_B1_CTL, 0, 0, + &dec1_mux, msm8916_wcd_digital_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("DEC2 MUX", LPASS_CDC_CLK_TX_CLK_EN_B1_CTL, 1, 0, + &dec2_mux, msm8916_wcd_digital_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT("I2S TX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("I2S TX2", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("I2S TX3", NULL, 0, SND_SOC_NOPM, 0, 0), + + /* Digital Mic Inputs */ + SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, + msm8916_wcd_digital_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0, + msm8916_wcd_digital_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("DMIC_CLK", LPASS_CDC_CLK_DMIC_B1_CTL, 0, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("RX_I2S_CLK", LPASS_CDC_CLK_RX_I2S_CTL, + 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("TX_I2S_CLK", LPASS_CDC_CLK_TX_I2S_CTL, 4, 0, + NULL, 0), + + SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PDM_CLK", LPASS_CDC_CLK_PDM_CTL, 0, 0, NULL, 0), + /* Connectivity Clock */ + SND_SOC_DAPM_SUPPLY_S("CDC_CONN", -2, LPASS_CDC_CLK_OTHR_CTL, 2, 0, + NULL, 0), + +}; + +static int msm8916_wcd_digital_get_clks(struct platform_device *pdev, + struct msm8916_wcd_digital_priv *priv) +{ + struct device *dev = &pdev->dev; + + priv->ahbclk = devm_clk_get(dev, "ahbix-clk"); + if (IS_ERR(priv->ahbclk)) { + dev_err(dev, "failed to get ahbix clk\n"); + return PTR_ERR(priv->ahbclk); + } + + priv->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(priv->mclk)) { + dev_err(dev, "failed to get mclk\n"); + return PTR_ERR(priv->mclk); + } + + return 0; +} + +static int msm8916_wcd_digital_codec_probe(struct snd_soc_codec *codec) +{ + struct msm8916_wcd_digital_priv *priv = dev_get_drvdata(codec->dev); + + snd_soc_codec_set_drvdata(codec, priv); + + return 0; +} + +static int msm8916_wcd_digital_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + u8 tx_fs_rate; + u8 rx_fs_rate; + + switch (params_rate(params)) { + case 8000: + tx_fs_rate = TX_I2S_CTL_TX_I2S_FS_RATE_F_8_KHZ; + rx_fs_rate = RX_I2S_CTL_RX_I2S_FS_RATE_F_8_KHZ; + break; + case 16000: + tx_fs_rate = TX_I2S_CTL_TX_I2S_FS_RATE_F_16_KHZ; + rx_fs_rate = RX_I2S_CTL_RX_I2S_FS_RATE_F_16_KHZ; + break; + case 32000: + tx_fs_rate = TX_I2S_CTL_TX_I2S_FS_RATE_F_32_KHZ; + rx_fs_rate = RX_I2S_CTL_RX_I2S_FS_RATE_F_32_KHZ; + break; + case 48000: + tx_fs_rate = TX_I2S_CTL_TX_I2S_FS_RATE_F_48_KHZ; + rx_fs_rate = RX_I2S_CTL_RX_I2S_FS_RATE_F_48_KHZ; + break; + default: + dev_err(dai->codec->dev, "Invalid sampling rate %d\n", + params_rate(params)); + return -EINVAL; + } + + switch (substream->stream) { + case SNDRV_PCM_STREAM_CAPTURE: + snd_soc_update_bits(dai->codec, LPASS_CDC_CLK_TX_I2S_CTL, + TX_I2S_CTL_TX_I2S_FS_RATE_MASK, tx_fs_rate); + break; + case SNDRV_PCM_STREAM_PLAYBACK: + snd_soc_update_bits(dai->codec, LPASS_CDC_CLK_RX_I2S_CTL, + RX_I2S_CTL_RX_I2S_FS_RATE_MASK, rx_fs_rate); + break; + default: + return -EINVAL; + } + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + snd_soc_update_bits(dai->codec, LPASS_CDC_CLK_TX_I2S_CTL, + TX_I2S_CTL_TX_I2S_MODE_MASK, + TX_I2S_CTL_TX_I2S_MODE_16); + snd_soc_update_bits(dai->codec, LPASS_CDC_CLK_RX_I2S_CTL, + RX_I2S_CTL_RX_I2S_MODE_MASK, + RX_I2S_CTL_RX_I2S_MODE_16); + break; + case SNDRV_PCM_FORMAT_S24_LE: + snd_soc_update_bits(dai->codec, LPASS_CDC_CLK_TX_I2S_CTL, + TX_I2S_CTL_TX_I2S_MODE_MASK, + TX_I2S_CTL_TX_I2S_MODE_32); + snd_soc_update_bits(dai->codec, LPASS_CDC_CLK_RX_I2S_CTL, + RX_I2S_CTL_RX_I2S_MODE_MASK, + RX_I2S_CTL_RX_I2S_MODE_32); + break; + default: + dev_err(dai->dev, "%s: wrong format selected\n", __func__); + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dapm_route msm8916_wcd_digital_audio_map[] = { + + {"I2S RX1", NULL, "AIF1 Playback"}, + {"I2S RX2", NULL, "AIF1 Playback"}, + {"I2S RX3", NULL, "AIF1 Playback"}, + + {"AIF1 Capture", NULL, "I2S TX1"}, + {"AIF1 Capture", NULL, "I2S TX2"}, + {"AIF1 Capture", NULL, "I2S TX3"}, + + /* Decimator Inputs */ + {"DEC1 MUX", "DMIC1", "DMIC1"}, + {"DEC1 MUX", "DMIC2", "DMIC2"}, + {"DEC1 MUX", "ADC1", "ADC1"}, + {"DEC1 MUX", "ADC2", "ADC2"}, + {"DEC1 MUX", "ADC3", "ADC3"}, + {"DEC1 MUX", NULL, "CDC_CONN"}, + + {"DEC2 MUX", "DMIC1", "DMIC1"}, + {"DEC2 MUX", "DMIC2", "DMIC2"}, + {"DEC2 MUX", "ADC1", "ADC1"}, + {"DEC2 MUX", "ADC2", "ADC2"}, + {"DEC2 MUX", "ADC3", "ADC3"}, + {"DEC2 MUX", NULL, "CDC_CONN"}, + + {"DMIC1", NULL, "DMIC_CLK"}, + {"DMIC2", NULL, "DMIC_CLK"}, + + {"I2S TX1", NULL, "DEC1 MUX"}, + {"I2S TX2", NULL, "DEC2 MUX"}, + + {"I2S TX1", NULL, "TX_I2S_CLK"}, + {"I2S TX2", NULL, "TX_I2S_CLK"}, + + {"TX_I2S_CLK", NULL, "MCLK"}, + {"TX_I2S_CLK", NULL, "PDM_CLK"}, + + {"ADC1", NULL, "LPASS_PDM_TX"}, + {"ADC2", NULL, "LPASS_PDM_TX"}, + {"ADC3", NULL, "LPASS_PDM_TX"}, + + {"I2S RX1", NULL, "RX_I2S_CLK"}, + {"I2S RX2", NULL, "RX_I2S_CLK"}, + {"I2S RX3", NULL, "RX_I2S_CLK"}, + + {"RX_I2S_CLK", NULL, "PDM_CLK"}, + {"RX_I2S_CLK", NULL, "MCLK"}, + {"RX_I2S_CLK", NULL, "CDC_CONN"}, + + /* RX1 PATH.. */ + {"PDM_RX1", NULL, "RX1 INT"}, + {"RX1 INT", NULL, "RX1 MIX1"}, + + {"RX1 MIX1", NULL, "RX1 MIX1 INP1"}, + {"RX1 MIX1", NULL, "RX1 MIX1 INP2"}, + {"RX1 MIX1", NULL, "RX1 MIX1 INP3"}, + + {"RX1 MIX1 INP1", "RX1", "I2S RX1"}, + {"RX1 MIX1 INP1", "RX2", "I2S RX2"}, + {"RX1 MIX1 INP1", "RX3", "I2S RX3"}, + + {"RX1 MIX1 INP2", "RX1", "I2S RX1"}, + {"RX1 MIX1 INP2", "RX2", "I2S RX2"}, + {"RX1 MIX1 INP2", "RX3", "I2S RX3"}, + + {"RX1 MIX1 INP3", "RX1", "I2S RX1"}, + {"RX1 MIX1 INP3", "RX2", "I2S RX2"}, + {"RX1 MIX1 INP3", "RX3", "I2S RX3"}, + + /* RX2 PATH */ + {"PDM_RX2", NULL, "RX2 INT"}, + {"RX2 INT", NULL, "RX2 MIX1"}, + + {"RX2 MIX1", NULL, "RX2 MIX1 INP1"}, + {"RX2 MIX1", NULL, "RX2 MIX1 INP2"}, + {"RX2 MIX1", NULL, "RX2 MIX1 INP3"}, + + {"RX2 MIX1 INP1", "RX1", "I2S RX1"}, + {"RX2 MIX1 INP1", "RX2", "I2S RX2"}, + {"RX2 MIX1 INP1", "RX3", "I2S RX3"}, + + {"RX2 MIX1 INP2", "RX1", "I2S RX1"}, + {"RX2 MIX1 INP2", "RX2", "I2S RX2"}, + {"RX2 MIX1 INP2", "RX3", "I2S RX3"}, + + {"RX2 MIX1 INP3", "RX1", "I2S RX1"}, + {"RX2 MIX1 INP3", "RX2", "I2S RX2"}, + {"RX2 MIX1 INP3", "RX3", "I2S RX3"}, + + /* RX3 PATH */ + {"PDM_RX3", NULL, "RX3 INT"}, + {"RX3 INT", NULL, "RX3 MIX1"}, + + {"RX3 MIX1", NULL, "RX3 MIX1 INP1"}, + {"RX3 MIX1", NULL, "RX3 MIX1 INP2"}, + {"RX3 MIX1", NULL, "RX3 MIX1 INP3"}, + + {"RX3 MIX1 INP1", "RX1", "I2S RX1"}, + {"RX3 MIX1 INP1", "RX2", "I2S RX2"}, + {"RX3 MIX1 INP1", "RX3", "I2S RX3"}, + + {"RX3 MIX1 INP2", "RX1", "I2S RX1"}, + {"RX3 MIX1 INP2", "RX2", "I2S RX2"}, + {"RX3 MIX1 INP2", "RX3", "I2S RX3"}, + + {"RX3 MIX1 INP3", "RX1", "I2S RX1"}, + {"RX3 MIX1 INP3", "RX2", "I2S RX2"}, + {"RX3 MIX1 INP3", "RX3", "I2S RX3"}, + +}; + +static int msm8916_wcd_digital_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct msm8916_wcd_digital_priv *msm8916_wcd; + unsigned long mclk_rate; + + msm8916_wcd = snd_soc_codec_get_drvdata(codec); + snd_soc_update_bits(codec, LPASS_CDC_CLK_MCLK_CTL, + MCLK_CTL_MCLK_EN_MASK, + MCLK_CTL_MCLK_EN_ENABLE); + snd_soc_update_bits(codec, LPASS_CDC_CLK_PDM_CTL, + LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_MASK, + LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_FB); + + mclk_rate = clk_get_rate(msm8916_wcd->mclk); + switch (mclk_rate) { + case 12288000: + snd_soc_update_bits(codec, LPASS_CDC_TOP_CTL, + TOP_CTL_DIG_MCLK_FREQ_MASK, + TOP_CTL_DIG_MCLK_FREQ_F_12_288MHZ); + break; + case 9600000: + snd_soc_update_bits(codec, LPASS_CDC_TOP_CTL, + TOP_CTL_DIG_MCLK_FREQ_MASK, + TOP_CTL_DIG_MCLK_FREQ_F_9_6MHZ); + break; + default: + dev_err(codec->dev, "Invalid mclk rate %ld\n", mclk_rate); + break; + } + return 0; +} + +static void msm8916_wcd_digital_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_update_bits(dai->codec, LPASS_CDC_CLK_PDM_CTL, + LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_MASK, 0); +} + +static struct snd_soc_dai_ops msm8916_wcd_digital_dai_ops = { + .startup = msm8916_wcd_digital_startup, + .shutdown = msm8916_wcd_digital_shutdown, + .hw_params = msm8916_wcd_digital_hw_params, +}; + +static struct snd_soc_dai_driver msm8916_wcd_digital_dai[] = { + [0] = { + .name = "msm8916_wcd_digital_i2s_rx1", + .id = 0, + .playback = { + .stream_name = "AIF1 Playback", + .rates = MSM8916_WCD_DIGITAL_RATES, + .formats = MSM8916_WCD_DIGITAL_FORMATS, + .channels_min = 1, + .channels_max = 3, + }, + .ops = &msm8916_wcd_digital_dai_ops, + }, + [1] = { + .name = "msm8916_wcd_digital_i2s_tx1", + .id = 1, + .capture = { + .stream_name = "AIF1 Capture", + .rates = MSM8916_WCD_DIGITAL_RATES, + .formats = MSM8916_WCD_DIGITAL_FORMATS, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &msm8916_wcd_digital_dai_ops, + }, +}; + +static struct snd_soc_codec_driver msm8916_wcd_digital = { + .probe = msm8916_wcd_digital_codec_probe, + .component_driver = { + .controls = msm8916_wcd_digital_snd_controls, + .num_controls = ARRAY_SIZE(msm8916_wcd_digital_snd_controls), + .dapm_widgets = msm8916_wcd_digital_dapm_widgets, + .num_dapm_widgets = + ARRAY_SIZE(msm8916_wcd_digital_dapm_widgets), + .dapm_routes = msm8916_wcd_digital_audio_map, + .num_dapm_routes = ARRAY_SIZE(msm8916_wcd_digital_audio_map), + }, +}; + +static const struct regmap_config msm8916_codec_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = LPASS_CDC_TX2_DMIC_CTL, + .cache_type = REGCACHE_FLAT, +}; + +static int msm8916_wcd_digital_probe(struct platform_device *pdev) +{ + struct msm8916_wcd_digital_priv *priv; + struct device *dev = &pdev->dev; + void __iomem *base; + struct resource *mem_res; + struct regmap *digital_map; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, mem_res); + if (IS_ERR(base)) + return PTR_ERR(base); + + digital_map = + devm_regmap_init_mmio(&pdev->dev, base, + &msm8916_codec_regmap_config); + if (IS_ERR(digital_map)) + return PTR_ERR(digital_map); + + ret = msm8916_wcd_digital_get_clks(pdev, priv); + if (ret < 0) + return ret; + + ret = clk_prepare_enable(priv->ahbclk); + if (ret < 0) { + dev_err(dev, "failed to enable ahbclk %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(priv->mclk); + if (ret < 0) { + dev_err(dev, "failed to enable mclk %d\n", ret); + return ret; + } + + dev_set_drvdata(dev, priv); + + return snd_soc_register_codec(dev, &msm8916_wcd_digital, + msm8916_wcd_digital_dai, + ARRAY_SIZE(msm8916_wcd_digital_dai)); +} + +static int msm8916_wcd_digital_remove(struct platform_device *pdev) +{ + struct msm8916_wcd_digital_priv *priv = dev_get_drvdata(&pdev->dev); + + snd_soc_unregister_codec(&pdev->dev); + clk_disable_unprepare(priv->mclk); + clk_disable_unprepare(priv->ahbclk); + + return 0; +} + +static const struct of_device_id msm8916_wcd_digital_match_table[] = { + { .compatible = "qcom,msm8916-wcd-digital-codec" }, + { } +}; + +MODULE_DEVICE_TABLE(of, msm8916_wcd_digital_match_table); + +static struct platform_driver msm8916_wcd_digital_driver = { + .driver = { + .name = "msm8916-wcd-digital-codec", + .of_match_table = msm8916_wcd_digital_match_table, + }, + .probe = msm8916_wcd_digital_probe, + .remove = msm8916_wcd_digital_remove, +}; + +module_platform_driver(msm8916_wcd_digital_driver); + +MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org>"); +MODULE_DESCRIPTION("MSM8916 WCD Digital Codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/nau8810.c b/sound/soc/codecs/nau8810.c new file mode 100644 index 000000000000..e45518629968 --- /dev/null +++ b/sound/soc/codecs/nau8810.c @@ -0,0 +1,884 @@ +/* + * nau8810.c -- NAU8810 ALSA Soc Audio driver + * + * Copyright 2016 Nuvoton Technology Corp. + * + * Author: David Lin <ctlin0@nuvoton.com> + * + * Based on WM8974.c + * + * 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/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.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> + +#include "nau8810.h" + +#define NAU_PLL_FREQ_MAX 100000000 +#define NAU_PLL_FREQ_MIN 90000000 +#define NAU_PLL_REF_MAX 33000000 +#define NAU_PLL_REF_MIN 8000000 +#define NAU_PLL_OPTOP_MIN 6 + + +static const int nau8810_mclk_scaler[] = { 10, 15, 20, 30, 40, 60, 80, 120 }; + +static const struct reg_default nau8810_reg_defaults[] = { + { NAU8810_REG_POWER1, 0x0000 }, + { NAU8810_REG_POWER2, 0x0000 }, + { NAU8810_REG_POWER3, 0x0000 }, + { NAU8810_REG_IFACE, 0x0050 }, + { NAU8810_REG_COMP, 0x0000 }, + { NAU8810_REG_CLOCK, 0x0140 }, + { NAU8810_REG_SMPLR, 0x0000 }, + { NAU8810_REG_DAC, 0x0000 }, + { NAU8810_REG_DACGAIN, 0x00FF }, + { NAU8810_REG_ADC, 0x0100 }, + { NAU8810_REG_ADCGAIN, 0x00FF }, + { NAU8810_REG_EQ1, 0x012C }, + { NAU8810_REG_EQ2, 0x002C }, + { NAU8810_REG_EQ3, 0x002C }, + { NAU8810_REG_EQ4, 0x002C }, + { NAU8810_REG_EQ5, 0x002C }, + { NAU8810_REG_DACLIM1, 0x0032 }, + { NAU8810_REG_DACLIM2, 0x0000 }, + { NAU8810_REG_NOTCH1, 0x0000 }, + { NAU8810_REG_NOTCH2, 0x0000 }, + { NAU8810_REG_NOTCH3, 0x0000 }, + { NAU8810_REG_NOTCH4, 0x0000 }, + { NAU8810_REG_ALC1, 0x0038 }, + { NAU8810_REG_ALC2, 0x000B }, + { NAU8810_REG_ALC3, 0x0032 }, + { NAU8810_REG_NOISEGATE, 0x0000 }, + { NAU8810_REG_PLLN, 0x0008 }, + { NAU8810_REG_PLLK1, 0x000C }, + { NAU8810_REG_PLLK2, 0x0093 }, + { NAU8810_REG_PLLK3, 0x00E9 }, + { NAU8810_REG_ATTEN, 0x0000 }, + { NAU8810_REG_INPUT_SIGNAL, 0x0003 }, + { NAU8810_REG_PGAGAIN, 0x0010 }, + { NAU8810_REG_ADCBOOST, 0x0100 }, + { NAU8810_REG_OUTPUT, 0x0002 }, + { NAU8810_REG_SPKMIX, 0x0001 }, + { NAU8810_REG_SPKGAIN, 0x0039 }, + { NAU8810_REG_MONOMIX, 0x0001 }, + { NAU8810_REG_POWER4, 0x0000 }, + { NAU8810_REG_TSLOTCTL1, 0x0000 }, + { NAU8810_REG_TSLOTCTL2, 0x0020 }, + { NAU8810_REG_DEVICE_REVID, 0x0000 }, + { NAU8810_REG_I2C_DEVICEID, 0x001A }, + { NAU8810_REG_ADDITIONID, 0x00CA }, + { NAU8810_REG_RESERVE, 0x0124 }, + { NAU8810_REG_OUTCTL, 0x0001 }, + { NAU8810_REG_ALC1ENHAN1, 0x0010 }, + { NAU8810_REG_ALC1ENHAN2, 0x0000 }, + { NAU8810_REG_MISCCTL, 0x0000 }, + { NAU8810_REG_OUTTIEOFF, 0x0000 }, + { NAU8810_REG_AGCP2POUT, 0x0000 }, + { NAU8810_REG_AGCPOUT, 0x0000 }, + { NAU8810_REG_AMTCTL, 0x0000 }, + { NAU8810_REG_OUTTIEOFFMAN, 0x0000 }, +}; + +static bool nau8810_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8810_REG_RESET ... NAU8810_REG_SMPLR: + case NAU8810_REG_DAC ... NAU8810_REG_DACGAIN: + case NAU8810_REG_ADC ... NAU8810_REG_ADCGAIN: + case NAU8810_REG_EQ1 ... NAU8810_REG_EQ5: + case NAU8810_REG_DACLIM1 ... NAU8810_REG_DACLIM2: + case NAU8810_REG_NOTCH1 ... NAU8810_REG_NOTCH4: + case NAU8810_REG_ALC1 ... NAU8810_REG_ATTEN: + case NAU8810_REG_INPUT_SIGNAL ... NAU8810_REG_PGAGAIN: + case NAU8810_REG_ADCBOOST: + case NAU8810_REG_OUTPUT ... NAU8810_REG_SPKMIX: + case NAU8810_REG_SPKGAIN: + case NAU8810_REG_MONOMIX: + case NAU8810_REG_POWER4 ... NAU8810_REG_TSLOTCTL2: + case NAU8810_REG_DEVICE_REVID ... NAU8810_REG_RESERVE: + case NAU8810_REG_OUTCTL ... NAU8810_REG_ALC1ENHAN2: + case NAU8810_REG_MISCCTL: + case NAU8810_REG_OUTTIEOFF ... NAU8810_REG_OUTTIEOFFMAN: + return true; + default: + return false; + } +} + +static bool nau8810_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8810_REG_RESET ... NAU8810_REG_SMPLR: + case NAU8810_REG_DAC ... NAU8810_REG_DACGAIN: + case NAU8810_REG_ADC ... NAU8810_REG_ADCGAIN: + case NAU8810_REG_EQ1 ... NAU8810_REG_EQ5: + case NAU8810_REG_DACLIM1 ... NAU8810_REG_DACLIM2: + case NAU8810_REG_NOTCH1 ... NAU8810_REG_NOTCH4: + case NAU8810_REG_ALC1 ... NAU8810_REG_ATTEN: + case NAU8810_REG_INPUT_SIGNAL ... NAU8810_REG_PGAGAIN: + case NAU8810_REG_ADCBOOST: + case NAU8810_REG_OUTPUT ... NAU8810_REG_SPKMIX: + case NAU8810_REG_SPKGAIN: + case NAU8810_REG_MONOMIX: + case NAU8810_REG_POWER4 ... NAU8810_REG_TSLOTCTL2: + case NAU8810_REG_OUTCTL ... NAU8810_REG_ALC1ENHAN2: + case NAU8810_REG_MISCCTL: + case NAU8810_REG_OUTTIEOFF ... NAU8810_REG_OUTTIEOFFMAN: + return true; + default: + return false; + } +} + +static bool nau8810_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8810_REG_RESET: + case NAU8810_REG_DEVICE_REVID ... NAU8810_REG_RESERVE: + return true; + default: + return false; + } +} + +/* The EQ parameters get function is to get the 5 band equalizer control. + * The regmap raw read can't work here because regmap doesn't provide + * value format for value width of 9 bits. Therefore, the driver reads data + * from cache and makes value format according to the endianness of + * bytes type control element. + */ +static int nau8810_eq_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec); + struct soc_bytes_ext *params = (void *)kcontrol->private_value; + int i, reg, reg_val; + u16 *val; + + val = (u16 *)ucontrol->value.bytes.data; + reg = NAU8810_REG_EQ1; + for (i = 0; i < params->max / sizeof(u16); i++) { + regmap_read(nau8810->regmap, reg + i, ®_val); + /* conversion of 16-bit integers between native CPU format + * and big endian format + */ + reg_val = cpu_to_be16(reg_val); + memcpy(val + i, ®_val, sizeof(reg_val)); + } + + return 0; +} + +/* The EQ parameters put function is to make configuration of 5 band equalizer + * control. These configuration includes central frequency, equalizer gain, + * cut-off frequency, bandwidth control, and equalizer path. + * The regmap raw write can't work here because regmap doesn't provide + * register and value format for register with address 7 bits and value 9 bits. + * Therefore, the driver makes value format according to the endianness of + * bytes type control element and writes data to codec. + */ +static int nau8810_eq_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec); + struct soc_bytes_ext *params = (void *)kcontrol->private_value; + void *data; + u16 *val, value; + int i, reg, ret; + + data = kmemdup(ucontrol->value.bytes.data, + params->max, GFP_KERNEL | GFP_DMA); + if (!data) + return -ENOMEM; + + val = (u16 *)data; + reg = NAU8810_REG_EQ1; + for (i = 0; i < params->max / sizeof(u16); i++) { + /* conversion of 16-bit integers between native CPU format + * and big endian format + */ + value = be16_to_cpu(*(val + i)); + ret = regmap_write(nau8810->regmap, reg + i, value); + if (ret) { + dev_err(codec->dev, "EQ configuration fail, register: %x ret: %d\n", + reg + i, ret); + kfree(data); + return ret; + } + } + kfree(data); + + return 0; +} + +static const char * const nau8810_companding[] = { + "Off", "NC", "u-law", "A-law" }; + +static const struct soc_enum nau8810_companding_adc_enum = + SOC_ENUM_SINGLE(NAU8810_REG_COMP, NAU8810_ADCCM_SFT, + ARRAY_SIZE(nau8810_companding), nau8810_companding); + +static const struct soc_enum nau8810_companding_dac_enum = + SOC_ENUM_SINGLE(NAU8810_REG_COMP, NAU8810_DACCM_SFT, + ARRAY_SIZE(nau8810_companding), nau8810_companding); + +static const char * const nau8810_deemp[] = { + "None", "32kHz", "44.1kHz", "48kHz" }; + +static const struct soc_enum nau8810_deemp_enum = + SOC_ENUM_SINGLE(NAU8810_REG_DAC, NAU8810_DEEMP_SFT, + ARRAY_SIZE(nau8810_deemp), nau8810_deemp); + +static const char * const nau8810_eqmode[] = {"Capture", "Playback" }; + +static const struct soc_enum nau8810_eqmode_enum = + SOC_ENUM_SINGLE(NAU8810_REG_EQ1, NAU8810_EQM_SFT, + ARRAY_SIZE(nau8810_eqmode), nau8810_eqmode); + +static const char * const nau8810_alc[] = {"Normal", "Limiter" }; + +static const struct soc_enum nau8810_alc_enum = + SOC_ENUM_SINGLE(NAU8810_REG_ALC3, NAU8810_ALCM_SFT, + ARRAY_SIZE(nau8810_alc), nau8810_alc); + +static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1); +static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); +static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0); +static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0); + +static const struct snd_kcontrol_new nau8810_snd_controls[] = { + SOC_ENUM("ADC Companding", nau8810_companding_adc_enum), + SOC_ENUM("DAC Companding", nau8810_companding_dac_enum), + SOC_ENUM("DAC De-emphasis", nau8810_deemp_enum), + + SOC_ENUM("EQ Function", nau8810_eqmode_enum), + SND_SOC_BYTES_EXT("EQ Parameters", 10, + nau8810_eq_get, nau8810_eq_put), + + SOC_SINGLE("DAC Inversion Switch", NAU8810_REG_DAC, + NAU8810_DACPL_SFT, 1, 0), + SOC_SINGLE_TLV("Playback Volume", NAU8810_REG_DACGAIN, + NAU8810_DACGAIN_SFT, 0xff, 0, digital_tlv), + + SOC_SINGLE("High Pass Filter Switch", NAU8810_REG_ADC, + NAU8810_HPFEN_SFT, 1, 0), + SOC_SINGLE("High Pass Cut Off", NAU8810_REG_ADC, + NAU8810_HPF_SFT, 0x7, 0), + + SOC_SINGLE("ADC Inversion Switch", NAU8810_REG_ADC, + NAU8810_ADCPL_SFT, 1, 0), + SOC_SINGLE_TLV("Capture Volume", NAU8810_REG_ADCGAIN, + NAU8810_ADCGAIN_SFT, 0xff, 0, digital_tlv), + + SOC_SINGLE_TLV("EQ1 Volume", NAU8810_REG_EQ1, + NAU8810_EQ1GC_SFT, 0x18, 1, eq_tlv), + SOC_SINGLE_TLV("EQ2 Volume", NAU8810_REG_EQ2, + NAU8810_EQ2GC_SFT, 0x18, 1, eq_tlv), + SOC_SINGLE_TLV("EQ3 Volume", NAU8810_REG_EQ3, + NAU8810_EQ3GC_SFT, 0x18, 1, eq_tlv), + SOC_SINGLE_TLV("EQ4 Volume", NAU8810_REG_EQ4, + NAU8810_EQ4GC_SFT, 0x18, 1, eq_tlv), + SOC_SINGLE_TLV("EQ5 Volume", NAU8810_REG_EQ5, + NAU8810_EQ5GC_SFT, 0x18, 1, eq_tlv), + + SOC_SINGLE("DAC Limiter Switch", NAU8810_REG_DACLIM1, + NAU8810_DACLIMEN_SFT, 1, 0), + SOC_SINGLE("DAC Limiter Decay", NAU8810_REG_DACLIM1, + NAU8810_DACLIMDCY_SFT, 0xf, 0), + SOC_SINGLE("DAC Limiter Attack", NAU8810_REG_DACLIM1, + NAU8810_DACLIMATK_SFT, 0xf, 0), + SOC_SINGLE("DAC Limiter Threshold", NAU8810_REG_DACLIM2, + NAU8810_DACLIMTHL_SFT, 0x7, 0), + SOC_SINGLE("DAC Limiter Boost", NAU8810_REG_DACLIM2, + NAU8810_DACLIMBST_SFT, 0xf, 0), + + SOC_ENUM("ALC Mode", nau8810_alc_enum), + SOC_SINGLE("ALC Enable Switch", NAU8810_REG_ALC1, + NAU8810_ALCEN_SFT, 1, 0), + SOC_SINGLE("ALC Max Volume", NAU8810_REG_ALC1, + NAU8810_ALCMXGAIN_SFT, 0x7, 0), + SOC_SINGLE("ALC Min Volume", NAU8810_REG_ALC1, + NAU8810_ALCMINGAIN_SFT, 0x7, 0), + SOC_SINGLE("ALC ZC Switch", NAU8810_REG_ALC2, + NAU8810_ALCZC_SFT, 1, 0), + SOC_SINGLE("ALC Hold", NAU8810_REG_ALC2, + NAU8810_ALCHT_SFT, 0xf, 0), + SOC_SINGLE("ALC Target", NAU8810_REG_ALC2, + NAU8810_ALCSL_SFT, 0xf, 0), + SOC_SINGLE("ALC Decay", NAU8810_REG_ALC3, + NAU8810_ALCDCY_SFT, 0xf, 0), + SOC_SINGLE("ALC Attack", NAU8810_REG_ALC3, + NAU8810_ALCATK_SFT, 0xf, 0), + SOC_SINGLE("ALC Noise Gate Switch", NAU8810_REG_NOISEGATE, + NAU8810_ALCNEN_SFT, 1, 0), + SOC_SINGLE("ALC Noise Gate Threshold", NAU8810_REG_NOISEGATE, + NAU8810_ALCNTH_SFT, 0x7, 0), + + SOC_SINGLE("PGA ZC Switch", NAU8810_REG_PGAGAIN, + NAU8810_PGAZC_SFT, 1, 0), + SOC_SINGLE_TLV("PGA Volume", NAU8810_REG_PGAGAIN, + NAU8810_PGAGAIN_SFT, 0x3f, 0, inpga_tlv), + + SOC_SINGLE("Speaker ZC Switch", NAU8810_REG_SPKGAIN, + NAU8810_SPKZC_SFT, 1, 0), + SOC_SINGLE("Speaker Mute Switch", NAU8810_REG_SPKGAIN, + NAU8810_SPKMT_SFT, 1, 0), + SOC_SINGLE_TLV("Speaker Volume", NAU8810_REG_SPKGAIN, + NAU8810_SPKGAIN_SFT, 0x3f, 0, spk_tlv), + + SOC_SINGLE("Capture Boost(+20dB)", NAU8810_REG_ADCBOOST, + NAU8810_PGABST_SFT, 1, 0), + SOC_SINGLE("Mono Mute Switch", NAU8810_REG_MONOMIX, + NAU8810_MOUTMXMT_SFT, 1, 0), + + SOC_SINGLE("DAC Oversampling Rate(128x) Switch", NAU8810_REG_DAC, + NAU8810_DACOS_SFT, 1, 0), + SOC_SINGLE("ADC Oversampling Rate(128x) Switch", NAU8810_REG_ADC, + NAU8810_ADCOS_SFT, 1, 0), +}; + +/* Speaker Output Mixer */ +static const struct snd_kcontrol_new nau8810_speaker_mixer_controls[] = { + SOC_DAPM_SINGLE("Line Bypass Switch", NAU8810_REG_SPKMIX, + NAU8810_BYPSPK_SFT, 1, 0), + SOC_DAPM_SINGLE("PCM Playback Switch", NAU8810_REG_SPKMIX, + NAU8810_DACSPK_SFT, 1, 0), +}; + +/* Mono Output Mixer */ +static const struct snd_kcontrol_new nau8810_mono_mixer_controls[] = { + SOC_DAPM_SINGLE("Line Bypass Switch", NAU8810_REG_MONOMIX, + NAU8810_BYPMOUT_SFT, 1, 0), + SOC_DAPM_SINGLE("PCM Playback Switch", NAU8810_REG_MONOMIX, + NAU8810_DACMOUT_SFT, 1, 0), +}; + +/* PGA Mute */ +static const struct snd_kcontrol_new nau8810_inpga_mute[] = { + SOC_DAPM_SINGLE("PGA Mute Switch", NAU8810_REG_PGAGAIN, + NAU8810_PGAMT_SFT, 1, 0), +}; + +/* Input PGA */ +static const struct snd_kcontrol_new nau8810_inpga[] = { + SOC_DAPM_SINGLE("MicN Switch", NAU8810_REG_INPUT_SIGNAL, + NAU8810_NMICPGA_SFT, 1, 0), + SOC_DAPM_SINGLE("MicP Switch", NAU8810_REG_INPUT_SIGNAL, + NAU8810_PMICPGA_SFT, 1, 0), +}; + +/* Mic Input boost vol */ +static const struct snd_kcontrol_new nau8810_mic_boost_controls = + SOC_DAPM_SINGLE("Mic Volume", NAU8810_REG_ADCBOOST, + NAU8810_PMICBSTGAIN_SFT, 0x7, 0); + +/* Loopback Switch */ +static const struct snd_kcontrol_new nau8810_loopback = + SOC_DAPM_SINGLE("Switch", NAU8810_REG_COMP, + NAU8810_ADDAP_SFT, 1, 0); + +static int check_mclk_select_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm); + struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec); + unsigned int value; + + regmap_read(nau8810->regmap, NAU8810_REG_CLOCK, &value); + return (value & NAU8810_CLKM_MASK); +} + +static const struct snd_soc_dapm_widget nau8810_dapm_widgets[] = { + SND_SOC_DAPM_MIXER("Speaker Mixer", NAU8810_REG_POWER3, + NAU8810_SPKMX_EN_SFT, 0, &nau8810_speaker_mixer_controls[0], + ARRAY_SIZE(nau8810_speaker_mixer_controls)), + SND_SOC_DAPM_MIXER("Mono Mixer", NAU8810_REG_POWER3, + NAU8810_MOUTMX_EN_SFT, 0, &nau8810_mono_mixer_controls[0], + ARRAY_SIZE(nau8810_mono_mixer_controls)), + SND_SOC_DAPM_DAC("DAC", "HiFi Playback", NAU8810_REG_POWER3, + NAU8810_DAC_EN_SFT, 0), + SND_SOC_DAPM_ADC("ADC", "HiFi Capture", NAU8810_REG_POWER2, + NAU8810_ADC_EN_SFT, 0), + SND_SOC_DAPM_PGA("SpkN Out", NAU8810_REG_POWER3, + NAU8810_NSPK_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("SpkP Out", NAU8810_REG_POWER3, + NAU8810_PSPK_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("Mono Out", NAU8810_REG_POWER3, + NAU8810_MOUT_EN_SFT, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("Input PGA", NAU8810_REG_POWER2, + NAU8810_PGA_EN_SFT, 0, nau8810_inpga, + ARRAY_SIZE(nau8810_inpga)), + SND_SOC_DAPM_MIXER("Input Boost Stage", NAU8810_REG_POWER2, + NAU8810_BST_EN_SFT, 0, nau8810_inpga_mute, + ARRAY_SIZE(nau8810_inpga_mute)), + + SND_SOC_DAPM_SUPPLY("Mic Bias", NAU8810_REG_POWER1, + NAU8810_MICBIAS_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL", NAU8810_REG_POWER1, + NAU8810_PLL_EN_SFT, 0, NULL, 0), + + SND_SOC_DAPM_SWITCH("Digital Loopback", SND_SOC_NOPM, 0, 0, + &nau8810_loopback), + + SND_SOC_DAPM_INPUT("MICN"), + SND_SOC_DAPM_INPUT("MICP"), + SND_SOC_DAPM_OUTPUT("MONOOUT"), + SND_SOC_DAPM_OUTPUT("SPKOUTP"), + SND_SOC_DAPM_OUTPUT("SPKOUTN"), +}; + +static const struct snd_soc_dapm_route nau8810_dapm_routes[] = { + {"DAC", NULL, "PLL", check_mclk_select_pll}, + + /* Mono output mixer */ + {"Mono Mixer", "PCM Playback Switch", "DAC"}, + {"Mono Mixer", "Line Bypass Switch", "Input Boost Stage"}, + + /* Speaker output mixer */ + {"Speaker Mixer", "PCM Playback Switch", "DAC"}, + {"Speaker Mixer", "Line Bypass Switch", "Input Boost Stage"}, + + /* Outputs */ + {"Mono Out", NULL, "Mono Mixer"}, + {"MONOOUT", NULL, "Mono Out"}, + {"SpkN Out", NULL, "Speaker Mixer"}, + {"SpkP Out", NULL, "Speaker Mixer"}, + {"SPKOUTN", NULL, "SpkN Out"}, + {"SPKOUTP", NULL, "SpkP Out"}, + + /* Input Boost Stage */ + {"ADC", NULL, "Input Boost Stage"}, + {"ADC", NULL, "PLL", check_mclk_select_pll}, + {"Input Boost Stage", NULL, "Input PGA"}, + {"Input Boost Stage", NULL, "MICP"}, + + /* Input PGA */ + {"Input PGA", NULL, "Mic Bias"}, + {"Input PGA", "MicN Switch", "MICN"}, + {"Input PGA", "MicP Switch", "MICP"}, + + /* Digital Looptack */ + {"Digital Loopback", "Switch", "ADC"}, + {"DAC", NULL, "Digital Loopback"}, +}; + +static int nau8810_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec); + + nau8810->clk_id = clk_id; + nau8810->sysclk = freq; + dev_dbg(nau8810->dev, "master sysclk %dHz, source %s\n", + freq, clk_id == NAU8810_SCLK_PLL ? "PLL" : "MCLK"); + + return 0; +} + +static int nau88l0_calc_pll(unsigned int pll_in, + unsigned int fs, struct nau8810_pll *pll_param) +{ + u64 f2, f2_max, pll_ratio; + int i, scal_sel; + + if (pll_in > NAU_PLL_REF_MAX || pll_in < NAU_PLL_REF_MIN) + return -EINVAL; + + f2_max = 0; + scal_sel = ARRAY_SIZE(nau8810_mclk_scaler); + for (i = 0; i < ARRAY_SIZE(nau8810_mclk_scaler); i++) { + f2 = 256 * fs * 4 * nau8810_mclk_scaler[i] / 10; + if (f2 > NAU_PLL_FREQ_MIN && f2 < NAU_PLL_FREQ_MAX && + f2_max < f2) { + f2_max = f2; + scal_sel = i; + } + } + if (ARRAY_SIZE(nau8810_mclk_scaler) == scal_sel) + return -EINVAL; + pll_param->mclk_scaler = scal_sel; + f2 = f2_max; + + /* Calculate the PLL 4-bit integer input and the PLL 24-bit fractional + * input; round up the 24+4bit. + */ + pll_ratio = div_u64(f2 << 28, pll_in); + pll_param->pre_factor = 0; + if (((pll_ratio >> 28) & 0xF) < NAU_PLL_OPTOP_MIN) { + pll_ratio <<= 1; + pll_param->pre_factor = 1; + } + pll_param->pll_int = (pll_ratio >> 28) & 0xF; + pll_param->pll_frac = ((pll_ratio & 0xFFFFFFF) >> 4); + + return 0; +} + +static int nau8810_set_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec); + struct regmap *map = nau8810->regmap; + struct nau8810_pll *pll_param = &nau8810->pll; + int ret, fs; + + fs = freq_out / 256; + ret = nau88l0_calc_pll(freq_in, fs, pll_param); + if (ret < 0) { + dev_err(nau8810->dev, "Unsupported input clock %d\n", freq_in); + return ret; + } + dev_info(nau8810->dev, "pll_int=%x pll_frac=%x mclk_scaler=%x pre_factor=%x\n", + pll_param->pll_int, pll_param->pll_frac, pll_param->mclk_scaler, + pll_param->pre_factor); + + regmap_update_bits(map, NAU8810_REG_PLLN, + NAU8810_PLLMCLK_DIV2 | NAU8810_PLLN_MASK, + (pll_param->pre_factor ? NAU8810_PLLMCLK_DIV2 : 0) | + pll_param->pll_int); + regmap_write(map, NAU8810_REG_PLLK1, + (pll_param->pll_frac >> NAU8810_PLLK1_SFT) & + NAU8810_PLLK1_MASK); + regmap_write(map, NAU8810_REG_PLLK2, + (pll_param->pll_frac >> NAU8810_PLLK2_SFT) & + NAU8810_PLLK2_MASK); + regmap_write(map, NAU8810_REG_PLLK3, + pll_param->pll_frac & NAU8810_PLLK3_MASK); + regmap_update_bits(map, NAU8810_REG_CLOCK, NAU8810_MCLKSEL_MASK, + pll_param->mclk_scaler << NAU8810_MCLKSEL_SFT); + regmap_update_bits(map, NAU8810_REG_CLOCK, + NAU8810_CLKM_MASK, NAU8810_CLKM_PLL); + + return 0; +} + +static int nau8810_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec); + u16 ctrl1_val = 0, ctrl2_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + ctrl2_val |= NAU8810_CLKIO_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ctrl1_val |= NAU8810_AIFMT_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + ctrl1_val |= NAU8810_AIFMT_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + ctrl1_val |= NAU8810_AIFMT_PCM_A; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + ctrl1_val |= NAU8810_BCLKP_IB | NAU8810_FSP_IF; + break; + case SND_SOC_DAIFMT_IB_NF: + ctrl1_val |= NAU8810_BCLKP_IB; + break; + case SND_SOC_DAIFMT_NB_IF: + ctrl1_val |= NAU8810_FSP_IF; + break; + default: + return -EINVAL; + } + + regmap_update_bits(nau8810->regmap, NAU8810_REG_IFACE, + NAU8810_AIFMT_MASK | NAU8810_FSP_IF | + NAU8810_BCLKP_IB, ctrl1_val); + regmap_update_bits(nau8810->regmap, NAU8810_REG_CLOCK, + NAU8810_CLKIO_MASK, ctrl2_val); + + return 0; +} + +static int nau8810_mclk_clkdiv(struct nau8810 *nau8810, int rate) +{ + int i, sclk, imclk = rate * 256, div = 0; + + if (!nau8810->sysclk) { + dev_err(nau8810->dev, "Make mclk div configuration fail because of invalid system clock\n"); + return -EINVAL; + } + + /* Configure the master clock prescaler div to make system + * clock to approximate the internal master clock (IMCLK); + * and large or equal to IMCLK. + */ + for (i = 1; i < ARRAY_SIZE(nau8810_mclk_scaler); i++) { + sclk = (nau8810->sysclk * 10) / + nau8810_mclk_scaler[i]; + if (sclk < imclk) + break; + div = i; + } + dev_dbg(nau8810->dev, + "master clock prescaler %x for fs %d\n", div, rate); + + /* master clock from MCLK and disable PLL */ + regmap_update_bits(nau8810->regmap, NAU8810_REG_CLOCK, + NAU8810_MCLKSEL_MASK, (div << NAU8810_MCLKSEL_SFT)); + regmap_update_bits(nau8810->regmap, NAU8810_REG_CLOCK, + NAU8810_CLKM_MASK, NAU8810_CLKM_MCLK); + + return 0; +} + +static int nau8810_pcm_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 nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec); + int val_len = 0, val_rate = 0, ret = 0; + + switch (params_width(params)) { + case 16: + break; + case 20: + val_len |= NAU8810_WLEN_20; + break; + case 24: + val_len |= NAU8810_WLEN_24; + break; + case 32: + val_len |= NAU8810_WLEN_32; + break; + } + + switch (params_rate(params)) { + case 8000: + val_rate |= NAU8810_SMPLR_8K; + break; + case 11025: + val_rate |= NAU8810_SMPLR_12K; + break; + case 16000: + val_rate |= NAU8810_SMPLR_16K; + break; + case 22050: + val_rate |= NAU8810_SMPLR_24K; + break; + case 32000: + val_rate |= NAU8810_SMPLR_32K; + break; + case 44100: + case 48000: + break; + } + + regmap_update_bits(nau8810->regmap, NAU8810_REG_IFACE, + NAU8810_WLEN_MASK, val_len); + regmap_update_bits(nau8810->regmap, NAU8810_REG_SMPLR, + NAU8810_SMPLR_MASK, val_rate); + + /* If the master clock is from MCLK, provide the runtime FS for driver + * to get the master clock prescaler configuration. + */ + if (nau8810->clk_id == NAU8810_SCLK_MCLK) { + ret = nau8810_mclk_clkdiv(nau8810, params_rate(params)); + if (ret < 0) + dev_err(nau8810->dev, "MCLK div configuration fail\n"); + } + + return ret; +} + +static int nau8810_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec); + struct regmap *map = nau8810->regmap; + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + regmap_update_bits(map, NAU8810_REG_POWER1, + NAU8810_REFIMP_MASK, NAU8810_REFIMP_80K); + break; + + case SND_SOC_BIAS_STANDBY: + regmap_update_bits(map, NAU8810_REG_POWER1, + NAU8810_IOBUF_EN | NAU8810_ABIAS_EN, + NAU8810_IOBUF_EN | NAU8810_ABIAS_EN); + + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { + regcache_sync(map); + regmap_update_bits(map, NAU8810_REG_POWER1, + NAU8810_REFIMP_MASK, NAU8810_REFIMP_3K); + mdelay(100); + } + regmap_update_bits(map, NAU8810_REG_POWER1, + NAU8810_REFIMP_MASK, NAU8810_REFIMP_300K); + break; + + case SND_SOC_BIAS_OFF: + regmap_write(map, NAU8810_REG_POWER1, 0); + regmap_write(map, NAU8810_REG_POWER2, 0); + regmap_write(map, NAU8810_REG_POWER3, 0); + break; + } + + return 0; +} + + +#define NAU8810_RATES (SNDRV_PCM_RATE_8000_48000) + +#define NAU8810_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops nau8810_ops = { + .hw_params = nau8810_pcm_hw_params, + .set_fmt = nau8810_set_dai_fmt, + .set_sysclk = nau8810_set_sysclk, + .set_pll = nau8810_set_pll, +}; + +static struct snd_soc_dai_driver nau8810_dai = { + .name = "nau8810-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, /* Only 1 channel of data */ + .rates = NAU8810_RATES, + .formats = NAU8810_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, /* Only 1 channel of data */ + .rates = NAU8810_RATES, + .formats = NAU8810_FORMATS, + }, + .ops = &nau8810_ops, + .symmetric_rates = 1, +}; + +static const struct regmap_config nau8810_regmap_config = { + .reg_bits = 7, + .val_bits = 9, + + .max_register = NAU8810_REG_MAX, + .readable_reg = nau8810_readable_reg, + .writeable_reg = nau8810_writeable_reg, + .volatile_reg = nau8810_volatile_reg, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = nau8810_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(nau8810_reg_defaults), +}; + +static struct snd_soc_codec_driver nau8810_codec_driver = { + .set_bias_level = nau8810_set_bias_level, + .suspend_bias_off = true, + + .component_driver = { + .controls = nau8810_snd_controls, + .num_controls = ARRAY_SIZE(nau8810_snd_controls), + .dapm_widgets = nau8810_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(nau8810_dapm_widgets), + .dapm_routes = nau8810_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(nau8810_dapm_routes), + }, +}; + +static int nau8810_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct device *dev = &i2c->dev; + struct nau8810 *nau8810 = dev_get_platdata(dev); + + if (!nau8810) { + nau8810 = devm_kzalloc(dev, sizeof(*nau8810), GFP_KERNEL); + if (!nau8810) + return -ENOMEM; + } + i2c_set_clientdata(i2c, nau8810); + + nau8810->regmap = devm_regmap_init_i2c(i2c, &nau8810_regmap_config); + if (IS_ERR(nau8810->regmap)) + return PTR_ERR(nau8810->regmap); + nau8810->dev = dev; + + regmap_write(nau8810->regmap, NAU8810_REG_RESET, 0x00); + + return snd_soc_register_codec(dev, + &nau8810_codec_driver, &nau8810_dai, 1); +} + +static int nau8810_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + + return 0; +} + +static const struct i2c_device_id nau8810_i2c_id[] = { + { "nau8810", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, nau8810_i2c_id); + +#ifdef CONFIG_OF +static const struct of_device_id nau8810_of_match[] = { + { .compatible = "nuvoton,nau8810", }, + { } +}; +MODULE_DEVICE_TABLE(of, nau8810_of_match); +#endif + +static struct i2c_driver nau8810_i2c_driver = { + .driver = { + .name = "nau8810", + .of_match_table = of_match_ptr(nau8810_of_match), + }, + .probe = nau8810_i2c_probe, + .remove = nau8810_i2c_remove, + .id_table = nau8810_i2c_id, +}; + +module_i2c_driver(nau8810_i2c_driver); + +MODULE_DESCRIPTION("ASoC NAU8810 driver"); +MODULE_AUTHOR("David Lin <ctlin0@nuvoton.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/nau8810.h b/sound/soc/codecs/nau8810.h new file mode 100644 index 000000000000..df882658ca91 --- /dev/null +++ b/sound/soc/codecs/nau8810.h @@ -0,0 +1,281 @@ +/* + * NAU8810 ALSA SoC audio driver + * + * Copyright 2016 Nuvoton Technology Corp. + * Author: David Lin <ctlin0@nuvoton.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 __NAU8810_H__ +#define __NAU8810_H__ + +#define NAU8810_REG_RESET 0x00 +#define NAU8810_REG_POWER1 0x01 +#define NAU8810_REG_POWER2 0x02 +#define NAU8810_REG_POWER3 0x03 +#define NAU8810_REG_IFACE 0x04 +#define NAU8810_REG_COMP 0x05 +#define NAU8810_REG_CLOCK 0x06 +#define NAU8810_REG_SMPLR 0x07 +#define NAU8810_REG_DAC 0x0A +#define NAU8810_REG_DACGAIN 0x0B +#define NAU8810_REG_ADC 0x0E +#define NAU8810_REG_ADCGAIN 0x0F +#define NAU8810_REG_EQ1 0x12 +#define NAU8810_REG_EQ2 0x13 +#define NAU8810_REG_EQ3 0x14 +#define NAU8810_REG_EQ4 0x15 +#define NAU8810_REG_EQ5 0x16 +#define NAU8810_REG_DACLIM1 0x18 +#define NAU8810_REG_DACLIM2 0x19 +#define NAU8810_REG_NOTCH1 0x1B +#define NAU8810_REG_NOTCH2 0x1C +#define NAU8810_REG_NOTCH3 0x1D +#define NAU8810_REG_NOTCH4 0x1E +#define NAU8810_REG_ALC1 0x20 +#define NAU8810_REG_ALC2 0x21 +#define NAU8810_REG_ALC3 0x22 +#define NAU8810_REG_NOISEGATE 0x23 +#define NAU8810_REG_PLLN 0x24 +#define NAU8810_REG_PLLK1 0x25 +#define NAU8810_REG_PLLK2 0x26 +#define NAU8810_REG_PLLK3 0x27 +#define NAU8810_REG_ATTEN 0x28 +#define NAU8810_REG_INPUT_SIGNAL 0x2C +#define NAU8810_REG_PGAGAIN 0x2D +#define NAU8810_REG_ADCBOOST 0x2F +#define NAU8810_REG_OUTPUT 0x31 +#define NAU8810_REG_SPKMIX 0x32 +#define NAU8810_REG_SPKGAIN 0x36 +#define NAU8810_REG_MONOMIX 0x38 +#define NAU8810_REG_POWER4 0x3A +#define NAU8810_REG_TSLOTCTL1 0x3B +#define NAU8810_REG_TSLOTCTL2 0x3C +#define NAU8810_REG_DEVICE_REVID 0x3E +#define NAU8810_REG_I2C_DEVICEID 0x3F +#define NAU8810_REG_ADDITIONID 0x40 +#define NAU8810_REG_RESERVE 0x41 +#define NAU8810_REG_OUTCTL 0x45 +#define NAU8810_REG_ALC1ENHAN1 0x46 +#define NAU8810_REG_ALC1ENHAN2 0x47 +#define NAU8810_REG_MISCCTL 0x49 +#define NAU8810_REG_OUTTIEOFF 0x4B +#define NAU8810_REG_AGCP2POUT 0x4C +#define NAU8810_REG_AGCPOUT 0x4D +#define NAU8810_REG_AMTCTL 0x4E +#define NAU8810_REG_OUTTIEOFFMAN 0x4F +#define NAU8810_REG_MAX NAU8810_REG_OUTTIEOFFMAN + + +/* NAU8810_REG_POWER1 (0x1) */ +#define NAU8810_DCBUF_EN (0x1 << 8) +#define NAU8810_PLL_EN_SFT 5 +#define NAU8810_MICBIAS_EN_SFT 4 +#define NAU8810_ABIAS_EN (0x1 << 3) +#define NAU8810_IOBUF_EN (0x1 << 2) +#define NAU8810_REFIMP_MASK 0x3 +#define NAU8810_REFIMP_DIS 0x0 +#define NAU8810_REFIMP_80K 0x1 +#define NAU8810_REFIMP_300K 0x2 +#define NAU8810_REFIMP_3K 0x3 + +/* NAU8810_REG_POWER2 (0x2) */ +#define NAU8810_BST_EN_SFT 4 +#define NAU8810_PGA_EN_SFT 2 +#define NAU8810_ADC_EN_SFT 0 + +/* NAU8810_REG_POWER3 (0x3) */ +#define NAU8810_DAC_EN_SFT 0 +#define NAU8810_SPKMX_EN_SFT 2 +#define NAU8810_MOUTMX_EN_SFT 3 +#define NAU8810_PSPK_EN_SFT 5 +#define NAU8810_NSPK_EN_SFT 6 +#define NAU8810_MOUT_EN_SFT 7 + +/* NAU8810_REG_IFACE (0x4) */ +#define NAU8810_AIFMT_SFT 3 +#define NAU8810_AIFMT_MASK (0x3 << NAU8810_AIFMT_SFT) +#define NAU8810_AIFMT_RIGHT (0x0 << NAU8810_AIFMT_SFT) +#define NAU8810_AIFMT_LEFT (0x1 << NAU8810_AIFMT_SFT) +#define NAU8810_AIFMT_I2S (0x2 << NAU8810_AIFMT_SFT) +#define NAU8810_AIFMT_PCM_A (0x3 << NAU8810_AIFMT_SFT) +#define NAU8810_WLEN_SFT 5 +#define NAU8810_WLEN_MASK (0x3 << NAU8810_WLEN_SFT) +#define NAU8810_WLEN_16 (0x0 << NAU8810_WLEN_SFT) +#define NAU8810_WLEN_20 (0x1 << NAU8810_WLEN_SFT) +#define NAU8810_WLEN_24 (0x2 << NAU8810_WLEN_SFT) +#define NAU8810_WLEN_32 (0x3 << NAU8810_WLEN_SFT) +#define NAU8810_FSP_IF (0x1 << 7) +#define NAU8810_BCLKP_IB (0x1 << 8) + +/* NAU8810_REG_COMP (0x5) */ +#define NAU8810_ADDAP_SFT 0 +#define NAU8810_ADCCM_SFT 1 +#define NAU8810_DACCM_SFT 3 + +/* NAU8810_REG_CLOCK (0x6) */ +#define NAU8810_CLKIO_MASK 0x1 +#define NAU8810_CLKIO_SLAVE 0x0 +#define NAU8810_CLKIO_MASTER 0x1 +#define NAU8810_BCLKSEL_SFT 2 +#define NAU8810_BCLKSEL_MASK (0x7 << NAU8810_BCLKSEL_SFT) +#define NAU8810_BCLKDIV_1 (0x0 << NAU8810_BCLKSEL_SFT) +#define NAU8810_BCLKDIV_2 (0x1 << NAU8810_BCLKSEL_SFT) +#define NAU8810_BCLKDIV_4 (0x2 << NAU8810_BCLKSEL_SFT) +#define NAU8810_BCLKDIV_8 (0x3 << NAU8810_BCLKSEL_SFT) +#define NAU8810_BCLKDIV_16 (0x4 << NAU8810_BCLKSEL_SFT) +#define NAU8810_BCLKDIV_32 (0x5 << NAU8810_BCLKSEL_SFT) +#define NAU8810_MCLKSEL_SFT 5 +#define NAU8810_MCLKSEL_MASK (0x7 << NAU8810_MCLKSEL_SFT) +#define NAU8810_CLKM_SFT 8 +#define NAU8810_CLKM_MASK (0x1 << NAU8810_CLKM_SFT) +#define NAU8810_CLKM_MCLK (0x0 << NAU8810_CLKM_SFT) +#define NAU8810_CLKM_PLL (0x1 << NAU8810_CLKM_SFT) + +/* NAU8810_REG_SMPLR (0x7) */ +#define NAU8810_SMPLR_SFT 1 +#define NAU8810_SMPLR_MASK (0x7 << NAU8810_SMPLR_SFT) +#define NAU8810_SMPLR_48K (0x0 << NAU8810_SMPLR_SFT) +#define NAU8810_SMPLR_32K (0x1 << NAU8810_SMPLR_SFT) +#define NAU8810_SMPLR_24K (0x2 << NAU8810_SMPLR_SFT) +#define NAU8810_SMPLR_16K (0x3 << NAU8810_SMPLR_SFT) +#define NAU8810_SMPLR_12K (0x4 << NAU8810_SMPLR_SFT) +#define NAU8810_SMPLR_8K (0x5 << NAU8810_SMPLR_SFT) + +/* NAU8810_REG_DAC (0xA) */ +#define NAU8810_DACPL_SFT 0 +#define NAU8810_DACOS_SFT 3 +#define NAU8810_DEEMP_SFT 4 + +/* NAU8810_REG_DACGAIN (0xB) */ +#define NAU8810_DACGAIN_SFT 0 + +/* NAU8810_REG_ADC (0xE) */ +#define NAU8810_ADCPL_SFT 0 +#define NAU8810_ADCOS_SFT 3 +#define NAU8810_HPF_SFT 4 +#define NAU8810_HPFEN_SFT 8 + +/* NAU8810_REG_ADCGAIN (0xF) */ +#define NAU8810_ADCGAIN_SFT 0 + +/* NAU8810_REG_EQ1 (0x12) */ +#define NAU8810_EQ1GC_SFT 0 +#define NAU8810_EQ1CF_SFT 5 +#define NAU8810_EQM_SFT 8 + +/* NAU8810_REG_EQ2 (0x13) */ +#define NAU8810_EQ2GC_SFT 0 +#define NAU8810_EQ2CF_SFT 5 +#define NAU8810_EQ2BW_SFT 8 + +/* NAU8810_REG_EQ3 (0x14) */ +#define NAU8810_EQ3GC_SFT 0 +#define NAU8810_EQ3CF_SFT 5 +#define NAU8810_EQ3BW_SFT 8 + +/* NAU8810_REG_EQ4 (0x15) */ +#define NAU8810_EQ4GC_SFT 0 +#define NAU8810_EQ4CF_SFT 5 +#define NAU8810_EQ4BW_SFT 8 + +/* NAU8810_REG_EQ5 (0x16) */ +#define NAU8810_EQ5GC_SFT 0 +#define NAU8810_EQ5CF_SFT 5 + +/* NAU8810_REG_DACLIM1 (0x18) */ +#define NAU8810_DACLIMATK_SFT 0 +#define NAU8810_DACLIMDCY_SFT 4 +#define NAU8810_DACLIMEN_SFT 8 + +/* NAU8810_REG_DACLIM2 (0x19) */ +#define NAU8810_DACLIMBST_SFT 0 +#define NAU8810_DACLIMTHL_SFT 4 + +/* NAU8810_REG_ALC1 (0x20) */ +#define NAU8810_ALCMINGAIN_SFT 0 +#define NAU8810_ALCMXGAIN_SFT 3 +#define NAU8810_ALCEN_SFT 8 + +/* NAU8810_REG_ALC2 (0x21) */ +#define NAU8810_ALCSL_SFT 0 +#define NAU8810_ALCHT_SFT 4 +#define NAU8810_ALCZC_SFT 8 + +/* NAU8810_REG_ALC3 (0x22) */ +#define NAU8810_ALCATK_SFT 0 +#define NAU8810_ALCDCY_SFT 4 +#define NAU8810_ALCM_SFT 8 + +/* NAU8810_REG_NOISEGATE (0x23) */ +#define NAU8810_ALCNTH_SFT 0 +#define NAU8810_ALCNEN_SFT 3 + +/* NAU8810_REG_PLLN (0x24) */ +#define NAU8810_PLLN_MASK 0xF +#define NAU8810_PLLMCLK_DIV2 (0x1 << 4) + +/* NAU8810_REG_PLLK1 (0x25) */ +#define NAU8810_PLLK1_SFT 18 +#define NAU8810_PLLK1_MASK 0x3F + +/* NAU8810_REG_PLLK2 (0x26) */ +#define NAU8810_PLLK2_SFT 9 +#define NAU8810_PLLK2_MASK 0x1FF + +/* NAU8810_REG_PLLK3 (0x27) */ +#define NAU8810_PLLK3_MASK 0x1FF + +/* NAU8810_REG_INPUT_SIGNAL (0x2C) */ +#define NAU8810_PMICPGA_SFT 0 +#define NAU8810_NMICPGA_SFT 1 + +/* NAU8810_REG_PGAGAIN (0x2D) */ +#define NAU8810_PGAGAIN_SFT 0 +#define NAU8810_PGAMT_SFT 6 +#define NAU8810_PGAZC_SFT 7 + +/* NAU8810_REG_ADCBOOST (0x2F) */ +#define NAU8810_PMICBSTGAIN_SFT 4 +#define NAU8810_PGABST_SFT 8 + +/* NAU8810_REG_SPKMIX (0x32) */ +#define NAU8810_DACSPK_SFT 0 +#define NAU8810_BYPSPK_SFT 1 + +/* NAU8810_REG_SPKGAIN (0x36) */ +#define NAU8810_SPKGAIN_SFT 0 +#define NAU8810_SPKMT_SFT 6 +#define NAU8810_SPKZC_SFT 7 + +/* NAU8810_REG_MONOMIX (0x38) */ +#define NAU8810_DACMOUT_SFT 0 +#define NAU8810_BYPMOUT_SFT 1 +#define NAU8810_MOUTMXMT_SFT 6 + + +/* System Clock Source */ +enum { + NAU8810_SCLK_MCLK, + NAU8810_SCLK_PLL, +}; + +struct nau8810_pll { + int pre_factor; + int mclk_scaler; + int pll_frac; + int pll_int; +}; + +struct nau8810 { + struct device *dev; + struct regmap *regmap; + struct nau8810_pll pll; + int sysclk; + int clk_id; +}; + +#endif diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index 2e59a85e360b..4576f987a4a5 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -43,6 +43,8 @@ #define GAIN_AUGMENT 22500 #define SIDETONE_BASE 207000 +/* the maximum frequency of CLK_ADC and CLK_DAC */ +#define CLK_DA_AD_MAX 6144000 static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id, unsigned int freq); @@ -95,6 +97,27 @@ static const struct nau8825_fll_attr fll_pre_scalar[] = { { 8, 0x3 }, }; +/* over sampling rate */ +struct nau8825_osr_attr { + unsigned int osr; + unsigned int clk_src; +}; + +static const struct nau8825_osr_attr osr_dac_sel[] = { + { 64, 2 }, /* OSR 64, SRC 1/4 */ + { 256, 0 }, /* OSR 256, SRC 1 */ + { 128, 1 }, /* OSR 128, SRC 1/2 */ + { 0, 0 }, + { 32, 3 }, /* OSR 32, SRC 1/8 */ +}; + +static const struct nau8825_osr_attr osr_adc_sel[] = { + { 32, 3 }, /* OSR 32, SRC 1/8 */ + { 64, 2 }, /* OSR 64, SRC 1/4 */ + { 128, 1 }, /* OSR 128, SRC 1/2 */ + { 256, 0 }, /* OSR 256, SRC 1 */ +}; + static const struct reg_default nau8825_reg_defaults[] = { { NAU8825_REG_ENA_CTRL, 0x00ff }, { NAU8825_REG_IIC_ADDR_SET, 0x0 }, @@ -538,9 +561,9 @@ static void nau8825_xtalk_prepare(struct nau8825 *nau8825) nau8825_xtalk_backup(nau8825); /* Config IIS as master to output signal by codec */ regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2, - NAU8825_I2S_MS_MASK | NAU8825_I2S_DRV_MASK | + NAU8825_I2S_MS_MASK | NAU8825_I2S_LRC_DIV_MASK | NAU8825_I2S_BLK_DIV_MASK, NAU8825_I2S_MS_MASTER | - (0x2 << NAU8825_I2S_DRV_SFT) | 0x1); + (0x2 << NAU8825_I2S_LRC_DIV_SFT) | 0x1); /* Ramp up headphone volume to 0dB to get better performance and * avoid pop noise in headphone. */ @@ -634,7 +657,7 @@ static void nau8825_xtalk_clean(struct nau8825 *nau8825) NAU8825_IRQ_RMS_EN, NAU8825_IRQ_RMS_EN); /* Recover default value for IIS */ regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2, - NAU8825_I2S_MS_MASK | NAU8825_I2S_DRV_MASK | + NAU8825_I2S_MS_MASK | NAU8825_I2S_LRC_DIV_MASK | NAU8825_I2S_BLK_DIV_MASK, NAU8825_I2S_MS_SLAVE); /* Restore value of specific register for cross talk */ nau8825_xtalk_restore(nau8825); @@ -1179,15 +1202,64 @@ static const struct snd_soc_dapm_route nau8825_dapm_routes[] = { {"HPOR", NULL, "Class G"}, }; +static int nau8825_clock_check(struct nau8825 *nau8825, + int stream, int rate, int osr) +{ + int osrate; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (osr >= ARRAY_SIZE(osr_dac_sel)) + return -EINVAL; + osrate = osr_dac_sel[osr].osr; + } else { + if (osr >= ARRAY_SIZE(osr_adc_sel)) + return -EINVAL; + osrate = osr_adc_sel[osr].osr; + } + + if (!osrate || rate * osr > CLK_DA_AD_MAX) { + dev_err(nau8825->dev, "exceed the maximum frequency of CLK_ADC or CLK_DAC\n"); + return -EINVAL; + } + + return 0; +} + static int nau8825_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 nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); - unsigned int val_len = 0; + unsigned int val_len = 0, osr; + + nau8825_sema_acquire(nau8825, 3 * HZ); - nau8825_sema_acquire(nau8825, 2 * HZ); + /* CLK_DAC or CLK_ADC = OSR * FS + * DAC or ADC clock frequency is defined as Over Sampling Rate (OSR) + * multiplied by the audio sample rate (Fs). Note that the OSR and Fs + * values must be selected such that the maximum frequency is less + * than 6.144 MHz. + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_read(nau8825->regmap, NAU8825_REG_DAC_CTRL1, &osr); + osr &= NAU8825_DAC_OVERSAMPLE_MASK; + if (nau8825_clock_check(nau8825, substream->stream, + params_rate(params), osr)) + return -EINVAL; + regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER, + NAU8825_CLK_DAC_SRC_MASK, + osr_dac_sel[osr].clk_src << NAU8825_CLK_DAC_SRC_SFT); + } else { + regmap_read(nau8825->regmap, NAU8825_REG_ADC_RATE, &osr); + osr &= NAU8825_ADC_SYNC_DOWN_MASK; + if (nau8825_clock_check(nau8825, substream->stream, + params_rate(params), osr)) + return -EINVAL; + regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER, + NAU8825_CLK_ADC_SRC_MASK, + osr_adc_sel[osr].clk_src << NAU8825_CLK_ADC_SRC_SFT); + } switch (params_width(params)) { case 16: @@ -1221,7 +1293,7 @@ static int nau8825_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); unsigned int ctrl1_val = 0, ctrl2_val = 0; - nau8825_sema_acquire(nau8825, 2 * HZ); + nau8825_sema_acquire(nau8825, 3 * HZ); switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: @@ -1774,9 +1846,10 @@ static void nau8825_init_regs(struct nau8825 *nau8825) * (audible hiss). Set it to something better. */ regmap_update_bits(regmap, NAU8825_REG_ADC_RATE, - NAU8825_ADC_SYNC_DOWN_MASK, NAU8825_ADC_SYNC_DOWN_128); + NAU8825_ADC_SYNC_DOWN_MASK | NAU8825_ADC_SINC4_EN, + NAU8825_ADC_SYNC_DOWN_64); regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1, - NAU8825_DAC_OVERSAMPLE_MASK, NAU8825_DAC_OVERSAMPLE_128); + NAU8825_DAC_OVERSAMPLE_MASK, NAU8825_DAC_OVERSAMPLE_64); /* Disable DACR/L power */ regmap_update_bits(regmap, NAU8825_REG_CHARGE_PUMP, NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, @@ -1811,6 +1884,9 @@ static void nau8825_init_regs(struct nau8825 *nau8825) NAU8825_DACL_CH_SEL_MASK, NAU8825_DACL_CH_SEL_L); regmap_update_bits(nau8825->regmap, NAU8825_REG_DACR_CTRL, NAU8825_DACL_CH_SEL_MASK, NAU8825_DACL_CH_SEL_R); + /* Disable short Frame Sync detection logic */ + regmap_update_bits(regmap, NAU8825_REG_LEFT_TIME_SLOT, + NAU8825_DIS_FS_SHORT_DET, NAU8825_DIS_FS_SHORT_DET); } static const struct regmap_config nau8825_regmap_config = { @@ -1907,7 +1983,7 @@ static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs, /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional * input based on FDCO, FREF and FLL ratio. */ - fvco = div_u64(fvco << 16, fref * fll_param->ratio); + fvco = div_u64(fvco_max << 16, fref * fll_param->ratio); fll_param->fll_int = (fvco >> 16) & 0x3FF; fll_param->fll_frac = fvco & 0xFFFF; return 0; @@ -1919,8 +1995,10 @@ static void nau8825_fll_apply(struct nau8825 *nau8825, regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER, NAU8825_CLK_SRC_MASK | NAU8825_CLK_MCLK_SRC_MASK, NAU8825_CLK_SRC_MCLK | fll_param->mclk_src); + /* Make DSP operate at high speed for better performance. */ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL1, - NAU8825_FLL_RATIO_MASK, fll_param->ratio); + NAU8825_FLL_RATIO_MASK | NAU8825_ICTRL_LATCH_MASK, + fll_param->ratio | (0x6 << NAU8825_ICTRL_LATCH_SFT)); /* FLL 16-bit fractional input */ regmap_write(nau8825->regmap, NAU8825_REG_FLL2, fll_param->fll_frac); /* FLL 10-bit integer input */ @@ -1928,7 +2006,8 @@ static void nau8825_fll_apply(struct nau8825 *nau8825, NAU8825_FLL_INTEGER_MASK, fll_param->fll_int); /* FLL pre-scaler */ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL4, - NAU8825_FLL_REF_DIV_MASK, fll_param->clk_ref_div); + NAU8825_FLL_REF_DIV_MASK, + fll_param->clk_ref_div << NAU8825_FLL_REF_DIV_SFT); /* select divided VCO input */ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5, NAU8825_FLL_CLK_SW_MASK, NAU8825_FLL_CLK_SW_REF); @@ -1936,19 +2015,22 @@ static void nau8825_fll_apply(struct nau8825 *nau8825, regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN, 0); if (fll_param->fll_frac) { + /* set FLL loop filter enable and cutoff frequency at 500Khz */ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5, NAU8825_FLL_PDB_DAC_EN | NAU8825_FLL_LOOP_FTR_EN | NAU8825_FLL_FTR_SW_MASK, NAU8825_FLL_PDB_DAC_EN | NAU8825_FLL_LOOP_FTR_EN | NAU8825_FLL_FTR_SW_FILTER); regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6, - NAU8825_SDM_EN, NAU8825_SDM_EN); + NAU8825_SDM_EN | NAU8825_CUTOFF500, + NAU8825_SDM_EN | NAU8825_CUTOFF500); } else { + /* disable FLL loop filter and cutoff frequency */ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5, NAU8825_FLL_PDB_DAC_EN | NAU8825_FLL_LOOP_FTR_EN | NAU8825_FLL_FTR_SW_MASK, NAU8825_FLL_FTR_SW_ACCU); - regmap_update_bits(nau8825->regmap, - NAU8825_REG_FLL6, NAU8825_SDM_EN, 0); + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6, + NAU8825_SDM_EN | NAU8825_CUTOFF500, 0); } } @@ -2014,6 +2096,9 @@ static void nau8825_configure_mclk_as_sysclk(struct regmap *regmap) NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_MCLK); regmap_update_bits(regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN, 0); + /* Make DSP operate as default setting for power saving. */ + regmap_update_bits(regmap, NAU8825_REG_FLL1, + NAU8825_ICTRL_LATCH_MASK, 0); } static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id, @@ -2038,7 +2123,7 @@ static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id, * fered by cross talk process, the driver make the playback * preparation halted until cross talk process finish. */ - nau8825_sema_acquire(nau8825, 2 * HZ); + nau8825_sema_acquire(nau8825, 3 * HZ); nau8825_configure_mclk_as_sysclk(regmap); /* MCLK not changed by clock tree */ regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER, @@ -2057,10 +2142,13 @@ static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id, NAU8825_DCO_EN, NAU8825_DCO_EN); regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER, NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO); - /* Decrease the VCO frequency for power saving */ + /* Decrease the VCO frequency and make DSP operate + * as default setting for power saving. + */ regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER, NAU8825_CLK_MCLK_SRC_MASK, 0xf); regmap_update_bits(regmap, NAU8825_REG_FLL1, + NAU8825_ICTRL_LATCH_MASK | NAU8825_FLL_RATIO_MASK, 0x10); regmap_update_bits(regmap, NAU8825_REG_FLL6, NAU8825_SDM_EN, NAU8825_SDM_EN); @@ -2083,9 +2171,14 @@ static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id, * fered by cross talk process, the driver make the playback * preparation halted until cross talk process finish. */ - nau8825_sema_acquire(nau8825, 2 * HZ); + nau8825_sema_acquire(nau8825, 3 * HZ); + /* Higher FLL reference input frequency can only set lower + * gain error, such as 0000 for input reference from MCLK + * 12.288Mhz. + */ regmap_update_bits(regmap, NAU8825_REG_FLL3, - NAU8825_FLL_CLK_SRC_MASK, NAU8825_FLL_CLK_SRC_MCLK); + NAU8825_FLL_CLK_SRC_MASK | NAU8825_GAIN_ERR_MASK, + NAU8825_FLL_CLK_SRC_MCLK | 0); /* Release the semaphone. */ nau8825_sema_release(nau8825); @@ -2100,9 +2193,17 @@ static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id, * fered by cross talk process, the driver make the playback * preparation halted until cross talk process finish. */ - nau8825_sema_acquire(nau8825, 2 * HZ); + nau8825_sema_acquire(nau8825, 3 * HZ); + /* If FLL reference input is from low frequency source, + * higher error gain can apply such as 0xf which has + * the most sensitive gain error correction threshold, + * Therefore, FLL has the most accurate DCO to + * target frequency. + */ regmap_update_bits(regmap, NAU8825_REG_FLL3, - NAU8825_FLL_CLK_SRC_MASK, NAU8825_FLL_CLK_SRC_BLK); + NAU8825_FLL_CLK_SRC_MASK | NAU8825_GAIN_ERR_MASK, + NAU8825_FLL_CLK_SRC_BLK | + (0xf << NAU8825_GAIN_ERR_SFT)); /* Release the semaphone. */ nau8825_sema_release(nau8825); @@ -2118,9 +2219,17 @@ static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id, * fered by cross talk process, the driver make the playback * preparation halted until cross talk process finish. */ - nau8825_sema_acquire(nau8825, 2 * HZ); + nau8825_sema_acquire(nau8825, 3 * HZ); + /* If FLL reference input is from low frequency source, + * higher error gain can apply such as 0xf which has + * the most sensitive gain error correction threshold, + * Therefore, FLL has the most accurate DCO to + * target frequency. + */ regmap_update_bits(regmap, NAU8825_REG_FLL3, - NAU8825_FLL_CLK_SRC_MASK, NAU8825_FLL_CLK_SRC_FS); + NAU8825_FLL_CLK_SRC_MASK | NAU8825_GAIN_ERR_MASK, + NAU8825_FLL_CLK_SRC_FS | + (0xf << NAU8825_GAIN_ERR_SFT)); /* Release the semaphone. */ nau8825_sema_release(nau8825); @@ -2256,12 +2365,14 @@ static struct snd_soc_codec_driver nau8825_codec_driver = { .suspend = nau8825_suspend, .resume = nau8825_resume, - .controls = nau8825_controls, - .num_controls = ARRAY_SIZE(nau8825_controls), - .dapm_widgets = nau8825_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(nau8825_dapm_widgets), - .dapm_routes = nau8825_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(nau8825_dapm_routes), + .component_driver = { + .controls = nau8825_controls, + .num_controls = ARRAY_SIZE(nau8825_controls), + .dapm_widgets = nau8825_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(nau8825_dapm_widgets), + .dapm_routes = nau8825_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(nau8825_dapm_routes), + }, }; static void nau8825_reset_chip(struct regmap *regmap) diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h index 1c63e2abafa9..514fd13c2f46 100644 --- a/sound/soc/codecs/nau8825.h +++ b/sound/soc/codecs/nau8825.h @@ -115,12 +115,20 @@ #define NAU8825_CLK_SRC_MASK (1 << NAU8825_CLK_SRC_SFT) #define NAU8825_CLK_SRC_VCO (1 << NAU8825_CLK_SRC_SFT) #define NAU8825_CLK_SRC_MCLK (0 << NAU8825_CLK_SRC_SFT) +#define NAU8825_CLK_ADC_SRC_SFT 6 +#define NAU8825_CLK_ADC_SRC_MASK (0x3 << NAU8825_CLK_ADC_SRC_SFT) +#define NAU8825_CLK_DAC_SRC_SFT 4 +#define NAU8825_CLK_DAC_SRC_MASK (0x3 << NAU8825_CLK_DAC_SRC_SFT) #define NAU8825_CLK_MCLK_SRC_MASK (0xf << 0) /* FLL1 (0x04) */ +#define NAU8825_ICTRL_LATCH_SFT 10 +#define NAU8825_ICTRL_LATCH_MASK (0x7 << NAU8825_ICTRL_LATCH_SFT) #define NAU8825_FLL_RATIO_MASK (0x7f << 0) /* FLL3 (0x06) */ +#define NAU8825_GAIN_ERR_SFT 12 +#define NAU8825_GAIN_ERR_MASK (0xf << NAU8825_GAIN_ERR_SFT) #define NAU8825_FLL_INTEGER_MASK (0x3ff << 0) #define NAU8825_FLL_CLK_SRC_SFT 10 #define NAU8825_FLL_CLK_SRC_MASK (0x3 << NAU8825_FLL_CLK_SRC_SFT) @@ -129,7 +137,8 @@ #define NAU8825_FLL_CLK_SRC_FS (0x3 << NAU8825_FLL_CLK_SRC_SFT) /* FLL4 (0x07) */ -#define NAU8825_FLL_REF_DIV_MASK (0x3 << 10) +#define NAU8825_FLL_REF_DIV_SFT 10 +#define NAU8825_FLL_REF_DIV_MASK (0x3 << NAU8825_FLL_REF_DIV_SFT) /* FLL5 (0x08) */ #define NAU8825_FLL_PDB_DAC_EN (0x1 << 15) @@ -144,6 +153,7 @@ /* FLL6 (0x9) */ #define NAU8825_DCO_EN (0x1 << 15) #define NAU8825_SDM_EN (0x1 << 14) +#define NAU8825_CUTOFF500 (0x1 << 13) /* HSD_CTRL (0xc) */ #define NAU8825_HSD_AUTO_MODE (1 << 6) @@ -238,14 +248,19 @@ /* I2S_PCM_CTRL2 (0x1d) */ #define NAU8825_I2S_TRISTATE (1 << 15) /* 0 - normal mode, 1 - Hi-Z output */ -#define NAU8825_I2S_DRV_SFT 12 -#define NAU8825_I2S_DRV_MASK (0x3 << NAU8825_I2S_DRV_SFT) +#define NAU8825_I2S_LRC_DIV_SFT 12 +#define NAU8825_I2S_LRC_DIV_MASK (0x3 << NAU8825_I2S_LRC_DIV_SFT) #define NAU8825_I2S_MS_SFT 3 #define NAU8825_I2S_MS_MASK (1 << NAU8825_I2S_MS_SFT) #define NAU8825_I2S_MS_MASTER (1 << NAU8825_I2S_MS_SFT) #define NAU8825_I2S_MS_SLAVE (0 << NAU8825_I2S_MS_SFT) #define NAU8825_I2S_BLK_DIV_MASK 0x7 +/* LEFT_TIME_SLOT (0x1e) */ +#define NAU8825_FS_ERR_CMP_SEL_SFT 14 +#define NAU8825_FS_ERR_CMP_SEL_MASK (0x3 << NAU8825_FS_ERR_CMP_SEL_SFT) +#define NAU8825_DIS_FS_SHORT_DET (1 << 13) + /* BIQ_CTRL (0x20) */ #define NAU8825_BIQ_WRT_SFT 4 #define NAU8825_BIQ_WRT_EN (1 << NAU8825_BIQ_WRT_SFT) @@ -255,6 +270,8 @@ #define NAU8825_BIQ_PATH_DAC (1 << NAU8825_BIQ_PATH_SFT) /* ADC_RATE (0x2b) */ +#define NAU8825_ADC_SINC4_SFT 4 +#define NAU8825_ADC_SINC4_EN (1 << NAU8825_ADC_SINC4_SFT) #define NAU8825_ADC_SYNC_DOWN_SFT 0 #define NAU8825_ADC_SYNC_DOWN_MASK 0x3 #define NAU8825_ADC_SYNC_DOWN_32 0 diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c index 33e1fc2d1598..0b14efab6280 100644 --- a/sound/soc/codecs/pcm1681.c +++ b/sound/soc/codecs/pcm1681.c @@ -289,12 +289,14 @@ static const struct regmap_config pcm1681_regmap = { }; static struct snd_soc_codec_driver soc_codec_dev_pcm1681 = { - .controls = pcm1681_controls, - .num_controls = ARRAY_SIZE(pcm1681_controls), - .dapm_widgets = pcm1681_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(pcm1681_dapm_widgets), - .dapm_routes = pcm1681_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(pcm1681_dapm_routes), + .component_driver = { + .controls = pcm1681_controls, + .num_controls = ARRAY_SIZE(pcm1681_controls), + .dapm_widgets = pcm1681_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcm1681_dapm_widgets), + .dapm_routes = pcm1681_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(pcm1681_dapm_routes), + }, }; static const struct i2c_device_id pcm1681_i2c_id[] = { diff --git a/sound/soc/codecs/pcm179x.c b/sound/soc/codecs/pcm179x.c index 88fbdd184aa0..b813a154ddd9 100644 --- a/sound/soc/codecs/pcm179x.c +++ b/sound/soc/codecs/pcm179x.c @@ -206,12 +206,14 @@ const struct regmap_config pcm179x_regmap_config = { EXPORT_SYMBOL_GPL(pcm179x_regmap_config); static struct snd_soc_codec_driver soc_codec_dev_pcm179x = { - .controls = pcm179x_controls, - .num_controls = ARRAY_SIZE(pcm179x_controls), - .dapm_widgets = pcm179x_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(pcm179x_dapm_widgets), - .dapm_routes = pcm179x_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(pcm179x_dapm_routes), + .component_driver = { + .controls = pcm179x_controls, + .num_controls = ARRAY_SIZE(pcm179x_controls), + .dapm_widgets = pcm179x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcm179x_dapm_widgets), + .dapm_routes = pcm179x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(pcm179x_dapm_routes), + }, }; int pcm179x_common_init(struct device *dev, struct regmap *regmap) diff --git a/sound/soc/codecs/pcm3008.c b/sound/soc/codecs/pcm3008.c index 8fb445f33f6f..708af05486f6 100644 --- a/sound/soc/codecs/pcm3008.c +++ b/sound/soc/codecs/pcm3008.c @@ -99,10 +99,12 @@ static struct snd_soc_dai_driver pcm3008_dai = { }; static struct snd_soc_codec_driver soc_codec_dev_pcm3008 = { - .dapm_widgets = pcm3008_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(pcm3008_dapm_widgets), - .dapm_routes = pcm3008_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(pcm3008_dapm_routes), + .component_driver = { + .dapm_widgets = pcm3008_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcm3008_dapm_widgets), + .dapm_routes = pcm3008_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(pcm3008_dapm_routes), + }, }; static int pcm3008_codec_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c index 992a77edcd5d..39bc02d5bc5d 100644 --- a/sound/soc/codecs/pcm3168a.c +++ b/sound/soc/codecs/pcm3168a.c @@ -599,12 +599,14 @@ EXPORT_SYMBOL_GPL(pcm3168a_regmap); static const struct snd_soc_codec_driver pcm3168a_driver = { .idle_bias_off = true, - .controls = pcm3168a_snd_controls, - .num_controls = ARRAY_SIZE(pcm3168a_snd_controls), - .dapm_widgets = pcm3168a_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(pcm3168a_dapm_widgets), - .dapm_routes = pcm3168a_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(pcm3168a_dapm_routes) + .component_driver = { + .controls = pcm3168a_snd_controls, + .num_controls = ARRAY_SIZE(pcm3168a_snd_controls), + .dapm_widgets = pcm3168a_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcm3168a_dapm_widgets), + .dapm_routes = pcm3168a_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(pcm3168a_dapm_routes) + }, }; int pcm3168a_probe(struct device *dev, struct regmap *regmap) diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 047c48953a20..72b19e62f626 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -1348,12 +1348,14 @@ static struct snd_soc_codec_driver pcm512x_codec_driver = { .set_bias_level = pcm512x_set_bias_level, .idle_bias_off = true, - .controls = pcm512x_controls, - .num_controls = ARRAY_SIZE(pcm512x_controls), - .dapm_widgets = pcm512x_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(pcm512x_dapm_widgets), - .dapm_routes = pcm512x_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(pcm512x_dapm_routes), + .component_driver = { + .controls = pcm512x_controls, + .num_controls = ARRAY_SIZE(pcm512x_controls), + .dapm_widgets = pcm512x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcm512x_dapm_widgets), + .dapm_routes = pcm512x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(pcm512x_dapm_routes), + }, }; static const struct regmap_range_cfg pcm512x_range = { diff --git a/sound/soc/codecs/rl6231.c b/sound/soc/codecs/rl6231.c index 1dc68ab08a17..7b447d0b173a 100644 --- a/sound/soc/codecs/rl6231.c +++ b/sound/soc/codecs/rl6231.c @@ -102,6 +102,7 @@ struct pll_calc_map { }; static const struct pll_calc_map pll_preset_table[] = { + {19200000, 4096000, 23, 14, 1, false}, {19200000, 24576000, 3, 30, 3, false}, }; diff --git a/sound/soc/codecs/rl6347a.c b/sound/soc/codecs/rl6347a.c index a4b910efbd45..8f571cf8edd4 100644 --- a/sound/soc/codecs/rl6347a.c +++ b/sound/soc/codecs/rl6347a.c @@ -51,7 +51,7 @@ int rl6347a_hw_write(void *context, unsigned int reg, unsigned int value) if (ret == 4) return 0; else - pr_err("ret=%d\n", ret); + dev_err(&client->dev, "I2C error %d\n", ret); if (ret < 0) return ret; else diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index 74c0e4eb3788..9c365a7f758d 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -1053,12 +1053,14 @@ static struct snd_soc_codec_driver soc_codec_dev_rt286 = { .resume = rt286_resume, .set_bias_level = rt286_set_bias_level, .idle_bias_off = true, - .controls = rt286_snd_controls, - .num_controls = ARRAY_SIZE(rt286_snd_controls), - .dapm_widgets = rt286_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(rt286_dapm_widgets), - .dapm_routes = rt286_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(rt286_dapm_routes), + .component_driver = { + .controls = rt286_snd_controls, + .num_controls = ARRAY_SIZE(rt286_snd_controls), + .dapm_widgets = rt286_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt286_dapm_widgets), + .dapm_routes = rt286_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt286_dapm_routes), + }, }; static const struct regmap_config rt286_regmap = { diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index f80cfe4d2ef2..7150a407ffd9 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -249,6 +249,11 @@ static int rt298_jack_detect(struct rt298_priv *rt298, bool *hp, bool *mic) snd_soc_dapm_force_enable_pin(dapm, "LDO1"); snd_soc_dapm_sync(dapm); + regmap_update_bits(rt298->regmap, + RT298_POWER_CTRL1, 0x1001, 0); + regmap_update_bits(rt298->regmap, + RT298_POWER_CTRL2, 0x4, 0x4); + regmap_write(rt298->regmap, RT298_SET_MIC1, 0x24); msleep(50); @@ -321,11 +326,31 @@ static void rt298_jack_detect_work(struct work_struct *work) int rt298_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack) { struct rt298_priv *rt298 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm; + bool hp = false; + bool mic = false; + int status = 0; + + /* If jack in NULL, disable HS jack */ + if (!jack) { + regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x2, 0x0); + dapm = snd_soc_codec_get_dapm(codec); + snd_soc_dapm_disable_pin(dapm, "LDO1"); + snd_soc_dapm_sync(dapm); + return 0; + } rt298->jack = jack; + regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x2, 0x2); - /* Send an initial empty report */ - snd_soc_jack_report(rt298->jack, 0, + rt298_jack_detect(rt298, &hp, &mic); + if (hp == true) + status |= SND_JACK_HEADPHONE; + + if (mic == true) + status |= SND_JACK_MICROPHONE; + + snd_soc_jack_report(rt298->jack, status, SND_JACK_MICROPHONE | SND_JACK_HEADPHONE); return 0; @@ -1095,12 +1120,14 @@ static struct snd_soc_codec_driver soc_codec_dev_rt298 = { .resume = rt298_resume, .set_bias_level = rt298_set_bias_level, .idle_bias_off = true, - .controls = rt298_snd_controls, - .num_controls = ARRAY_SIZE(rt298_snd_controls), - .dapm_widgets = rt298_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(rt298_dapm_widgets), - .dapm_routes = rt298_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(rt298_dapm_routes), + .component_driver = { + .controls = rt298_snd_controls, + .num_controls = ARRAY_SIZE(rt298_snd_controls), + .dapm_widgets = rt298_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt298_dapm_widgets), + .dapm_routes = rt298_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt298_dapm_routes), + }, }; static const struct regmap_config rt298_regmap = { diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c index 77ff8ebe6dfb..0901e25d6db6 100644 --- a/sound/soc/codecs/rt5514-spi.c +++ b/sound/soc/codecs/rt5514-spi.c @@ -20,7 +20,6 @@ #include <linux/slab.h> #include <linux/gpio.h> #include <linux/sched.h> -#include <linux/kthread.h> #include <linux/uaccess.h> #include <linux/miscdevice.h> #include <linux/regulator/consumer.h> @@ -236,7 +235,7 @@ static snd_pcm_uframes_t rt5514_spi_pcm_pointer( return bytes_to_frames(runtime, rt5514_dsp->dma_offset); } -static struct snd_pcm_ops rt5514_spi_pcm_ops = { +static const struct snd_pcm_ops rt5514_spi_pcm_ops = { .open = rt5514_spi_pcm_open, .hw_params = rt5514_spi_hw_params, .hw_free = rt5514_spi_hw_free, diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c index 7162f05101d9..b281a46d769d 100644 --- a/sound/soc/codecs/rt5514.c +++ b/sound/soc/codecs/rt5514.c @@ -278,7 +278,7 @@ static const DECLARE_TLV_DB_RANGE(bst_tlv, 8, 8, TLV_DB_SCALE_ITEM(1700, 0, 0) ); -static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0); +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1725, 75, 0); static int rt5514_dsp_voice_wake_up_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -352,10 +352,10 @@ static const struct snd_kcontrol_new rt5514_snd_controls[] = { SOC_DOUBLE_TLV("MIC Boost Volume", RT5514_ANA_CTRL_MICBST, RT5514_SEL_BSTL_SFT, RT5514_SEL_BSTR_SFT, 8, 0, bst_tlv), SOC_DOUBLE_R_TLV("ADC1 Capture Volume", RT5514_DOWNFILTER0_CTRL1, - RT5514_DOWNFILTER0_CTRL2, RT5514_AD_GAIN_SFT, 127, 0, + RT5514_DOWNFILTER0_CTRL2, RT5514_AD_GAIN_SFT, 63, 0, adc_vol_tlv), SOC_DOUBLE_R_TLV("ADC2 Capture Volume", RT5514_DOWNFILTER1_CTRL1, - RT5514_DOWNFILTER1_CTRL2, RT5514_AD_GAIN_SFT, 127, 0, + RT5514_DOWNFILTER1_CTRL2, RT5514_AD_GAIN_SFT, 63, 0, adc_vol_tlv), SOC_SINGLE_EXT("DSP Voice Wake Up", SND_SOC_NOPM, 0, 1, 0, rt5514_dsp_voice_wake_up_get, rt5514_dsp_voice_wake_up_put), @@ -452,6 +452,9 @@ static int rt5514_set_dmic_clk(struct snd_soc_dapm_widget *w, RT5514_CLK_DMIC_OUT_SEL_MASK, idx << RT5514_CLK_DMIC_OUT_SEL_SFT); + if (rt5514->pdata.dmic_init_delay) + msleep(rt5514->pdata.dmic_init_delay); + return idx; } @@ -1023,12 +1026,14 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5514 = { .probe = rt5514_probe, .idle_bias_off = true, .set_bias_level = rt5514_set_bias_level, - .controls = rt5514_snd_controls, - .num_controls = ARRAY_SIZE(rt5514_snd_controls), - .dapm_widgets = rt5514_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(rt5514_dapm_widgets), - .dapm_routes = rt5514_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(rt5514_dapm_routes), + .component_driver = { + .controls = rt5514_snd_controls, + .num_controls = ARRAY_SIZE(rt5514_snd_controls), + .dapm_widgets = rt5514_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5514_dapm_widgets), + .dapm_routes = rt5514_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5514_dapm_routes), + }, }; static const struct regmap_config rt5514_i2c_regmap = { @@ -1071,9 +1076,18 @@ static const struct of_device_id rt5514_of_match[] = { MODULE_DEVICE_TABLE(of, rt5514_of_match); #endif +static int rt5514_parse_dt(struct rt5514_priv *rt5514, struct device *dev) +{ + device_property_read_u32(dev, "realtek,dmic-init-delay-ms", + &rt5514->pdata.dmic_init_delay); + + return 0; +} + static int rt5514_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { + struct rt5514_platform_data *pdata = dev_get_platdata(&i2c->dev); struct rt5514_priv *rt5514; int ret; unsigned int val; @@ -1085,6 +1099,11 @@ static int rt5514_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, rt5514); + if (pdata) + rt5514->pdata = *pdata; + else if (i2c->dev.of_node) + rt5514_parse_dt(rt5514, &i2c->dev); + rt5514->i2c_regmap = devm_regmap_init_i2c(i2c, &rt5514_i2c_regmap); if (IS_ERR(rt5514->i2c_regmap)) { ret = PTR_ERR(rt5514->i2c_regmap); diff --git a/sound/soc/codecs/rt5514.h b/sound/soc/codecs/rt5514.h index 68883c68e999..5d343fb6d125 100644 --- a/sound/soc/codecs/rt5514.h +++ b/sound/soc/codecs/rt5514.h @@ -13,6 +13,7 @@ #define __RT5514_H__ #include <linux/clk.h> +#include <sound/rt5514.h> #define RT5514_DEVICE_ID 0x10ec5514 @@ -196,8 +197,8 @@ #define RT5514_AD_AD_MIX_BIT 10 #define RT5514_AD_AD_MUTE (0x1 << 7) #define RT5514_AD_AD_MUTE_BIT 7 -#define RT5514_AD_GAIN_MASK (0x7f << 0) -#define RT5514_AD_GAIN_SFT 0 +#define RT5514_AD_GAIN_MASK (0x3f << 1) +#define RT5514_AD_GAIN_SFT 1 /* RT5514_ANA_CTRL_MICBST (0x2220) */ #define RT5514_SEL_BSTL_MASK (0xf << 4) @@ -243,6 +244,7 @@ enum { }; struct rt5514_priv { + struct rt5514_platform_data pdata; struct snd_soc_codec *codec; struct regmap *i2c_regmap, *regmap; struct clk *mclk; diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c index f527b5b2817b..7d6e0823f98f 100644 --- a/sound/soc/codecs/rt5616.c +++ b/sound/soc/codecs/rt5616.c @@ -294,8 +294,7 @@ static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0); static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); /* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ -static unsigned int bst_tlv[] = { - TLV_DB_RANGE_HEAD(7), +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(bst_tlv, 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), @@ -303,7 +302,7 @@ static unsigned int bst_tlv[] = { 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0), -}; +); static const struct snd_kcontrol_new rt5616_snd_controls[] = { /* Headphone Output Volume */ @@ -961,8 +960,7 @@ static int rt5616_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec); unsigned int val_len = 0, val_clk, mask_clk; int pre_div, bclk_ms, frame_size; @@ -1267,14 +1265,14 @@ static int rt5616_resume(struct snd_soc_codec *codec) #define RT5616_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) -struct snd_soc_dai_ops rt5616_aif_dai_ops = { +static struct snd_soc_dai_ops rt5616_aif_dai_ops = { .hw_params = rt5616_hw_params, .set_fmt = rt5616_set_dai_fmt, .set_sysclk = rt5616_set_dai_sysclk, .set_pll = rt5616_set_dai_pll, }; -struct snd_soc_dai_driver rt5616_dai[] = { +static struct snd_soc_dai_driver rt5616_dai[] = { { .name = "rt5616-aif1", .id = RT5616_AIF1, @@ -1302,12 +1300,14 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5616 = { .resume = rt5616_resume, .set_bias_level = rt5616_set_bias_level, .idle_bias_off = true, - .controls = rt5616_snd_controls, - .num_controls = ARRAY_SIZE(rt5616_snd_controls), - .dapm_widgets = rt5616_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(rt5616_dapm_widgets), - .dapm_routes = rt5616_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(rt5616_dapm_routes), + .component_driver = { + .controls = rt5616_snd_controls, + .num_controls = ARRAY_SIZE(rt5616_snd_controls), + .dapm_widgets = rt5616_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5616_dapm_widgets), + .dapm_routes = rt5616_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5616_dapm_routes), + }, }; static const struct regmap_config rt5616_regmap = { diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c index 1be2bab7dee3..0e418089c053 100644 --- a/sound/soc/codecs/rt5631.c +++ b/sound/soc/codecs/rt5631.c @@ -1657,12 +1657,14 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5631 = { .probe = rt5631_probe, .set_bias_level = rt5631_set_bias_level, .suspend_bias_off = true, - .controls = rt5631_snd_controls, - .num_controls = ARRAY_SIZE(rt5631_snd_controls), - .dapm_widgets = rt5631_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(rt5631_dapm_widgets), - .dapm_routes = rt5631_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(rt5631_dapm_routes), + .component_driver = { + .controls = rt5631_snd_controls, + .num_controls = ARRAY_SIZE(rt5631_snd_controls), + .dapm_widgets = rt5631_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5631_dapm_widgets), + .dapm_routes = rt5631_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5631_dapm_routes), + }, }; static const struct i2c_device_id rt5631_i2c_id[] = { diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 09e8988bbb2d..e29a6defefa0 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -423,6 +423,8 @@ static const struct snd_kcontrol_new rt5640_snd_controls[] = { SOC_DOUBLE_TLV("ADC Capture Volume", RT5640_ADC_DIG_VOL, RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 127, 0, adc_vol_tlv), + SOC_DOUBLE("Mono ADC Capture Switch", RT5640_DUMMY1, + RT5640_M_MONO_ADC_L_SFT, RT5640_M_MONO_ADC_R_SFT, 1, 1), SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5640_ADC_DATA, RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 127, 0, adc_vol_tlv), @@ -1870,6 +1872,9 @@ static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai, case RT5640_SCLK_S_PLL1: reg_val |= RT5640_SCLK_SRC_PLL1; break; + case RT5640_SCLK_S_RCCLK: + reg_val |= RT5640_SCLK_SRC_RCCLK; + break; default: dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id); return -EINVAL; @@ -2261,12 +2266,14 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5640 = { .resume = rt5640_resume, .set_bias_level = rt5640_set_bias_level, .idle_bias_off = true, - .controls = rt5640_snd_controls, - .num_controls = ARRAY_SIZE(rt5640_snd_controls), - .dapm_widgets = rt5640_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(rt5640_dapm_widgets), - .dapm_routes = rt5640_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(rt5640_dapm_routes), + .component_driver = { + .controls = rt5640_snd_controls, + .num_controls = ARRAY_SIZE(rt5640_snd_controls), + .dapm_widgets = rt5640_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5640_dapm_widgets), + .dapm_routes = rt5640_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5640_dapm_routes), + }, }; static const struct regmap_config rt5640_regmap = { @@ -2402,6 +2409,9 @@ static int rt5640_i2c_probe(struct i2c_client *i2c, if (ret != 0) dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); + regmap_update_bits(rt5640->regmap, RT5640_DUMMY1, + RT5640_MCLK_DET, RT5640_MCLK_DET); + if (rt5640->pdata.in1_diff) regmap_update_bits(rt5640->regmap, RT5640_IN1_IN2, RT5640_IN_DF1, RT5640_IN_DF1); diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h index 58b664b06c16..b8a811732a52 100644 --- a/sound/soc/codecs/rt5640.h +++ b/sound/soc/codecs/rt5640.h @@ -984,6 +984,7 @@ #define RT5640_SCLK_SRC_SFT 14 #define RT5640_SCLK_SRC_MCLK (0x0 << 14) #define RT5640_SCLK_SRC_PLL1 (0x1 << 14) +#define RT5640_SCLK_SRC_RCCLK (0x2 << 14) #define RT5640_PLL1_SRC_MASK (0x3 << 12) #define RT5640_PLL1_SRC_SFT 12 #define RT5640_PLL1_SRC_MCLK (0x0 << 12) @@ -1969,6 +1970,12 @@ #define RT5640_ZCD_HP_DIS (0x0 << 15) #define RT5640_ZCD_HP_EN (0x1 << 15) +/* General Control 1 (0xfa) */ +#define RT5640_M_MONO_ADC_L (0x1 << 13) +#define RT5640_M_MONO_ADC_L_SFT 13 +#define RT5640_M_MONO_ADC_R (0x1 << 12) +#define RT5640_M_MONO_ADC_R_SFT 12 +#define RT5640_MCLK_DET (0x1 << 11) /* Codec Private Register definition */ /* 3D Speaker Control (0x63) */ diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 490bfe661346..1ac96ef9ee20 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3484,12 +3484,14 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5645 = { .resume = rt5645_resume, .set_bias_level = rt5645_set_bias_level, .idle_bias_off = true, - .controls = rt5645_snd_controls, - .num_controls = ARRAY_SIZE(rt5645_snd_controls), - .dapm_widgets = rt5645_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(rt5645_dapm_widgets), - .dapm_routes = rt5645_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(rt5645_dapm_routes), + .component_driver = { + .controls = rt5645_snd_controls, + .num_controls = ARRAY_SIZE(rt5645_snd_controls), + .dapm_widgets = rt5645_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5645_dapm_widgets), + .dapm_routes = rt5645_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5645_dapm_routes), + }, }; static const struct regmap_config rt5645_regmap = { @@ -3831,6 +3833,9 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, } } + regmap_update_bits(rt5645->regmap, RT5645_ADDA_CLK1, + RT5645_I2S_PD1_MASK, RT5645_I2S_PD1_2); + if (rt5645->pdata.jd_invert) { regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2, RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV); diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c index 7a6197042423..f5d34153e21f 100644 --- a/sound/soc/codecs/rt5651.c +++ b/sound/soc/codecs/rt5651.c @@ -1712,12 +1712,14 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5651 = { .resume = rt5651_resume, .set_bias_level = rt5651_set_bias_level, .idle_bias_off = true, - .controls = rt5651_snd_controls, - .num_controls = ARRAY_SIZE(rt5651_snd_controls), - .dapm_widgets = rt5651_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(rt5651_dapm_widgets), - .dapm_routes = rt5651_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(rt5651_dapm_routes), + .component_driver = { + .controls = rt5651_snd_controls, + .num_controls = ARRAY_SIZE(rt5651_snd_controls), + .dapm_widgets = rt5651_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5651_dapm_widgets), + .dapm_routes = rt5651_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5651_dapm_routes), + }, }; static const struct regmap_config rt5651_regmap = { diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c index 1b30914c2d91..db54550aed60 100644 --- a/sound/soc/codecs/rt5659.c +++ b/sound/soc/codecs/rt5659.c @@ -9,6 +9,7 @@ * published by the Free Software Foundation. */ +#include <linux/clk.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @@ -3565,7 +3566,9 @@ static int rt5659_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) static int rt5659_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec); + int ret; switch (level) { case SND_SOC_BIAS_PREPARE: @@ -3582,6 +3585,17 @@ static int rt5659_set_bias_level(struct snd_soc_codec *codec, RT5659_PWR_FV1 | RT5659_PWR_FV2); break; + case SND_SOC_BIAS_STANDBY: + if (dapm->bias_level == SND_SOC_BIAS_OFF) { + ret = clk_prepare_enable(rt5659->mclk); + if (ret) { + dev_err(codec->dev, + "failed to enable MCLK: %d\n", ret); + return ret; + } + } + break; + case SND_SOC_BIAS_OFF: regmap_update_bits(rt5659->regmap, RT5659_PWR_DIG_1, RT5659_PWR_LDO, 0); @@ -3591,6 +3605,7 @@ static int rt5659_set_bias_level(struct snd_soc_codec *codec, RT5659_PWR_MB | RT5659_PWR_VREF2); regmap_update_bits(rt5659->regmap, RT5659_DIG_MISC, RT5659_DIG_GATE_CTRL, 0); + clk_disable_unprepare(rt5659->mclk); break; default: @@ -3722,12 +3737,14 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5659 = { .resume = rt5659_resume, .set_bias_level = rt5659_set_bias_level, .idle_bias_off = true, - .controls = rt5659_snd_controls, - .num_controls = ARRAY_SIZE(rt5659_snd_controls), - .dapm_widgets = rt5659_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(rt5659_dapm_widgets), - .dapm_routes = rt5659_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(rt5659_dapm_routes), + .component_driver = { + .controls = rt5659_snd_controls, + .num_controls = ARRAY_SIZE(rt5659_snd_controls), + .dapm_widgets = rt5659_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5659_dapm_widgets), + .dapm_routes = rt5659_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5659_dapm_routes), + }, }; @@ -4020,6 +4037,15 @@ static int rt5659_i2c_probe(struct i2c_client *i2c, regmap_write(rt5659->regmap, RT5659_RESET, 0); + /* Check if MCLK provided */ + rt5659->mclk = devm_clk_get(&i2c->dev, "mclk"); + if (IS_ERR(rt5659->mclk)) { + if (PTR_ERR(rt5659->mclk) != -ENOENT) + return PTR_ERR(rt5659->mclk); + /* Otherwise mark the mclk pointer to NULL */ + rt5659->mclk = NULL; + } + rt5659_calibrate(rt5659); /* line in diff mode*/ @@ -4163,6 +4189,9 @@ static int rt5659_i2c_probe(struct i2c_client *i2c, if (ret) dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + /* Enable IRQ output for GPIO1 pin any way */ + regmap_update_bits(rt5659->regmap, RT5659_GPIO_CTRL_1, + RT5659_GP1_PIN_MASK, RT5659_GP1_PIN_IRQ); } return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5659, diff --git a/sound/soc/codecs/rt5659.h b/sound/soc/codecs/rt5659.h index d31c9e5bcec8..8f1aeef08489 100644 --- a/sound/soc/codecs/rt5659.h +++ b/sound/soc/codecs/rt5659.h @@ -180,9 +180,9 @@ #define RT5659_IRQ_CTRL_1 0x00b6 #define RT5659_IRQ_CTRL_2 0x00b7 #define RT5659_IRQ_CTRL_3 0x00b8 -#define RT5659_IRQ_CTRL_4 0x00b9 -#define RT5659_IRQ_CTRL_5 0x00ba -#define RT5659_IRQ_CTRL_6 0x00bb +#define RT5659_IRQ_CTRL_4 0x00ba +#define RT5659_IRQ_CTRL_5 0x00bb +#define RT5659_IRQ_CTRL_6 0x00bc #define RT5659_INT_ST_1 0x00be #define RT5659_INT_ST_2 0x00bf #define RT5659_GPIO_CTRL_1 0x00c0 @@ -1796,6 +1796,7 @@ struct rt5659_priv { struct gpio_desc *gpiod_reset; struct snd_soc_jack *hs_jack; struct delayed_work jack_detect_work; + struct clk *mclk; int sysclk; int sysclk_src; diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c new file mode 100644 index 000000000000..76cf76a2e9b6 --- /dev/null +++ b/sound/soc/codecs/rt5660.c @@ -0,0 +1,1357 @@ +/* + * rt5660.c -- RT5660 ALSA SoC audio codec driver + * + * Copyright 2016 Realtek Semiconductor Corp. + * Author: Oder Chiou <oder_chiou@realtek.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/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/acpi.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 "rl6231.h" +#include "rt5660.h" + +#define RT5660_DEVICE_ID 0x6338 + +#define RT5660_PR_RANGE_BASE (0xff + 1) +#define RT5660_PR_SPACING 0x100 + +#define RT5660_PR_BASE (RT5660_PR_RANGE_BASE + (0 * RT5660_PR_SPACING)) + +static const struct regmap_range_cfg rt5660_ranges[] = { + { .name = "PR", .range_min = RT5660_PR_BASE, + .range_max = RT5660_PR_BASE + 0xf3, + .selector_reg = RT5660_PRIV_INDEX, + .selector_mask = 0xff, + .selector_shift = 0x0, + .window_start = RT5660_PRIV_DATA, + .window_len = 0x1, }, +}; + +static const struct reg_sequence rt5660_patch[] = { + { RT5660_ALC_PGA_CTRL2, 0x44c3 }, + { RT5660_PR_BASE + 0x3d, 0x2600 }, +}; + +static const struct reg_default rt5660_reg[] = { + { 0x00, 0x0000 }, + { 0x01, 0xc800 }, + { 0x02, 0xc8c8 }, + { 0x0d, 0x1010 }, + { 0x0e, 0x1010 }, + { 0x19, 0xafaf }, + { 0x1c, 0x2f2f }, + { 0x1e, 0x0000 }, + { 0x27, 0x6060 }, + { 0x29, 0x8080 }, + { 0x2a, 0x4242 }, + { 0x2f, 0x0000 }, + { 0x3b, 0x0000 }, + { 0x3c, 0x007f }, + { 0x3d, 0x0000 }, + { 0x3e, 0x007f }, + { 0x45, 0xe000 }, + { 0x46, 0x003e }, + { 0x48, 0xf800 }, + { 0x4a, 0x0004 }, + { 0x4d, 0x0000 }, + { 0x4e, 0x0000 }, + { 0x4f, 0x01ff }, + { 0x50, 0x0000 }, + { 0x51, 0x0000 }, + { 0x52, 0x01ff }, + { 0x61, 0x0000 }, + { 0x62, 0x0000 }, + { 0x63, 0x00c0 }, + { 0x64, 0x0000 }, + { 0x65, 0x0000 }, + { 0x66, 0x0000 }, + { 0x70, 0x8000 }, + { 0x73, 0x7000 }, + { 0x74, 0x3c00 }, + { 0x75, 0x2800 }, + { 0x80, 0x0000 }, + { 0x81, 0x0000 }, + { 0x82, 0x0000 }, + { 0x8c, 0x0228 }, + { 0x8d, 0xa000 }, + { 0x8e, 0x0000 }, + { 0x92, 0x0000 }, + { 0x93, 0x3000 }, + { 0xa1, 0x0059 }, + { 0xa2, 0x0001 }, + { 0xa3, 0x5c80 }, + { 0xa4, 0x0146 }, + { 0xa5, 0x1f1f }, + { 0xa6, 0x78c6 }, + { 0xa7, 0xe5ec }, + { 0xa8, 0xba61 }, + { 0xa9, 0x3c78 }, + { 0xaa, 0x8ae2 }, + { 0xab, 0xe5ec }, + { 0xac, 0xc600 }, + { 0xad, 0xba61 }, + { 0xae, 0x17ed }, + { 0xb0, 0x2080 }, + { 0xb1, 0x0000 }, + { 0xb3, 0x001f }, + { 0xb4, 0x020c }, + { 0xb5, 0x1f00 }, + { 0xb6, 0x0000 }, + { 0xb7, 0x4000 }, + { 0xbb, 0x0000 }, + { 0xbd, 0x0000 }, + { 0xbe, 0x0000 }, + { 0xbf, 0x0100 }, + { 0xc0, 0x0000 }, + { 0xc2, 0x0000 }, + { 0xd3, 0xa220 }, + { 0xd9, 0x0809 }, + { 0xda, 0x0000 }, + { 0xe0, 0x8000 }, + { 0xe1, 0x0200 }, + { 0xe2, 0x8000 }, + { 0xe3, 0x0200 }, + { 0xe4, 0x0f20 }, + { 0xe5, 0x001f }, + { 0xe6, 0x020c }, + { 0xe7, 0x1f00 }, + { 0xe8, 0x0000 }, + { 0xe9, 0x4000 }, + { 0xea, 0x00a6 }, + { 0xeb, 0x04c3 }, + { 0xec, 0x27c8 }, + { 0xed, 0x7418 }, + { 0xee, 0xbf50 }, + { 0xef, 0x0045 }, + { 0xf0, 0x0007 }, + { 0xfa, 0x0000 }, + { 0xfd, 0x0000 }, + { 0xfe, 0x10ec }, + { 0xff, 0x6338 }, +}; + +static bool rt5660_volatile_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt5660_ranges); i++) + if ((reg >= rt5660_ranges[i].window_start && + reg <= rt5660_ranges[i].window_start + + rt5660_ranges[i].window_len) || + (reg >= rt5660_ranges[i].range_min && + reg <= rt5660_ranges[i].range_max)) + return true; + + switch (reg) { + case RT5660_RESET: + case RT5660_PRIV_DATA: + case RT5660_EQ_CTRL1: + case RT5660_IRQ_CTRL2: + case RT5660_INT_IRQ_ST: + case RT5660_VENDOR_ID: + case RT5660_VENDOR_ID1: + case RT5660_VENDOR_ID2: + return true; + default: + return false; + } +} + +static bool rt5660_readable_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt5660_ranges); i++) + if ((reg >= rt5660_ranges[i].window_start && + reg <= rt5660_ranges[i].window_start + + rt5660_ranges[i].window_len) || + (reg >= rt5660_ranges[i].range_min && + reg <= rt5660_ranges[i].range_max)) + return true; + + switch (reg) { + case RT5660_RESET: + case RT5660_SPK_VOL: + case RT5660_LOUT_VOL: + case RT5660_IN1_IN2: + case RT5660_IN3_IN4: + case RT5660_DAC1_DIG_VOL: + case RT5660_STO1_ADC_DIG_VOL: + case RT5660_ADC_BST_VOL1: + case RT5660_STO1_ADC_MIXER: + case RT5660_AD_DA_MIXER: + case RT5660_STO_DAC_MIXER: + case RT5660_DIG_INF1_DATA: + case RT5660_REC_L1_MIXER: + case RT5660_REC_L2_MIXER: + case RT5660_REC_R1_MIXER: + case RT5660_REC_R2_MIXER: + case RT5660_LOUT_MIXER: + case RT5660_SPK_MIXER: + case RT5660_SPO_MIXER: + case RT5660_SPO_CLSD_RATIO: + case RT5660_OUT_L_GAIN1: + case RT5660_OUT_L_GAIN2: + case RT5660_OUT_L1_MIXER: + case RT5660_OUT_R_GAIN1: + case RT5660_OUT_R_GAIN2: + case RT5660_OUT_R1_MIXER: + case RT5660_PWR_DIG1: + case RT5660_PWR_DIG2: + case RT5660_PWR_ANLG1: + case RT5660_PWR_ANLG2: + case RT5660_PWR_MIXER: + case RT5660_PWR_VOL: + case RT5660_PRIV_INDEX: + case RT5660_PRIV_DATA: + case RT5660_I2S1_SDP: + case RT5660_ADDA_CLK1: + case RT5660_ADDA_CLK2: + case RT5660_DMIC_CTRL1: + case RT5660_GLB_CLK: + case RT5660_PLL_CTRL1: + case RT5660_PLL_CTRL2: + case RT5660_CLSD_AMP_OC_CTRL: + case RT5660_CLSD_AMP_CTRL: + case RT5660_LOUT_AMP_CTRL: + case RT5660_SPK_AMP_SPKVDD: + case RT5660_MICBIAS: + case RT5660_CLSD_OUT_CTRL1: + case RT5660_CLSD_OUT_CTRL2: + case RT5660_DIPOLE_MIC_CTRL1: + case RT5660_DIPOLE_MIC_CTRL2: + case RT5660_DIPOLE_MIC_CTRL3: + case RT5660_DIPOLE_MIC_CTRL4: + case RT5660_DIPOLE_MIC_CTRL5: + case RT5660_DIPOLE_MIC_CTRL6: + case RT5660_DIPOLE_MIC_CTRL7: + case RT5660_DIPOLE_MIC_CTRL8: + case RT5660_DIPOLE_MIC_CTRL9: + case RT5660_DIPOLE_MIC_CTRL10: + case RT5660_DIPOLE_MIC_CTRL11: + case RT5660_DIPOLE_MIC_CTRL12: + case RT5660_EQ_CTRL1: + case RT5660_EQ_CTRL2: + case RT5660_DRC_AGC_CTRL1: + case RT5660_DRC_AGC_CTRL2: + case RT5660_DRC_AGC_CTRL3: + case RT5660_DRC_AGC_CTRL4: + case RT5660_DRC_AGC_CTRL5: + case RT5660_JD_CTRL: + case RT5660_IRQ_CTRL1: + case RT5660_IRQ_CTRL2: + case RT5660_INT_IRQ_ST: + case RT5660_GPIO_CTRL1: + case RT5660_GPIO_CTRL2: + case RT5660_WIND_FILTER_CTRL1: + case RT5660_SV_ZCD1: + case RT5660_SV_ZCD2: + case RT5660_DRC1_LM_CTRL1: + case RT5660_DRC1_LM_CTRL2: + case RT5660_DRC2_LM_CTRL1: + case RT5660_DRC2_LM_CTRL2: + case RT5660_MULTI_DRC_CTRL: + case RT5660_DRC2_CTRL1: + case RT5660_DRC2_CTRL2: + case RT5660_DRC2_CTRL3: + case RT5660_DRC2_CTRL4: + case RT5660_DRC2_CTRL5: + case RT5660_ALC_PGA_CTRL1: + case RT5660_ALC_PGA_CTRL2: + case RT5660_ALC_PGA_CTRL3: + case RT5660_ALC_PGA_CTRL4: + case RT5660_ALC_PGA_CTRL5: + case RT5660_ALC_PGA_CTRL6: + case RT5660_ALC_PGA_CTRL7: + case RT5660_GEN_CTRL1: + case RT5660_GEN_CTRL2: + case RT5660_GEN_CTRL3: + case RT5660_VENDOR_ID: + case RT5660_VENDOR_ID1: + case RT5660_VENDOR_ID2: + return true; + default: + return false; + } +} + +static const DECLARE_TLV_DB_SCALE(rt5660_out_vol_tlv, -4650, 150, 0); +static const DECLARE_TLV_DB_SCALE(rt5660_dac_vol_tlv, -6525, 75, 0); +static const DECLARE_TLV_DB_SCALE(rt5660_adc_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(rt5660_adc_bst_tlv, 0, 1200, 0); +static const DECLARE_TLV_DB_SCALE(rt5660_bst_tlv, -1200, 75, 0); + +static const struct snd_kcontrol_new rt5660_snd_controls[] = { + /* Speaker Output Volume */ + SOC_SINGLE("Speaker Playback Switch", RT5660_SPK_VOL, RT5660_L_MUTE_SFT, + 1, 1), + SOC_SINGLE_TLV("Speaker Playback Volume", RT5660_SPK_VOL, + RT5660_L_VOL_SFT, 39, 1, rt5660_out_vol_tlv), + + /* OUTPUT Control */ + SOC_DOUBLE("OUT Playback Switch", RT5660_LOUT_VOL, RT5660_L_MUTE_SFT, + RT5660_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("OUT Playback Volume", RT5660_LOUT_VOL, RT5660_L_VOL_SFT, + RT5660_R_VOL_SFT, 39, 1, rt5660_out_vol_tlv), + + /* DAC Digital Volume */ + SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5660_DAC1_DIG_VOL, + RT5660_DAC_L1_VOL_SFT, RT5660_DAC_R1_VOL_SFT, 87, 0, + rt5660_dac_vol_tlv), + + /* IN1/IN2/IN3 Control */ + SOC_SINGLE_TLV("IN1 Boost Volume", RT5660_IN1_IN2, RT5660_BST_SFT1, 69, + 0, rt5660_bst_tlv), + SOC_SINGLE_TLV("IN2 Boost Volume", RT5660_IN1_IN2, RT5660_BST_SFT2, 69, + 0, rt5660_bst_tlv), + SOC_SINGLE_TLV("IN3 Boost Volume", RT5660_IN3_IN4, RT5660_BST_SFT3, 69, + 0, rt5660_bst_tlv), + + /* ADC Digital Volume Control */ + SOC_DOUBLE("ADC Capture Switch", RT5660_STO1_ADC_DIG_VOL, + RT5660_L_MUTE_SFT, RT5660_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("ADC Capture Volume", RT5660_STO1_ADC_DIG_VOL, + RT5660_ADC_L_VOL_SFT, RT5660_ADC_R_VOL_SFT, 63, 0, + rt5660_adc_vol_tlv), + + /* ADC Boost Volume Control */ + SOC_DOUBLE_TLV("STO1 ADC Boost Gain Volume", RT5660_ADC_BST_VOL1, + RT5660_STO1_ADC_L_BST_SFT, RT5660_STO1_ADC_R_BST_SFT, 3, 0, + rt5660_adc_bst_tlv), +}; + +/** + * rt5660_set_dmic_clk - Set parameter of dmic. + * + * @w: DAPM widget. + * @kcontrol: The kcontrol of this widget. + * @event: Event id. + * + */ +static int rt5660_set_dmic_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec); + int idx, rate; + + rate = rt5660->sysclk / rl6231_get_pre_div(rt5660->regmap, + RT5660_ADDA_CLK1, RT5660_I2S_PD1_SFT); + idx = rl6231_calc_dmic_clk(rate); + if (idx < 0) + dev_err(codec->dev, "Failed to set DMIC clock\n"); + else + snd_soc_update_bits(codec, RT5660_DMIC_CTRL1, + RT5660_DMIC_CLK_MASK, idx << RT5660_DMIC_CLK_SFT); + + return idx; +} + +static int rt5660_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm); + unsigned int val; + + val = snd_soc_read(codec, RT5660_GLB_CLK); + val &= RT5660_SCLK_SRC_MASK; + if (val == RT5660_SCLK_SRC_PLL1) + return 1; + else + return 0; +} + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt5660_sto1_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5660_STO1_ADC_MIXER, + RT5660_M_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5660_STO1_ADC_MIXER, + RT5660_M_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5660_sto1_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5660_STO1_ADC_MIXER, + RT5660_M_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5660_STO1_ADC_MIXER, + RT5660_M_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5660_dac_l_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5660_AD_DA_MIXER, + RT5660_M_ADCMIX_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5660_AD_DA_MIXER, + RT5660_M_DAC1_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5660_dac_r_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5660_AD_DA_MIXER, + RT5660_M_ADCMIX_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5660_AD_DA_MIXER, + RT5660_M_DAC1_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5660_sto_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5660_STO_DAC_MIXER, + RT5660_M_DAC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5660_STO_DAC_MIXER, + RT5660_M_DAC_R1_STO_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5660_sto_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5660_STO_DAC_MIXER, + RT5660_M_DAC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L1 Switch", RT5660_STO_DAC_MIXER, + RT5660_M_DAC_L1_STO_R_SFT, 1, 1), +}; + +/* Analog Input Mixer */ +static const struct snd_kcontrol_new rt5660_rec_l_mix[] = { + SOC_DAPM_SINGLE("BST3 Switch", RT5660_REC_L2_MIXER, + RT5660_M_BST3_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5660_REC_L2_MIXER, + RT5660_M_BST2_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5660_REC_L2_MIXER, + RT5660_M_BST1_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("OUT MIXL Switch", RT5660_REC_L2_MIXER, + RT5660_M_OM_L_RM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5660_rec_r_mix[] = { + SOC_DAPM_SINGLE("BST3 Switch", RT5660_REC_R2_MIXER, + RT5660_M_BST3_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5660_REC_R2_MIXER, + RT5660_M_BST2_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5660_REC_R2_MIXER, + RT5660_M_BST1_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("OUT MIXR Switch", RT5660_REC_R2_MIXER, + RT5660_M_OM_R_RM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5660_spk_mix[] = { + SOC_DAPM_SINGLE("BST3 Switch", RT5660_SPK_MIXER, + RT5660_M_BST3_SM_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5660_SPK_MIXER, + RT5660_M_BST1_SM_SFT, 1, 1), + SOC_DAPM_SINGLE("DACL Switch", RT5660_SPK_MIXER, + RT5660_M_DACL_SM_SFT, 1, 1), + SOC_DAPM_SINGLE("DACR Switch", RT5660_SPK_MIXER, + RT5660_M_DACR_SM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTMIXL Switch", RT5660_SPK_MIXER, + RT5660_M_OM_L_SM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5660_out_l_mix[] = { + SOC_DAPM_SINGLE("BST3 Switch", RT5660_OUT_L1_MIXER, + RT5660_M_BST3_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5660_OUT_L1_MIXER, + RT5660_M_BST2_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5660_OUT_L1_MIXER, + RT5660_M_BST1_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("RECMIXL Switch", RT5660_OUT_L1_MIXER, + RT5660_M_RM_L_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DACR Switch", RT5660_OUT_L1_MIXER, + RT5660_M_DAC_R_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DACL Switch", RT5660_OUT_L1_MIXER, + RT5660_M_DAC_L_OM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5660_out_r_mix[] = { + SOC_DAPM_SINGLE("BST2 Switch", RT5660_OUT_R1_MIXER, + RT5660_M_BST2_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5660_OUT_R1_MIXER, + RT5660_M_BST1_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("RECMIXR Switch", RT5660_OUT_R1_MIXER, + RT5660_M_RM_R_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DACR Switch", RT5660_OUT_R1_MIXER, + RT5660_M_DAC_R_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DACL Switch", RT5660_OUT_R1_MIXER, + RT5660_M_DAC_L_OM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5660_spo_mix[] = { + SOC_DAPM_SINGLE("DACR Switch", RT5660_SPO_MIXER, + RT5660_M_DAC_R_SPM_SFT, 1, 1), + SOC_DAPM_SINGLE("DACL Switch", RT5660_SPO_MIXER, + RT5660_M_DAC_L_SPM_SFT, 1, 1), + SOC_DAPM_SINGLE("SPKVOL Switch", RT5660_SPO_MIXER, + RT5660_M_SV_SPM_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5660_SPO_MIXER, + RT5660_M_BST1_SPM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5660_lout_mix[] = { + SOC_DAPM_SINGLE("DAC Switch", RT5660_LOUT_MIXER, + RT5660_M_DAC1_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTMIX Switch", RT5660_LOUT_MIXER, + RT5660_M_LOVOL_LM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new spk_vol_control = + SOC_DAPM_SINGLE("Switch", RT5660_SPK_VOL, + RT5660_VOL_L_SFT, 1, 1); + +static const struct snd_kcontrol_new lout_l_vol_control = + SOC_DAPM_SINGLE("Switch", RT5660_LOUT_VOL, + RT5660_VOL_L_SFT, 1, 1); + +static const struct snd_kcontrol_new lout_r_vol_control = + SOC_DAPM_SINGLE("Switch", RT5660_LOUT_VOL, + RT5660_VOL_R_SFT, 1, 1); + +/* Interface data select */ +static const char * const rt5660_data_select[] = { + "L/R", "R/L", "L/L", "R/R" +}; + +static const SOC_ENUM_SINGLE_DECL(rt5660_if1_dac_enum, + RT5660_DIG_INF1_DATA, RT5660_IF1_DAC_IN_SFT, rt5660_data_select); + +static const SOC_ENUM_SINGLE_DECL(rt5660_if1_adc_enum, + RT5660_DIG_INF1_DATA, RT5660_IF1_ADC_IN_SFT, rt5660_data_select); + +static const struct snd_kcontrol_new rt5660_if1_dac_swap_mux = + SOC_DAPM_ENUM("IF1 DAC Swap Source", rt5660_if1_dac_enum); + +static const struct snd_kcontrol_new rt5660_if1_adc_swap_mux = + SOC_DAPM_ENUM("IF1 ADC Swap Source", rt5660_if1_adc_enum); + +static int rt5660_lout_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, RT5660_LOUT_AMP_CTRL, + RT5660_LOUT_CO_MASK | RT5660_LOUT_CB_MASK, + RT5660_LOUT_CO_EN | RT5660_LOUT_CB_PU); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, RT5660_LOUT_AMP_CTRL, + RT5660_LOUT_CO_MASK | RT5660_LOUT_CB_MASK, + RT5660_LOUT_CO_DIS | RT5660_LOUT_CB_PD); + break; + + default: + return 0; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt5660_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("LDO2", RT5660_PWR_ANLG1, + RT5660_PWR_LDO2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL1", RT5660_PWR_ANLG2, + RT5660_PWR_PLL_BIT, 0, NULL, 0), + + /* MICBIAS */ + SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5660_PWR_ANLG2, + RT5660_PWR_MB1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS2", RT5660_PWR_ANLG2, + RT5660_PWR_MB2_BIT, 0, NULL, 0), + + /* Input Side */ + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC L1"), + SND_SOC_DAPM_INPUT("DMIC R1"), + + SND_SOC_DAPM_INPUT("IN1P"), + SND_SOC_DAPM_INPUT("IN1N"), + SND_SOC_DAPM_INPUT("IN2P"), + SND_SOC_DAPM_INPUT("IN3P"), + SND_SOC_DAPM_INPUT("IN3N"), + + SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0, + rt5660_set_dmic_clk, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("DMIC Power", RT5660_DMIC_CTRL1, + RT5660_DMIC_1_EN_SFT, 0, NULL, 0), + + /* Boost */ + SND_SOC_DAPM_PGA("BST1", RT5660_PWR_ANLG2, RT5660_PWR_BST1_BIT, 0, + NULL, 0), + SND_SOC_DAPM_PGA("BST2", RT5660_PWR_ANLG2, RT5660_PWR_BST2_BIT, 0, + NULL, 0), + SND_SOC_DAPM_PGA("BST3", RT5660_PWR_ANLG2, RT5660_PWR_BST3_BIT, 0, + NULL, 0), + + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIXL", RT5660_PWR_MIXER, RT5660_PWR_RM_L_BIT, + 0, rt5660_rec_l_mix, ARRAY_SIZE(rt5660_rec_l_mix)), + SND_SOC_DAPM_MIXER("RECMIXR", RT5660_PWR_MIXER, RT5660_PWR_RM_R_BIT, + 0, rt5660_rec_r_mix, ARRAY_SIZE(rt5660_rec_r_mix)), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC R", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY("ADC L power", RT5660_PWR_DIG1, + RT5660_PWR_ADC_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC R power", RT5660_PWR_DIG1, + RT5660_PWR_ADC_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC clock", RT5660_PR_BASE + RT5660_CHOP_DAC_ADC, + 12, 0, NULL, 0), + + /* ADC Mixer */ + SND_SOC_DAPM_SUPPLY("adc stereo1 filter", RT5660_PWR_DIG2, + RT5660_PWR_ADC_S1F_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Sto1 ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5660_sto1_adc_l_mix, ARRAY_SIZE(rt5660_sto1_adc_l_mix)), + SND_SOC_DAPM_MIXER("Sto1 ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5660_sto1_adc_r_mix, ARRAY_SIZE(rt5660_sto1_adc_r_mix)), + + /* ADC */ + SND_SOC_DAPM_ADC("Stereo1 ADC MIXL", NULL, RT5660_STO1_ADC_DIG_VOL, + RT5660_L_MUTE_SFT, 1), + SND_SOC_DAPM_ADC("Stereo1 ADC MIXR", NULL, RT5660_STO1_ADC_DIG_VOL, + RT5660_R_MUTE_SFT, 1), + + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("I2S1", RT5660_PWR_DIG1, RT5660_PWR_I2S1_BIT, 0, + NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MUX("IF1 DAC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5660_if1_dac_swap_mux), + SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MUX("IF1 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5660_if1_adc_swap_mux), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + + /* Output Side */ + /* DAC mixer before sound effect */ + SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0, rt5660_dac_l_mix, + ARRAY_SIZE(rt5660_dac_l_mix)), + SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0, rt5660_dac_r_mix, + ARRAY_SIZE(rt5660_dac_r_mix)), + + /* DAC Mixer */ + SND_SOC_DAPM_SUPPLY("dac stereo1 filter", RT5660_PWR_DIG2, + RT5660_PWR_DAC_S1F_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5660_sto_dac_l_mix, ARRAY_SIZE(rt5660_sto_dac_l_mix)), + SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5660_sto_dac_r_mix, ARRAY_SIZE(rt5660_sto_dac_r_mix)), + + /* DACs */ + SND_SOC_DAPM_DAC("DAC L1", NULL, RT5660_PWR_DIG1, + RT5660_PWR_DAC_L1_BIT, 0), + SND_SOC_DAPM_DAC("DAC R1", NULL, RT5660_PWR_DIG1, + RT5660_PWR_DAC_R1_BIT, 0), + + /* OUT Mixer */ + SND_SOC_DAPM_MIXER("SPK MIX", RT5660_PWR_MIXER, RT5660_PWR_SM_BIT, + 0, rt5660_spk_mix, ARRAY_SIZE(rt5660_spk_mix)), + SND_SOC_DAPM_MIXER("OUT MIXL", RT5660_PWR_MIXER, RT5660_PWR_OM_L_BIT, + 0, rt5660_out_l_mix, ARRAY_SIZE(rt5660_out_l_mix)), + SND_SOC_DAPM_MIXER("OUT MIXR", RT5660_PWR_MIXER, RT5660_PWR_OM_R_BIT, + 0, rt5660_out_r_mix, ARRAY_SIZE(rt5660_out_r_mix)), + + /* Output Volume */ + SND_SOC_DAPM_SWITCH("SPKVOL", RT5660_PWR_VOL, + RT5660_PWR_SV_BIT, 0, &spk_vol_control), + SND_SOC_DAPM_PGA("DAC 1", SND_SOC_NOPM, + 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("LOUTVOL", SND_SOC_NOPM, + 0, 0, NULL, 0), + SND_SOC_DAPM_SWITCH("LOUTVOL L", SND_SOC_NOPM, + RT5660_PWR_LV_L_BIT, 0, &lout_l_vol_control), + SND_SOC_DAPM_SWITCH("LOUTVOL R", SND_SOC_NOPM, + RT5660_PWR_LV_R_BIT, 0, &lout_r_vol_control), + + /* HPO/LOUT/Mono Mixer */ + SND_SOC_DAPM_MIXER("SPO MIX", SND_SOC_NOPM, 0, + 0, rt5660_spo_mix, ARRAY_SIZE(rt5660_spo_mix)), + SND_SOC_DAPM_MIXER("LOUT MIX", SND_SOC_NOPM, 0, 0, + rt5660_lout_mix, ARRAY_SIZE(rt5660_lout_mix)), + SND_SOC_DAPM_SUPPLY("VREF HP", RT5660_GEN_CTRL1, + RT5660_PWR_VREF_HP_SFT, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("LOUT amp", 1, RT5660_PWR_ANLG1, + RT5660_PWR_HA_BIT, 0, rt5660_lout_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("SPK amp", 1, RT5660_PWR_DIG1, + RT5660_PWR_CLS_D_BIT, 0, NULL, 0), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("LOUTL"), + SND_SOC_DAPM_OUTPUT("LOUTR"), + SND_SOC_DAPM_OUTPUT("SPO"), +}; + +static const struct snd_soc_dapm_route rt5660_dapm_routes[] = { + { "MICBIAS1", NULL, "LDO2" }, + { "MICBIAS2", NULL, "LDO2" }, + + { "BST1", NULL, "IN1P" }, + { "BST1", NULL, "IN1N" }, + { "BST2", NULL, "IN2P" }, + { "BST3", NULL, "IN3P" }, + { "BST3", NULL, "IN3N" }, + + { "RECMIXL", "BST3 Switch", "BST3" }, + { "RECMIXL", "BST2 Switch", "BST2" }, + { "RECMIXL", "BST1 Switch", "BST1" }, + { "RECMIXL", "OUT MIXL Switch", "OUT MIXL" }, + + { "RECMIXR", "BST3 Switch", "BST3" }, + { "RECMIXR", "BST2 Switch", "BST2" }, + { "RECMIXR", "BST1 Switch", "BST1" }, + { "RECMIXR", "OUT MIXR Switch", "OUT MIXR" }, + + { "ADC L", NULL, "RECMIXL" }, + { "ADC L", NULL, "ADC L power" }, + { "ADC L", NULL, "ADC clock" }, + { "ADC R", NULL, "RECMIXR" }, + { "ADC R", NULL, "ADC R power" }, + { "ADC R", NULL, "ADC clock" }, + + {"DMIC L1", NULL, "DMIC CLK"}, + {"DMIC L1", NULL, "DMIC Power"}, + {"DMIC R1", NULL, "DMIC CLK"}, + {"DMIC R1", NULL, "DMIC Power"}, + + { "Sto1 ADC MIXL", "ADC1 Switch", "ADC L" }, + { "Sto1 ADC MIXL", "ADC2 Switch", "DMIC L1" }, + { "Sto1 ADC MIXR", "ADC1 Switch", "ADC R" }, + { "Sto1 ADC MIXR", "ADC2 Switch", "DMIC R1" }, + + { "Stereo1 ADC MIXL", NULL, "Sto1 ADC MIXL" }, + { "Stereo1 ADC MIXL", NULL, "adc stereo1 filter" }, + { "adc stereo1 filter", NULL, "PLL1", rt5660_is_sys_clk_from_pll }, + + { "Stereo1 ADC MIXR", NULL, "Sto1 ADC MIXR" }, + { "Stereo1 ADC MIXR", NULL, "adc stereo1 filter" }, + { "adc stereo1 filter", NULL, "PLL1", rt5660_is_sys_clk_from_pll }, + + { "IF1 ADC", NULL, "Stereo1 ADC MIXL" }, + { "IF1 ADC", NULL, "Stereo1 ADC MIXR" }, + { "IF1 ADC", NULL, "I2S1" }, + + { "IF1 ADC Swap Mux", "L/R", "IF1 ADC" }, + { "IF1 ADC Swap Mux", "R/L", "IF1 ADC" }, + { "IF1 ADC Swap Mux", "L/L", "IF1 ADC" }, + { "IF1 ADC Swap Mux", "R/R", "IF1 ADC" }, + { "AIF1TX", NULL, "IF1 ADC Swap Mux" }, + + { "IF1 DAC", NULL, "AIF1RX" }, + { "IF1 DAC", NULL, "I2S1" }, + + { "IF1 DAC Swap Mux", "L/R", "IF1 DAC" }, + { "IF1 DAC Swap Mux", "R/L", "IF1 DAC" }, + { "IF1 DAC Swap Mux", "L/L", "IF1 DAC" }, + { "IF1 DAC Swap Mux", "R/R", "IF1 DAC" }, + + { "IF1 DAC L", NULL, "IF1 DAC Swap Mux" }, + { "IF1 DAC R", NULL, "IF1 DAC Swap Mux" }, + + { "DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL" }, + { "DAC1 MIXL", "DAC1 Switch", "IF1 DAC L" }, + { "DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR" }, + { "DAC1 MIXR", "DAC1 Switch", "IF1 DAC R" }, + + { "Stereo DAC MIXL", "DAC L1 Switch", "DAC1 MIXL" }, + { "Stereo DAC MIXL", "DAC R1 Switch", "DAC1 MIXR" }, + { "Stereo DAC MIXL", NULL, "dac stereo1 filter" }, + { "dac stereo1 filter", NULL, "PLL1", rt5660_is_sys_clk_from_pll }, + { "Stereo DAC MIXR", "DAC R1 Switch", "DAC1 MIXR" }, + { "Stereo DAC MIXR", "DAC L1 Switch", "DAC1 MIXL" }, + { "Stereo DAC MIXR", NULL, "dac stereo1 filter" }, + { "dac stereo1 filter", NULL, "PLL1", rt5660_is_sys_clk_from_pll }, + + { "DAC L1", NULL, "Stereo DAC MIXL" }, + { "DAC R1", NULL, "Stereo DAC MIXR" }, + + { "SPK MIX", "BST3 Switch", "BST3" }, + { "SPK MIX", "BST1 Switch", "BST1" }, + { "SPK MIX", "DACL Switch", "DAC L1" }, + { "SPK MIX", "DACR Switch", "DAC R1" }, + { "SPK MIX", "OUTMIXL Switch", "OUT MIXL" }, + + { "OUT MIXL", "BST3 Switch", "BST3" }, + { "OUT MIXL", "BST2 Switch", "BST2" }, + { "OUT MIXL", "BST1 Switch", "BST1" }, + { "OUT MIXL", "RECMIXL Switch", "RECMIXL" }, + { "OUT MIXL", "DACR Switch", "DAC R1" }, + { "OUT MIXL", "DACL Switch", "DAC L1" }, + + { "OUT MIXR", "BST2 Switch", "BST2" }, + { "OUT MIXR", "BST1 Switch", "BST1" }, + { "OUT MIXR", "RECMIXR Switch", "RECMIXR" }, + { "OUT MIXR", "DACR Switch", "DAC R1" }, + { "OUT MIXR", "DACL Switch", "DAC L1" }, + + { "SPO MIX", "DACR Switch", "DAC R1" }, + { "SPO MIX", "DACL Switch", "DAC L1" }, + { "SPO MIX", "SPKVOL Switch", "SPKVOL" }, + { "SPO MIX", "BST1 Switch", "BST1" }, + + { "SPKVOL", "Switch", "SPK MIX" }, + { "LOUTVOL L", "Switch", "OUT MIXL" }, + { "LOUTVOL R", "Switch", "OUT MIXR" }, + + { "LOUTVOL", NULL, "LOUTVOL L" }, + { "LOUTVOL", NULL, "LOUTVOL R" }, + + { "DAC 1", NULL, "DAC L1" }, + { "DAC 1", NULL, "DAC R1" }, + + { "LOUT MIX", "DAC Switch", "DAC 1" }, + { "LOUT MIX", "OUTMIX Switch", "LOUTVOL" }, + + { "LOUT amp", NULL, "LOUT MIX" }, + { "LOUT amp", NULL, "VREF HP" }, + { "LOUTL", NULL, "LOUT amp" }, + { "LOUTR", NULL, "LOUT amp" }, + + { "SPK amp", NULL, "SPO MIX" }, + { "SPO", NULL, "SPK amp" }, +}; + +static int rt5660_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 rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec); + unsigned int val_len = 0, val_clk, mask_clk; + int pre_div, bclk_ms, frame_size; + + rt5660->lrck[dai->id] = params_rate(params); + pre_div = rl6231_get_clk_info(rt5660->sysclk, rt5660->lrck[dai->id]); + if (pre_div < 0) { + dev_err(codec->dev, "Unsupported clock setting %d for DAI %d\n", + rt5660->lrck[dai->id], dai->id); + return -EINVAL; + } + + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size); + return frame_size; + } + + if (frame_size > 32) + bclk_ms = 1; + else + bclk_ms = 0; + + rt5660->bclk[dai->id] = rt5660->lrck[dai->id] * (32 << bclk_ms); + + dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n", + rt5660->bclk[dai->id], rt5660->lrck[dai->id]); + dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n", + bclk_ms, pre_div, dai->id); + + switch (params_width(params)) { + case 16: + break; + case 20: + val_len |= RT5660_I2S_DL_20; + break; + case 24: + val_len |= RT5660_I2S_DL_24; + break; + case 8: + val_len |= RT5660_I2S_DL_8; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5660_AIF1: + mask_clk = RT5660_I2S_BCLK_MS1_MASK | RT5660_I2S_PD1_MASK; + val_clk = bclk_ms << RT5660_I2S_BCLK_MS1_SFT | + pre_div << RT5660_I2S_PD1_SFT; + snd_soc_update_bits(codec, RT5660_I2S1_SDP, RT5660_I2S_DL_MASK, + val_len); + snd_soc_update_bits(codec, RT5660_ADDA_CLK1, mask_clk, val_clk); + break; + + default: + dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + + return 0; +} + +static int rt5660_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec); + unsigned int reg_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rt5660->master[dai->id] = 1; + break; + + case SND_SOC_DAIFMT_CBS_CFS: + reg_val |= RT5660_I2S_MS_S; + rt5660->master[dai->id] = 0; + break; + + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT5660_I2S_BP_INV; + break; + + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT5660_I2S_DF_LEFT; + break; + + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5660_I2S_DF_PCM_A; + break; + + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5660_I2S_DF_PCM_B; + break; + + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5660_AIF1: + snd_soc_update_bits(codec, RT5660_I2S1_SDP, + RT5660_I2S_MS_MASK | RT5660_I2S_BP_MASK | + RT5660_I2S_DF_MASK, reg_val); + break; + + default: + dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + + return 0; +} + +static int rt5660_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec); + unsigned int reg_val = 0; + + if (freq == rt5660->sysclk && clk_id == rt5660->sysclk_src) + return 0; + + switch (clk_id) { + case RT5660_SCLK_S_MCLK: + reg_val |= RT5660_SCLK_SRC_MCLK; + break; + + case RT5660_SCLK_S_PLL1: + reg_val |= RT5660_SCLK_SRC_PLL1; + break; + + case RT5660_SCLK_S_RCCLK: + reg_val |= RT5660_SCLK_SRC_RCCLK; + break; + + default: + dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + + snd_soc_update_bits(codec, RT5660_GLB_CLK, RT5660_SCLK_SRC_MASK, + reg_val); + + rt5660->sysclk = freq; + rt5660->sysclk_src = clk_id; + + dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); + + return 0; +} + +static int rt5660_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec); + struct rl6231_pll_code pll_code; + int ret; + + if (source == rt5660->pll_src && freq_in == rt5660->pll_in && + freq_out == rt5660->pll_out) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(codec->dev, "PLL disabled\n"); + + rt5660->pll_in = 0; + rt5660->pll_out = 0; + snd_soc_update_bits(codec, RT5660_GLB_CLK, + RT5660_SCLK_SRC_MASK, RT5660_SCLK_SRC_MCLK); + return 0; + } + + switch (source) { + case RT5660_PLL1_S_MCLK: + snd_soc_update_bits(codec, RT5660_GLB_CLK, + RT5660_PLL1_SRC_MASK, RT5660_PLL1_SRC_MCLK); + break; + + case RT5660_PLL1_S_BCLK: + snd_soc_update_bits(codec, RT5660_GLB_CLK, + RT5660_PLL1_SRC_MASK, RT5660_PLL1_SRC_BCLK1); + break; + + default: + dev_err(codec->dev, "Unknown PLL source %d\n", source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(codec->dev, "Unsupport input clock %d\n", freq_in); + return ret; + } + + dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + snd_soc_write(codec, RT5660_PLL_CTRL1, + pll_code.n_code << RT5660_PLL_N_SFT | pll_code.k_code); + snd_soc_write(codec, RT5660_PLL_CTRL2, + (pll_code.m_bp ? 0 : pll_code.m_code) << RT5660_PLL_M_SFT | + pll_code.m_bp << RT5660_PLL_M_BP_SFT); + + rt5660->pll_in = freq_in; + rt5660->pll_out = freq_out; + rt5660->pll_src = source; + + return 0; +} + +static int rt5660_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + snd_soc_update_bits(codec, RT5660_GEN_CTRL1, + RT5660_DIG_GATE_CTRL, RT5660_DIG_GATE_CTRL); + + if (IS_ERR(rt5660->mclk)) + break; + + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) { + clk_disable_unprepare(rt5660->mclk); + } else { + ret = clk_prepare_enable(rt5660->mclk); + if (ret) + return ret; + } + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { + snd_soc_update_bits(codec, RT5660_PWR_ANLG1, + RT5660_PWR_VREF1 | RT5660_PWR_MB | + RT5660_PWR_BG | RT5660_PWR_VREF2, + RT5660_PWR_VREF1 | RT5660_PWR_MB | + RT5660_PWR_BG | RT5660_PWR_VREF2); + usleep_range(10000, 15000); + snd_soc_update_bits(codec, RT5660_PWR_ANLG1, + RT5660_PWR_FV1 | RT5660_PWR_FV2, + RT5660_PWR_FV1 | RT5660_PWR_FV2); + } + break; + + case SND_SOC_BIAS_OFF: + snd_soc_update_bits(codec, RT5660_GEN_CTRL1, + RT5660_DIG_GATE_CTRL, 0); + break; + + default: + break; + } + + return 0; +} + +static int rt5660_probe(struct snd_soc_codec *codec) +{ + struct rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec); + + rt5660->codec = codec; + + return 0; +} + +static int rt5660_remove(struct snd_soc_codec *codec) +{ + return snd_soc_write(codec, RT5660_RESET, 0); +} + +#ifdef CONFIG_PM +static int rt5660_suspend(struct snd_soc_codec *codec) +{ + struct rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec); + + regcache_cache_only(rt5660->regmap, true); + regcache_mark_dirty(rt5660->regmap); + + return 0; +} + +static int rt5660_resume(struct snd_soc_codec *codec) +{ + struct rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec); + + if (rt5660->pdata.poweroff_codec_in_suspend) + usleep_range(350000, 400000); + + regcache_cache_only(rt5660->regmap, false); + regcache_sync(rt5660->regmap); + + return 0; +} +#else +#define rt5660_suspend NULL +#define rt5660_resume NULL +#endif + +#define RT5660_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT5660_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt5660_aif_dai_ops = { + .hw_params = rt5660_hw_params, + .set_fmt = rt5660_set_dai_fmt, + .set_sysclk = rt5660_set_dai_sysclk, + .set_pll = rt5660_set_dai_pll, +}; + +static struct snd_soc_dai_driver rt5660_dai[] = { + { + .name = "rt5660-aif1", + .id = RT5660_AIF1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5660_STEREO_RATES, + .formats = RT5660_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5660_STEREO_RATES, + .formats = RT5660_FORMATS, + }, + .ops = &rt5660_aif_dai_ops, + }, +}; + +static struct snd_soc_codec_driver soc_codec_dev_rt5660 = { + .probe = rt5660_probe, + .remove = rt5660_remove, + .suspend = rt5660_suspend, + .resume = rt5660_resume, + .set_bias_level = rt5660_set_bias_level, + .idle_bias_off = true, + .component_driver = { + .controls = rt5660_snd_controls, + .num_controls = ARRAY_SIZE(rt5660_snd_controls), + .dapm_widgets = rt5660_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5660_dapm_widgets), + .dapm_routes = rt5660_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5660_dapm_routes), + }, +}; + +static const struct regmap_config rt5660_regmap = { + .reg_bits = 8, + .val_bits = 16, + .use_single_rw = true, + + .max_register = RT5660_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5660_ranges) * + RT5660_PR_SPACING), + .volatile_reg = rt5660_volatile_register, + .readable_reg = rt5660_readable_register, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt5660_reg, + .num_reg_defaults = ARRAY_SIZE(rt5660_reg), + .ranges = rt5660_ranges, + .num_ranges = ARRAY_SIZE(rt5660_ranges), +}; + +static const struct i2c_device_id rt5660_i2c_id[] = { + { "rt5660", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt5660_i2c_id); + +static const struct of_device_id rt5660_of_match[] = { + { .compatible = "realtek,rt5660", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rt5660_of_match); + +static const struct acpi_device_id rt5660_acpi_match[] = { + { "10EC5660", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, rt5660_acpi_match); + +static int rt5660_parse_dt(struct rt5660_priv *rt5660, struct device *dev) +{ + rt5660->pdata.in1_diff = device_property_read_bool(dev, + "realtek,in1-differential"); + rt5660->pdata.in3_diff = device_property_read_bool(dev, + "realtek,in3-differential"); + rt5660->pdata.poweroff_codec_in_suspend = device_property_read_bool(dev, + "realtek,poweroff-in-suspend"); + device_property_read_u32(dev, "realtek,dmic1-data-pin", + &rt5660->pdata.dmic1_data_pin); + + return 0; +} + +static int rt5660_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct rt5660_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct rt5660_priv *rt5660; + int ret; + unsigned int val; + + rt5660 = devm_kzalloc(&i2c->dev, sizeof(struct rt5660_priv), + GFP_KERNEL); + + if (rt5660 == NULL) + return -ENOMEM; + + /* Check if MCLK provided */ + rt5660->mclk = devm_clk_get(&i2c->dev, "mclk"); + if (PTR_ERR(rt5660->mclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + i2c_set_clientdata(i2c, rt5660); + + if (pdata) + rt5660->pdata = *pdata; + else if (i2c->dev.of_node) + rt5660_parse_dt(rt5660, &i2c->dev); + + rt5660->regmap = devm_regmap_init_i2c(i2c, &rt5660_regmap); + if (IS_ERR(rt5660->regmap)) { + ret = PTR_ERR(rt5660->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + regmap_read(rt5660->regmap, RT5660_VENDOR_ID2, &val); + if (val != RT5660_DEVICE_ID) { + dev_err(&i2c->dev, + "Device with ID register %#x is not rt5660\n", val); + return -ENODEV; + } + + regmap_write(rt5660->regmap, RT5660_RESET, 0); + + ret = regmap_register_patch(rt5660->regmap, rt5660_patch, + ARRAY_SIZE(rt5660_patch)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); + + regmap_update_bits(rt5660->regmap, RT5660_GEN_CTRL1, + RT5660_AUTO_DIS_AMP | RT5660_MCLK_DET | RT5660_POW_CLKDET, + RT5660_AUTO_DIS_AMP | RT5660_MCLK_DET | RT5660_POW_CLKDET); + + if (rt5660->pdata.dmic1_data_pin) { + regmap_update_bits(rt5660->regmap, RT5660_GPIO_CTRL1, + RT5660_GP1_PIN_MASK, RT5660_GP1_PIN_DMIC1_SCL); + + if (rt5660->pdata.dmic1_data_pin == RT5660_DMIC1_DATA_GPIO2) + regmap_update_bits(rt5660->regmap, RT5660_DMIC_CTRL1, + RT5660_SEL_DMIC_DATA_MASK, + RT5660_SEL_DMIC_DATA_GPIO2); + else if (rt5660->pdata.dmic1_data_pin == RT5660_DMIC1_DATA_IN1P) + regmap_update_bits(rt5660->regmap, RT5660_DMIC_CTRL1, + RT5660_SEL_DMIC_DATA_MASK, + RT5660_SEL_DMIC_DATA_IN1P); + } + + return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5660, + rt5660_dai, ARRAY_SIZE(rt5660_dai)); +} + +static int rt5660_i2c_remove(struct i2c_client *i2c) +{ + snd_soc_unregister_codec(&i2c->dev); + + return 0; +} + +static struct i2c_driver rt5660_i2c_driver = { + .driver = { + .name = "rt5660", + .acpi_match_table = ACPI_PTR(rt5660_acpi_match), + .of_match_table = of_match_ptr(rt5660_of_match), + }, + .probe = rt5660_i2c_probe, + .remove = rt5660_i2c_remove, + .id_table = rt5660_i2c_id, +}; +module_i2c_driver(rt5660_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5660 driver"); +MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5660.h b/sound/soc/codecs/rt5660.h new file mode 100644 index 000000000000..bba18fb66b6f --- /dev/null +++ b/sound/soc/codecs/rt5660.h @@ -0,0 +1,850 @@ +/* + * rt5660.h -- RT5660 ALSA SoC audio driver + * + * Copyright 2016 Realtek Semiconductor Corp. + * Author: Oder Chiou <oder_chiou@realtek.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 _RT5660_H +#define _RT5660_H + +#include <linux/clk.h> +#include <sound/rt5660.h> + +/* Info */ +#define RT5660_RESET 0x00 +#define RT5660_VENDOR_ID 0xfd +#define RT5660_VENDOR_ID1 0xfe +#define RT5660_VENDOR_ID2 0xff +/* I/O - Output */ +#define RT5660_SPK_VOL 0x01 +#define RT5660_LOUT_VOL 0x02 +/* I/O - Input */ +#define RT5660_IN1_IN2 0x0d +#define RT5660_IN3_IN4 0x0e +/* I/O - ADC/DAC/DMIC */ +#define RT5660_DAC1_DIG_VOL 0x19 +#define RT5660_STO1_ADC_DIG_VOL 0x1c +#define RT5660_ADC_BST_VOL1 0x1e +/* Mixer - D-D */ +#define RT5660_STO1_ADC_MIXER 0x27 +#define RT5660_AD_DA_MIXER 0x29 +#define RT5660_STO_DAC_MIXER 0x2a +#define RT5660_DIG_INF1_DATA 0x2f +/* Mixer - ADC */ +#define RT5660_REC_L1_MIXER 0x3b +#define RT5660_REC_L2_MIXER 0x3c +#define RT5660_REC_R1_MIXER 0x3d +#define RT5660_REC_R2_MIXER 0x3e +/* Mixer - DAC */ +#define RT5660_LOUT_MIXER 0x45 +#define RT5660_SPK_MIXER 0x46 +#define RT5660_SPO_MIXER 0x48 +#define RT5660_SPO_CLSD_RATIO 0x4a +#define RT5660_OUT_L_GAIN1 0x4d +#define RT5660_OUT_L_GAIN2 0x4e +#define RT5660_OUT_L1_MIXER 0x4f +#define RT5660_OUT_R_GAIN1 0x50 +#define RT5660_OUT_R_GAIN2 0x51 +#define RT5660_OUT_R1_MIXER 0x52 +/* Power */ +#define RT5660_PWR_DIG1 0x61 +#define RT5660_PWR_DIG2 0x62 +#define RT5660_PWR_ANLG1 0x63 +#define RT5660_PWR_ANLG2 0x64 +#define RT5660_PWR_MIXER 0x65 +#define RT5660_PWR_VOL 0x66 +/* Private Register Control */ +#define RT5660_PRIV_INDEX 0x6a +#define RT5660_PRIV_DATA 0x6c +/* Format - ADC/DAC */ +#define RT5660_I2S1_SDP 0x70 +#define RT5660_ADDA_CLK1 0x73 +#define RT5660_ADDA_CLK2 0x74 +#define RT5660_DMIC_CTRL1 0x75 +/* Function - Analog */ +#define RT5660_GLB_CLK 0x80 +#define RT5660_PLL_CTRL1 0x81 +#define RT5660_PLL_CTRL2 0x82 +#define RT5660_CLSD_AMP_OC_CTRL 0x8c +#define RT5660_CLSD_AMP_CTRL 0x8d +#define RT5660_LOUT_AMP_CTRL 0x8e +#define RT5660_SPK_AMP_SPKVDD 0x92 +#define RT5660_MICBIAS 0x93 +#define RT5660_CLSD_OUT_CTRL1 0xa1 +#define RT5660_CLSD_OUT_CTRL2 0xa2 +#define RT5660_DIPOLE_MIC_CTRL1 0xa3 +#define RT5660_DIPOLE_MIC_CTRL2 0xa4 +#define RT5660_DIPOLE_MIC_CTRL3 0xa5 +#define RT5660_DIPOLE_MIC_CTRL4 0xa6 +#define RT5660_DIPOLE_MIC_CTRL5 0xa7 +#define RT5660_DIPOLE_MIC_CTRL6 0xa8 +#define RT5660_DIPOLE_MIC_CTRL7 0xa9 +#define RT5660_DIPOLE_MIC_CTRL8 0xaa +#define RT5660_DIPOLE_MIC_CTRL9 0xab +#define RT5660_DIPOLE_MIC_CTRL10 0xac +#define RT5660_DIPOLE_MIC_CTRL11 0xad +#define RT5660_DIPOLE_MIC_CTRL12 0xae +/* Function - Digital */ +#define RT5660_EQ_CTRL1 0xb0 +#define RT5660_EQ_CTRL2 0xb1 +#define RT5660_DRC_AGC_CTRL1 0xb3 +#define RT5660_DRC_AGC_CTRL2 0xb4 +#define RT5660_DRC_AGC_CTRL3 0xb5 +#define RT5660_DRC_AGC_CTRL4 0xb6 +#define RT5660_DRC_AGC_CTRL5 0xb7 +#define RT5660_JD_CTRL 0xbb +#define RT5660_IRQ_CTRL1 0xbd +#define RT5660_IRQ_CTRL2 0xbe +#define RT5660_INT_IRQ_ST 0xbf +#define RT5660_GPIO_CTRL1 0xc0 +#define RT5660_GPIO_CTRL2 0xc2 +#define RT5660_WIND_FILTER_CTRL1 0xd3 +#define RT5660_SV_ZCD1 0xd9 +#define RT5660_SV_ZCD2 0xda +#define RT5660_DRC1_LM_CTRL1 0xe0 +#define RT5660_DRC1_LM_CTRL2 0xe1 +#define RT5660_DRC2_LM_CTRL1 0xe2 +#define RT5660_DRC2_LM_CTRL2 0xe3 +#define RT5660_MULTI_DRC_CTRL 0xe4 +#define RT5660_DRC2_CTRL1 0xe5 +#define RT5660_DRC2_CTRL2 0xe6 +#define RT5660_DRC2_CTRL3 0xe7 +#define RT5660_DRC2_CTRL4 0xe8 +#define RT5660_DRC2_CTRL5 0xe9 +#define RT5660_ALC_PGA_CTRL1 0xea +#define RT5660_ALC_PGA_CTRL2 0xeb +#define RT5660_ALC_PGA_CTRL3 0xec +#define RT5660_ALC_PGA_CTRL4 0xed +#define RT5660_ALC_PGA_CTRL5 0xee +#define RT5660_ALC_PGA_CTRL6 0xef +#define RT5660_ALC_PGA_CTRL7 0xf0 + +/* General Control */ +#define RT5660_GEN_CTRL1 0xfa +#define RT5660_GEN_CTRL2 0xfb +#define RT5660_GEN_CTRL3 0xfc + +/* Index of Codec Private Register definition */ +#define RT5660_CHOP_DAC_ADC 0x3d + +/* Global Definition */ +#define RT5660_L_MUTE (0x1 << 15) +#define RT5660_L_MUTE_SFT 15 +#define RT5660_VOL_L_MUTE (0x1 << 14) +#define RT5660_VOL_L_SFT 14 +#define RT5660_R_MUTE (0x1 << 7) +#define RT5660_R_MUTE_SFT 7 +#define RT5660_VOL_R_MUTE (0x1 << 6) +#define RT5660_VOL_R_SFT 6 +#define RT5660_L_VOL_MASK (0x3f << 8) +#define RT5660_L_VOL_SFT 8 +#define RT5660_R_VOL_MASK (0x3f) +#define RT5660_R_VOL_SFT 0 + +/* IN1 and IN2 Control (0x0d) */ +#define RT5660_IN_DF1 (0x1 << 15) +#define RT5660_IN_SFT1 15 +#define RT5660_BST_MASK1 (0x7f << 8) +#define RT5660_BST_SFT1 8 +#define RT5660_IN_DF2 (0x1 << 7) +#define RT5660_IN_SFT2 7 +#define RT5660_BST_MASK2 (0x7f << 0) +#define RT5660_BST_SFT2 0 + +/* IN3 and IN4 Control (0x0e) */ +#define RT5660_IN_DF3 (0x1 << 15) +#define RT5660_IN_SFT3 15 +#define RT5660_BST_MASK3 (0x7f << 8) +#define RT5660_BST_SFT3 8 +#define RT5660_IN_DF4 (0x1 << 7) +#define RT5660_IN_SFT4 7 +#define RT5660_BST_MASK4 (0x7f << 0) +#define RT5660_BST_SFT4 0 + +/* DAC1 Digital Volume (0x19) */ +#define RT5660_DAC_L1_VOL_MASK (0x7f << 9) +#define RT5660_DAC_L1_VOL_SFT 9 +#define RT5660_DAC_R1_VOL_MASK (0x7f << 1) +#define RT5660_DAC_R1_VOL_SFT 1 + +/* ADC Digital Volume Control (0x1c) */ +#define RT5660_ADC_L_VOL_MASK (0x3f << 9) +#define RT5660_ADC_L_VOL_SFT 9 +#define RT5660_ADC_R_VOL_MASK (0x3f << 1) +#define RT5660_ADC_R_VOL_SFT 1 + +/* ADC Boost Volume Control (0x1e) */ +#define RT5660_STO1_ADC_L_BST_MASK (0x3 << 14) +#define RT5660_STO1_ADC_L_BST_SFT 14 +#define RT5660_STO1_ADC_R_BST_MASK (0x3 << 12) +#define RT5660_STO1_ADC_R_BST_SFT 12 + +/* Stereo ADC Mixer Control (0x27) */ +#define RT5660_M_ADC_L1 (0x1 << 14) +#define RT5660_M_ADC_L1_SFT 14 +#define RT5660_M_ADC_L2 (0x1 << 13) +#define RT5660_M_ADC_L2_SFT 13 +#define RT5660_M_ADC_R1 (0x1 << 6) +#define RT5660_M_ADC_R1_SFT 6 +#define RT5660_M_ADC_R2 (0x1 << 5) +#define RT5660_M_ADC_R2_SFT 5 + +/* ADC Mixer to DAC Mixer Control (0x29) */ +#define RT5660_M_ADCMIX_L (0x1 << 15) +#define RT5660_M_ADCMIX_L_SFT 15 +#define RT5660_M_DAC1_L (0x1 << 14) +#define RT5660_M_DAC1_L_SFT 14 +#define RT5660_M_ADCMIX_R (0x1 << 7) +#define RT5660_M_ADCMIX_R_SFT 7 +#define RT5660_M_DAC1_R (0x1 << 6) +#define RT5660_M_DAC1_R_SFT 6 + +/* Stereo DAC Mixer Control (0x2a) */ +#define RT5660_M_DAC_L1 (0x1 << 14) +#define RT5660_M_DAC_L1_SFT 14 +#define RT5660_DAC_L1_STO_L_VOL_MASK (0x1 << 13) +#define RT5660_DAC_L1_STO_L_VOL_SFT 13 +#define RT5660_M_DAC_R1_STO_L (0x1 << 9) +#define RT5660_M_DAC_R1_STO_L_SFT 9 +#define RT5660_DAC_R1_STO_L_VOL_MASK (0x1 << 8) +#define RT5660_DAC_R1_STO_L_VOL_SFT 8 +#define RT5660_M_DAC_R1 (0x1 << 6) +#define RT5660_M_DAC_R1_SFT 6 +#define RT5660_DAC_R1_STO_R_VOL_MASK (0x1 << 5) +#define RT5660_DAC_R1_STO_R_VOL_SFT 5 +#define RT5660_M_DAC_L1_STO_R (0x1 << 1) +#define RT5660_M_DAC_L1_STO_R_SFT 1 +#define RT5660_DAC_L1_STO_R_VOL_MASK (0x1) +#define RT5660_DAC_L1_STO_R_VOL_SFT 0 + +/* Digital Interface Data Control (0x2f) */ +#define RT5660_IF1_DAC_IN_SEL (0x3 << 14) +#define RT5660_IF1_DAC_IN_SFT 14 +#define RT5660_IF1_ADC_IN_SEL (0x3 << 12) +#define RT5660_IF1_ADC_IN_SFT 12 + +/* REC Left Mixer Control 1 (0x3b) */ +#define RT5660_G_BST3_RM_L_MASK (0x7 << 4) +#define RT5660_G_BST3_RM_L_SFT 4 +#define RT5660_G_BST2_RM_L_MASK (0x7 << 1) +#define RT5660_G_BST2_RM_L_SFT 1 + +/* REC Left Mixer Control 2 (0x3c) */ +#define RT5660_G_BST1_RM_L_MASK (0x7 << 13) +#define RT5660_G_BST1_RM_L_SFT 13 +#define RT5660_G_OM_L_RM_L_MASK (0x7 << 10) +#define RT5660_G_OM_L_RM_L_SFT 10 +#define RT5660_M_BST3_RM_L (0x1 << 3) +#define RT5660_M_BST3_RM_L_SFT 3 +#define RT5660_M_BST2_RM_L (0x1 << 2) +#define RT5660_M_BST2_RM_L_SFT 2 +#define RT5660_M_BST1_RM_L (0x1 << 1) +#define RT5660_M_BST1_RM_L_SFT 1 +#define RT5660_M_OM_L_RM_L (0x1) +#define RT5660_M_OM_L_RM_L_SFT 0 + +/* REC Right Mixer Control 1 (0x3d) */ +#define RT5660_G_BST3_RM_R_MASK (0x7 << 4) +#define RT5660_G_BST3_RM_R_SFT 4 +#define RT5660_G_BST2_RM_R_MASK (0x7 << 1) +#define RT5660_G_BST2_RM_R_SFT 1 + +/* REC Right Mixer Control 2 (0x3e) */ +#define RT5660_G_BST1_RM_R_MASK (0x7 << 13) +#define RT5660_G_BST1_RM_R_SFT 13 +#define RT5660_G_OM_R_RM_R_MASK (0x7 << 10) +#define RT5660_G_OM_R_RM_R_SFT 10 +#define RT5660_M_BST3_RM_R (0x1 << 3) +#define RT5660_M_BST3_RM_R_SFT 3 +#define RT5660_M_BST2_RM_R (0x1 << 2) +#define RT5660_M_BST2_RM_R_SFT 2 +#define RT5660_M_BST1_RM_R (0x1 << 1) +#define RT5660_M_BST1_RM_R_SFT 1 +#define RT5660_M_OM_R_RM_R (0x1) +#define RT5660_M_OM_R_RM_R_SFT 0 + +/* LOUTMIX Control (0x45) */ +#define RT5660_M_DAC1_LM (0x1 << 14) +#define RT5660_M_DAC1_LM_SFT 14 +#define RT5660_M_LOVOL_M (0x1 << 13) +#define RT5660_M_LOVOL_LM_SFT 13 + +/* SPK Mixer Control (0x46) */ +#define RT5660_G_BST3_SM_MASK (0x3 << 14) +#define RT5660_G_BST3_SM_SFT 14 +#define RT5660_G_BST1_SM_MASK (0x3 << 12) +#define RT5660_G_BST1_SM_SFT 12 +#define RT5660_G_DACl_SM_MASK (0x3 << 10) +#define RT5660_G_DACl_SM_SFT 10 +#define RT5660_G_DACR_SM_MASK (0x3 << 8) +#define RT5660_G_DACR_SM_SFT 8 +#define RT5660_G_OM_L_SM_MASK (0x3 << 6) +#define RT5660_G_OM_L_SM_SFT 6 +#define RT5660_M_DACR_SM (0x1 << 5) +#define RT5660_M_DACR_SM_SFT 5 +#define RT5660_M_BST1_SM (0x1 << 4) +#define RT5660_M_BST1_SM_SFT 4 +#define RT5660_M_BST3_SM (0x1 << 3) +#define RT5660_M_BST3_SM_SFT 3 +#define RT5660_M_DACL_SM (0x1 << 2) +#define RT5660_M_DACL_SM_SFT 2 +#define RT5660_M_OM_L_SM (0x1 << 1) +#define RT5660_M_OM_L_SM_SFT 1 + +/* SPOMIX Control (0x48) */ +#define RT5660_M_DAC_R_SPM (0x1 << 14) +#define RT5660_M_DAC_R_SPM_SFT 14 +#define RT5660_M_DAC_L_SPM (0x1 << 13) +#define RT5660_M_DAC_L_SPM_SFT 13 +#define RT5660_M_SV_SPM (0x1 << 12) +#define RT5660_M_SV_SPM_SFT 12 +#define RT5660_M_BST1_SPM (0x1 << 11) +#define RT5660_M_BST1_SPM_SFT 11 + +/* Output Left Mixer Control 1 (0x4d) */ +#define RT5660_G_BST3_OM_L_MASK (0x7 << 13) +#define RT5660_G_BST3_OM_L_SFT 13 +#define RT5660_G_BST2_OM_L_MASK (0x7 << 10) +#define RT5660_G_BST2_OM_L_SFT 10 +#define RT5660_G_BST1_OM_L_MASK (0x7 << 7) +#define RT5660_G_BST1_OM_L_SFT 7 +#define RT5660_G_RM_L_OM_L_MASK (0x7 << 1) +#define RT5660_G_RM_L_OM_L_SFT 1 + +/* Output Left Mixer Control 2 (0x4e) */ +#define RT5660_G_DAC_R1_OM_L_MASK (0x7 << 10) +#define RT5660_G_DAC_R1_OM_L_SFT 10 +#define RT5660_G_DAC_L1_OM_L_MASK (0x7 << 7) +#define RT5660_G_DAC_L1_OM_L_SFT 7 + +/* Output Left Mixer Control 3 (0x4f) */ +#define RT5660_M_BST3_OM_L (0x1 << 5) +#define RT5660_M_BST3_OM_L_SFT 5 +#define RT5660_M_BST2_OM_L (0x1 << 4) +#define RT5660_M_BST2_OM_L_SFT 4 +#define RT5660_M_BST1_OM_L (0x1 << 3) +#define RT5660_M_BST1_OM_L_SFT 3 +#define RT5660_M_RM_L_OM_L (0x1 << 2) +#define RT5660_M_RM_L_OM_L_SFT 2 +#define RT5660_M_DAC_R_OM_L (0x1 << 1) +#define RT5660_M_DAC_R_OM_L_SFT 1 +#define RT5660_M_DAC_L_OM_L (0x1) +#define RT5660_M_DAC_L_OM_L_SFT 0 + +/* Output Right Mixer Control 1 (0x50) */ +#define RT5660_G_BST2_OM_R_MASK (0x7 << 10) +#define RT5660_G_BST2_OM_R_SFT 10 +#define RT5660_G_BST1_OM_R_MASK (0x7 << 7) +#define RT5660_G_BST1_OM_R_SFT 7 +#define RT5660_G_RM_R_OM_R_MASK (0x7 << 1) +#define RT5660_G_RM_R_OM_R_SFT 1 + +/* Output Right Mixer Control 2 (0x51) */ +#define RT5660_G_DAC_L_OM_R_MASK (0x7 << 10) +#define RT5660_G_DAC_L_OM_R_SFT 10 +#define RT5660_G_DAC_R_OM_R_MASK (0x7 << 7) +#define RT5660_G_DAC_R_OM_R_SFT 7 + +/* Output Right Mixer Control 3 (0x52) */ +#define RT5660_M_BST2_OM_R (0x1 << 4) +#define RT5660_M_BST2_OM_R_SFT 4 +#define RT5660_M_BST1_OM_R (0x1 << 3) +#define RT5660_M_BST1_OM_R_SFT 3 +#define RT5660_M_RM_R_OM_R (0x1 << 2) +#define RT5660_M_RM_R_OM_R_SFT 2 +#define RT5660_M_DAC_L_OM_R (0x1 << 1) +#define RT5660_M_DAC_L_OM_R_SFT 1 +#define RT5660_M_DAC_R_OM_R (0x1) +#define RT5660_M_DAC_R_OM_R_SFT 0 + +/* Power Management for Digital 1 (0x61) */ +#define RT5660_PWR_I2S1 (0x1 << 15) +#define RT5660_PWR_I2S1_BIT 15 +#define RT5660_PWR_DAC_L1 (0x1 << 12) +#define RT5660_PWR_DAC_L1_BIT 12 +#define RT5660_PWR_DAC_R1 (0x1 << 11) +#define RT5660_PWR_DAC_R1_BIT 11 +#define RT5660_PWR_ADC_L (0x1 << 2) +#define RT5660_PWR_ADC_L_BIT 2 +#define RT5660_PWR_ADC_R (0x1 << 1) +#define RT5660_PWR_ADC_R_BIT 1 +#define RT5660_PWR_CLS_D (0x1) +#define RT5660_PWR_CLS_D_BIT 0 + +/* Power Management for Digital 2 (0x62) */ +#define RT5660_PWR_ADC_S1F (0x1 << 15) +#define RT5660_PWR_ADC_S1F_BIT 15 +#define RT5660_PWR_DAC_S1F (0x1 << 11) +#define RT5660_PWR_DAC_S1F_BIT 11 + +/* Power Management for Analog 1 (0x63) */ +#define RT5660_PWR_VREF1 (0x1 << 15) +#define RT5660_PWR_VREF1_BIT 15 +#define RT5660_PWR_FV1 (0x1 << 14) +#define RT5660_PWR_FV1_BIT 14 +#define RT5660_PWR_MB (0x1 << 13) +#define RT5660_PWR_MB_BIT 13 +#define RT5660_PWR_BG (0x1 << 11) +#define RT5660_PWR_BG_BIT 11 +#define RT5660_PWR_HP_L (0x1 << 7) +#define RT5660_PWR_HP_L_BIT 7 +#define RT5660_PWR_HP_R (0x1 << 6) +#define RT5660_PWR_HP_R_BIT 6 +#define RT5660_PWR_HA (0x1 << 5) +#define RT5660_PWR_HA_BIT 5 +#define RT5660_PWR_VREF2 (0x1 << 4) +#define RT5660_PWR_VREF2_BIT 4 +#define RT5660_PWR_FV2 (0x1 << 3) +#define RT5660_PWR_FV2_BIT 3 +#define RT5660_PWR_LDO2 (0x1 << 2) +#define RT5660_PWR_LDO2_BIT 2 + +/* Power Management for Analog 2 (0x64) */ +#define RT5660_PWR_BST1 (0x1 << 15) +#define RT5660_PWR_BST1_BIT 15 +#define RT5660_PWR_BST2 (0x1 << 14) +#define RT5660_PWR_BST2_BIT 14 +#define RT5660_PWR_BST3 (0x1 << 13) +#define RT5660_PWR_BST3_BIT 13 +#define RT5660_PWR_MB1 (0x1 << 11) +#define RT5660_PWR_MB1_BIT 11 +#define RT5660_PWR_MB2 (0x1 << 10) +#define RT5660_PWR_MB2_BIT 10 +#define RT5660_PWR_PLL (0x1 << 9) +#define RT5660_PWR_PLL_BIT 9 + +/* Power Management for Mixer (0x65) */ +#define RT5660_PWR_OM_L (0x1 << 15) +#define RT5660_PWR_OM_L_BIT 15 +#define RT5660_PWR_OM_R (0x1 << 14) +#define RT5660_PWR_OM_R_BIT 14 +#define RT5660_PWR_SM (0x1 << 13) +#define RT5660_PWR_SM_BIT 13 +#define RT5660_PWR_RM_L (0x1 << 11) +#define RT5660_PWR_RM_L_BIT 11 +#define RT5660_PWR_RM_R (0x1 << 10) +#define RT5660_PWR_RM_R_BIT 10 + +/* Power Management for Volume (0x66) */ +#define RT5660_PWR_SV (0x1 << 15) +#define RT5660_PWR_SV_BIT 15 +#define RT5660_PWR_LV_L (0x1 << 11) +#define RT5660_PWR_LV_L_BIT 11 +#define RT5660_PWR_LV_R (0x1 << 10) +#define RT5660_PWR_LV_R_BIT 10 + +/* I2S1 Audio Serial Data Port Control (0x70) */ +#define RT5660_I2S_MS_MASK (0x1 << 15) +#define RT5660_I2S_MS_SFT 15 +#define RT5660_I2S_MS_M (0x0 << 15) +#define RT5660_I2S_MS_S (0x1 << 15) +#define RT5660_I2S_O_CP_MASK (0x3 << 10) +#define RT5660_I2S_O_CP_SFT 10 +#define RT5660_I2S_O_CP_OFF (0x0 << 10) +#define RT5660_I2S_O_CP_U_LAW (0x1 << 10) +#define RT5660_I2S_O_CP_A_LAW (0x2 << 10) +#define RT5660_I2S_I_CP_MASK (0x3 << 8) +#define RT5660_I2S_I_CP_SFT 8 +#define RT5660_I2S_I_CP_OFF (0x0 << 8) +#define RT5660_I2S_I_CP_U_LAW (0x1 << 8) +#define RT5660_I2S_I_CP_A_LAW (0x2 << 8) +#define RT5660_I2S_BP_MASK (0x1 << 7) +#define RT5660_I2S_BP_SFT 7 +#define RT5660_I2S_BP_NOR (0x0 << 7) +#define RT5660_I2S_BP_INV (0x1 << 7) +#define RT5660_I2S_DL_MASK (0x3 << 2) +#define RT5660_I2S_DL_SFT 2 +#define RT5660_I2S_DL_16 (0x0 << 2) +#define RT5660_I2S_DL_20 (0x1 << 2) +#define RT5660_I2S_DL_24 (0x2 << 2) +#define RT5660_I2S_DL_8 (0x3 << 2) +#define RT5660_I2S_DF_MASK (0x3) +#define RT5660_I2S_DF_SFT 0 +#define RT5660_I2S_DF_I2S (0x0) +#define RT5660_I2S_DF_LEFT (0x1) +#define RT5660_I2S_DF_PCM_A (0x2) +#define RT5660_I2S_DF_PCM_B (0x3) + +/* ADC/DAC Clock Control 1 (0x73) */ +#define RT5660_I2S_BCLK_MS1_MASK (0x1 << 15) +#define RT5660_I2S_BCLK_MS1_SFT 15 +#define RT5660_I2S_BCLK_MS1_32 (0x0 << 15) +#define RT5660_I2S_BCLK_MS1_64 (0x1 << 15) +#define RT5660_I2S_PD1_MASK (0x7 << 12) +#define RT5660_I2S_PD1_SFT 12 +#define RT5660_I2S_PD1_1 (0x0 << 12) +#define RT5660_I2S_PD1_2 (0x1 << 12) +#define RT5660_I2S_PD1_3 (0x2 << 12) +#define RT5660_I2S_PD1_4 (0x3 << 12) +#define RT5660_I2S_PD1_6 (0x4 << 12) +#define RT5660_I2S_PD1_8 (0x5 << 12) +#define RT5660_I2S_PD1_12 (0x6 << 12) +#define RT5660_I2S_PD1_16 (0x7 << 12) +#define RT5660_DAC_OSR_MASK (0x3 << 2) +#define RT5660_DAC_OSR_SFT 2 +#define RT5660_DAC_OSR_128 (0x0 << 2) +#define RT5660_DAC_OSR_64 (0x1 << 2) +#define RT5660_DAC_OSR_32 (0x2 << 2) +#define RT5660_DAC_OSR_16 (0x3 << 2) +#define RT5660_ADC_OSR_MASK (0x3) +#define RT5660_ADC_OSR_SFT 0 +#define RT5660_ADC_OSR_128 (0x0) +#define RT5660_ADC_OSR_64 (0x1) +#define RT5660_ADC_OSR_32 (0x2) +#define RT5660_ADC_OSR_16 (0x3) + +/* ADC/DAC Clock Control 2 (0x74) */ +#define RT5660_RESET_ADF (0x1 << 13) +#define RT5660_RESET_ADF_SFT 13 +#define RT5660_RESET_DAF (0x1 << 12) +#define RT5660_RESET_DAF_SFT 12 +#define RT5660_DAHPF_EN (0x1 << 11) +#define RT5660_DAHPF_EN_SFT 11 +#define RT5660_ADHPF_EN (0x1 << 10) +#define RT5660_ADHPF_EN_SFT 10 + +/* Digital Microphone Control (0x75) */ +#define RT5660_DMIC_1_EN_MASK (0x1 << 15) +#define RT5660_DMIC_1_EN_SFT 15 +#define RT5660_DMIC_1_DIS (0x0 << 15) +#define RT5660_DMIC_1_EN (0x1 << 15) +#define RT5660_DMIC_1L_LH_MASK (0x1 << 13) +#define RT5660_DMIC_1L_LH_SFT 13 +#define RT5660_DMIC_1L_LH_RISING (0x0 << 13) +#define RT5660_DMIC_1L_LH_FALLING (0x1 << 13) +#define RT5660_DMIC_1R_LH_MASK (0x1 << 12) +#define RT5660_DMIC_1R_LH_SFT 12 +#define RT5660_DMIC_1R_LH_RISING (0x0 << 12) +#define RT5660_DMIC_1R_LH_FALLING (0x1 << 12) +#define RT5660_SEL_DMIC_DATA_MASK (0x1 << 11) +#define RT5660_SEL_DMIC_DATA_SFT 11 +#define RT5660_SEL_DMIC_DATA_GPIO2 (0x0 << 11) +#define RT5660_SEL_DMIC_DATA_IN1P (0x1 << 11) +#define RT5660_DMIC_CLK_MASK (0x7 << 5) +#define RT5660_DMIC_CLK_SFT 5 + +/* Global Clock Control (0x80) */ +#define RT5660_SCLK_SRC_MASK (0x3 << 14) +#define RT5660_SCLK_SRC_SFT 14 +#define RT5660_SCLK_SRC_MCLK (0x0 << 14) +#define RT5660_SCLK_SRC_PLL1 (0x1 << 14) +#define RT5660_SCLK_SRC_RCCLK (0x2 << 14) +#define RT5660_PLL1_SRC_MASK (0x3 << 12) +#define RT5660_PLL1_SRC_SFT 12 +#define RT5660_PLL1_SRC_MCLK (0x0 << 12) +#define RT5660_PLL1_SRC_BCLK1 (0x1 << 12) +#define RT5660_PLL1_SRC_RCCLK (0x2 << 12) +#define RT5660_PLL1_PD_MASK (0x1 << 3) +#define RT5660_PLL1_PD_SFT 3 +#define RT5660_PLL1_PD_1 (0x0 << 3) +#define RT5660_PLL1_PD_2 (0x1 << 3) + +#define RT5660_PLL_INP_MAX 40000000 +#define RT5660_PLL_INP_MIN 256000 +/* PLL M/N/K Code Control 1 (0x81) */ +#define RT5660_PLL_N_MAX 0x1ff +#define RT5660_PLL_N_MASK (RT5660_PLL_N_MAX << 7) +#define RT5660_PLL_N_SFT 7 +#define RT5660_PLL_K_MAX 0x1f +#define RT5660_PLL_K_MASK (RT5660_PLL_K_MAX) +#define RT5660_PLL_K_SFT 0 + +/* PLL M/N/K Code Control 2 (0x82) */ +#define RT5660_PLL_M_MAX 0xf +#define RT5660_PLL_M_MASK (RT5660_PLL_M_MAX << 12) +#define RT5660_PLL_M_SFT 12 +#define RT5660_PLL_M_BP (0x1 << 11) +#define RT5660_PLL_M_BP_SFT 11 + +/* Class D Over Current Control (0x8c) */ +#define RT5660_CLSD_OC_MASK (0x1 << 9) +#define RT5660_CLSD_OC_SFT 9 +#define RT5660_CLSD_OC_PU (0x0 << 9) +#define RT5660_CLSD_OC_PD (0x1 << 9) +#define RT5660_AUTO_PD_MASK (0x1 << 8) +#define RT5660_AUTO_PD_SFT 8 +#define RT5660_AUTO_PD_DIS (0x0 << 8) +#define RT5660_AUTO_PD_EN (0x1 << 8) +#define RT5660_CLSD_OC_TH_MASK (0x3f) +#define RT5660_CLSD_OC_TH_SFT 0 + +/* Class D Output Control (0x8d) */ +#define RT5660_CLSD_RATIO_MASK (0xf << 12) +#define RT5660_CLSD_RATIO_SFT 12 + +/* Lout Amp Control 1 (0x8e) */ +#define RT5660_LOUT_CO_MASK (0x1 << 4) +#define RT5660_LOUT_CO_SFT 4 +#define RT5660_LOUT_CO_DIS (0x0 << 4) +#define RT5660_LOUT_CO_EN (0x1 << 4) +#define RT5660_LOUT_CB_MASK (0x1) +#define RT5660_LOUT_CB_SFT 0 +#define RT5660_LOUT_CB_PD (0x0) +#define RT5660_LOUT_CB_PU (0x1) + +/* SPKVDD detection control (0x92) */ +#define RT5660_SPKVDD_DET_MASK (0x1 << 15) +#define RT5660_SPKVDD_DET_SFT 15 +#define RT5660_SPKVDD_DET_DIS (0x0 << 15) +#define RT5660_SPKVDD_DET_EN (0x1 << 15) +#define RT5660_SPK_AG_MASK (0x1 << 14) +#define RT5660_SPK_AG_SFT 14 +#define RT5660_SPK_AG_DIS (0x0 << 14) +#define RT5660_SPK_AG_EN (0x1 << 14) + +/* Micbias Control (0x93) */ +#define RT5660_MIC1_BS_MASK (0x1 << 15) +#define RT5660_MIC1_BS_SFT 15 +#define RT5660_MIC1_BS_9AV (0x0 << 15) +#define RT5660_MIC1_BS_75AV (0x1 << 15) +#define RT5660_MIC2_BS_MASK (0x1 << 14) +#define RT5660_MIC2_BS_SFT 14 +#define RT5660_MIC2_BS_9AV (0x0 << 14) +#define RT5660_MIC2_BS_75AV (0x1 << 14) +#define RT5660_MIC1_OVCD_MASK (0x1 << 11) +#define RT5660_MIC1_OVCD_SFT 11 +#define RT5660_MIC1_OVCD_DIS (0x0 << 11) +#define RT5660_MIC1_OVCD_EN (0x1 << 11) +#define RT5660_MIC1_OVTH_MASK (0x3 << 9) +#define RT5660_MIC1_OVTH_SFT 9 +#define RT5660_MIC1_OVTH_600UA (0x0 << 9) +#define RT5660_MIC1_OVTH_1500UA (0x1 << 9) +#define RT5660_MIC1_OVTH_2000UA (0x2 << 9) +#define RT5660_MIC2_OVCD_MASK (0x1 << 8) +#define RT5660_MIC2_OVCD_SFT 8 +#define RT5660_MIC2_OVCD_DIS (0x0 << 8) +#define RT5660_MIC2_OVCD_EN (0x1 << 8) +#define RT5660_MIC2_OVTH_MASK (0x3 << 6) +#define RT5660_MIC2_OVTH_SFT 6 +#define RT5660_MIC2_OVTH_600UA (0x0 << 6) +#define RT5660_MIC2_OVTH_1500UA (0x1 << 6) +#define RT5660_MIC2_OVTH_2000UA (0x2 << 6) +#define RT5660_PWR_CLK25M_MASK (0x1 << 4) +#define RT5660_PWR_CLK25M_SFT 4 +#define RT5660_PWR_CLK25M_PD (0x0 << 4) +#define RT5660_PWR_CLK25M_PU (0x1 << 4) + +/* EQ Control 1 (0xb0) */ +#define RT5660_EQ_SRC_MASK (0x1 << 15) +#define RT5660_EQ_SRC_SFT 15 +#define RT5660_EQ_SRC_DAC (0x0 << 15) +#define RT5660_EQ_SRC_ADC (0x1 << 15) +#define RT5660_EQ_UPD (0x1 << 14) +#define RT5660_EQ_UPD_BIT 14 + +/* Jack Detect Control (0xbb) */ +#define RT5660_JD_MASK (0x3 << 14) +#define RT5660_JD_SFT 14 +#define RT5660_JD_DIS (0x0 << 14) +#define RT5660_JD_GPIO1 (0x1 << 14) +#define RT5660_JD_GPIO2 (0x2 << 14) +#define RT5660_JD_LOUT_MASK (0x1 << 11) +#define RT5660_JD_LOUT_SFT 11 +#define RT5660_JD_LOUT_DIS (0x0 << 11) +#define RT5660_JD_LOUT_EN (0x1 << 11) +#define RT5660_JD_LOUT_TRG_MASK (0x1 << 10) +#define RT5660_JD_LOUT_TRG_SFT 10 +#define RT5660_JD_LOUT_TRG_LO (0x0 << 10) +#define RT5660_JD_LOUT_TRG_HI (0x1 << 10) +#define RT5660_JD_SPO_MASK (0x1 << 9) +#define RT5660_JD_SPO_SFT 9 +#define RT5660_JD_SPO_DIS (0x0 << 9) +#define RT5660_JD_SPO_EN (0x1 << 9) +#define RT5660_JD_SPO_TRG_MASK (0x1 << 8) +#define RT5660_JD_SPO_TRG_SFT 8 +#define RT5660_JD_SPO_TRG_LO (0x0 << 8) +#define RT5660_JD_SPO_TRG_HI (0x1 << 8) + +/* IRQ Control 1 (0xbd) */ +#define RT5660_IRQ_JD_MASK (0x1 << 15) +#define RT5660_IRQ_JD_SFT 15 +#define RT5660_IRQ_JD_BP (0x0 << 15) +#define RT5660_IRQ_JD_NOR (0x1 << 15) +#define RT5660_IRQ_OT_MASK (0x1 << 14) +#define RT5660_IRQ_OT_SFT 14 +#define RT5660_IRQ_OT_BP (0x0 << 14) +#define RT5660_IRQ_OT_NOR (0x1 << 14) +#define RT5660_JD_STKY_MASK (0x1 << 13) +#define RT5660_JD_STKY_SFT 13 +#define RT5660_JD_STKY_DIS (0x0 << 13) +#define RT5660_JD_STKY_EN (0x1 << 13) +#define RT5660_OT_STKY_MASK (0x1 << 12) +#define RT5660_OT_STKY_SFT 12 +#define RT5660_OT_STKY_DIS (0x0 << 12) +#define RT5660_OT_STKY_EN (0x1 << 12) +#define RT5660_JD_P_MASK (0x1 << 11) +#define RT5660_JD_P_SFT 11 +#define RT5660_JD_P_NOR (0x0 << 11) +#define RT5660_JD_P_INV (0x1 << 11) +#define RT5660_OT_P_MASK (0x1 << 10) +#define RT5660_OT_P_SFT 10 +#define RT5660_OT_P_NOR (0x0 << 10) +#define RT5660_OT_P_INV (0x1 << 10) + +/* IRQ Control 2 (0xbe) */ +#define RT5660_IRQ_MB1_OC_MASK (0x1 << 15) +#define RT5660_IRQ_MB1_OC_SFT 15 +#define RT5660_IRQ_MB1_OC_BP (0x0 << 15) +#define RT5660_IRQ_MB1_OC_NOR (0x1 << 15) +#define RT5660_IRQ_MB2_OC_MASK (0x1 << 14) +#define RT5660_IRQ_MB2_OC_SFT 14 +#define RT5660_IRQ_MB2_OC_BP (0x0 << 14) +#define RT5660_IRQ_MB2_OC_NOR (0x1 << 14) +#define RT5660_MB1_OC_STKY_MASK (0x1 << 11) +#define RT5660_MB1_OC_STKY_SFT 11 +#define RT5660_MB1_OC_STKY_DIS (0x0 << 11) +#define RT5660_MB1_OC_STKY_EN (0x1 << 11) +#define RT5660_MB2_OC_STKY_MASK (0x1 << 10) +#define RT5660_MB2_OC_STKY_SFT 10 +#define RT5660_MB2_OC_STKY_DIS (0x0 << 10) +#define RT5660_MB2_OC_STKY_EN (0x1 << 10) +#define RT5660_MB1_OC_P_MASK (0x1 << 7) +#define RT5660_MB1_OC_P_SFT 7 +#define RT5660_MB1_OC_P_NOR (0x0 << 7) +#define RT5660_MB1_OC_P_INV (0x1 << 7) +#define RT5660_MB2_OC_P_MASK (0x1 << 6) +#define RT5660_MB2_OC_P_SFT 6 +#define RT5660_MB2_OC_P_NOR (0x0 << 6) +#define RT5660_MB2_OC_P_INV (0x1 << 6) +#define RT5660_MB1_OC_CLR (0x1 << 3) +#define RT5660_MB1_OC_CLR_SFT 3 +#define RT5660_MB2_OC_CLR (0x1 << 2) +#define RT5660_MB2_OC_CLR_SFT 2 + +/* GPIO Control 1 (0xc0) */ +#define RT5660_GP2_PIN_MASK (0x1 << 14) +#define RT5660_GP2_PIN_SFT 14 +#define RT5660_GP2_PIN_GPIO2 (0x0 << 14) +#define RT5660_GP2_PIN_DMIC1_SDA (0x1 << 14) +#define RT5660_GP1_PIN_MASK (0x3 << 12) +#define RT5660_GP1_PIN_SFT 12 +#define RT5660_GP1_PIN_GPIO1 (0x0 << 12) +#define RT5660_GP1_PIN_DMIC1_SCL (0x1 << 12) +#define RT5660_GP1_PIN_IRQ (0x2 << 12) +#define RT5660_GPIO_M_MASK (0x1 << 9) +#define RT5660_GPIO_M_SFT 9 +#define RT5660_GPIO_M_FLT (0x0 << 9) +#define RT5660_GPIO_M_PH (0x1 << 9) + +/* GPIO Control 3 (0xc2) */ +#define RT5660_GP2_PF_MASK (0x1 << 5) +#define RT5660_GP2_PF_SFT 5 +#define RT5660_GP2_PF_IN (0x0 << 5) +#define RT5660_GP2_PF_OUT (0x1 << 5) +#define RT5660_GP2_OUT_MASK (0x1 << 4) +#define RT5660_GP2_OUT_SFT 4 +#define RT5660_GP2_OUT_LO (0x0 << 4) +#define RT5660_GP2_OUT_HI (0x1 << 4) +#define RT5660_GP2_P_MASK (0x1 << 3) +#define RT5660_GP2_P_SFT 3 +#define RT5660_GP2_P_NOR (0x0 << 3) +#define RT5660_GP2_P_INV (0x1 << 3) +#define RT5660_GP1_PF_MASK (0x1 << 2) +#define RT5660_GP1_PF_SFT 2 +#define RT5660_GP1_PF_IN (0x0 << 2) +#define RT5660_GP1_PF_OUT (0x1 << 2) +#define RT5660_GP1_OUT_MASK (0x1 << 1) +#define RT5660_GP1_OUT_SFT 1 +#define RT5660_GP1_OUT_LO (0x0 << 1) +#define RT5660_GP1_OUT_HI (0x1 << 1) +#define RT5660_GP1_P_MASK (0x1) +#define RT5660_GP1_P_SFT 0 +#define RT5660_GP1_P_NOR (0x0) +#define RT5660_GP1_P_INV (0x1) + +/* Soft volume and zero cross control 1 (0xd9) */ +#define RT5660_SV_MASK (0x1 << 15) +#define RT5660_SV_SFT 15 +#define RT5660_SV_DIS (0x0 << 15) +#define RT5660_SV_EN (0x1 << 15) +#define RT5660_SPO_SV_MASK (0x1 << 14) +#define RT5660_SPO_SV_SFT 14 +#define RT5660_SPO_SV_DIS (0x0 << 14) +#define RT5660_SPO_SV_EN (0x1 << 14) +#define RT5660_OUT_SV_MASK (0x1 << 12) +#define RT5660_OUT_SV_SFT 12 +#define RT5660_OUT_SV_DIS (0x0 << 12) +#define RT5660_OUT_SV_EN (0x1 << 12) +#define RT5660_ZCD_DIG_MASK (0x1 << 11) +#define RT5660_ZCD_DIG_SFT 11 +#define RT5660_ZCD_DIG_DIS (0x0 << 11) +#define RT5660_ZCD_DIG_EN (0x1 << 11) +#define RT5660_ZCD_MASK (0x1 << 10) +#define RT5660_ZCD_SFT 10 +#define RT5660_ZCD_PD (0x0 << 10) +#define RT5660_ZCD_PU (0x1 << 10) +#define RT5660_SV_DLY_MASK (0xf) +#define RT5660_SV_DLY_SFT 0 + +/* Soft volume and zero cross control 2 (0xda) */ +#define RT5660_ZCD_SPO_MASK (0x1 << 15) +#define RT5660_ZCD_SPO_SFT 15 +#define RT5660_ZCD_SPO_DIS (0x0 << 15) +#define RT5660_ZCD_SPO_EN (0x1 << 15) +#define RT5660_ZCD_OMR_MASK (0x1 << 8) +#define RT5660_ZCD_OMR_SFT 8 +#define RT5660_ZCD_OMR_DIS (0x0 << 8) +#define RT5660_ZCD_OMR_EN (0x1 << 8) +#define RT5660_ZCD_OML_MASK (0x1 << 7) +#define RT5660_ZCD_OML_SFT 7 +#define RT5660_ZCD_OML_DIS (0x0 << 7) +#define RT5660_ZCD_OML_EN (0x1 << 7) +#define RT5660_ZCD_SPM_MASK (0x1 << 6) +#define RT5660_ZCD_SPM_SFT 6 +#define RT5660_ZCD_SPM_DIS (0x0 << 6) +#define RT5660_ZCD_SPM_EN (0x1 << 6) +#define RT5660_ZCD_RMR_MASK (0x1 << 5) +#define RT5660_ZCD_RMR_SFT 5 +#define RT5660_ZCD_RMR_DIS (0x0 << 5) +#define RT5660_ZCD_RMR_EN (0x1 << 5) +#define RT5660_ZCD_RML_MASK (0x1 << 4) +#define RT5660_ZCD_RML_SFT 4 +#define RT5660_ZCD_RML_DIS (0x0 << 4) +#define RT5660_ZCD_RML_EN (0x1 << 4) + +/* General Control 1 (0xfa) */ +#define RT5660_PWR_VREF_HP (0x1 << 11) +#define RT5660_PWR_VREF_HP_SFT 11 +#define RT5660_AUTO_DIS_AMP (0x1 << 6) +#define RT5660_MCLK_DET (0x1 << 5) +#define RT5660_POW_CLKDET (0x1 << 1) +#define RT5660_DIG_GATE_CTRL (0x1) +#define RT5660_DIG_GATE_CTRL_SFT 0 + +/* System Clock Source */ +#define RT5660_SCLK_S_MCLK 0 +#define RT5660_SCLK_S_PLL1 1 +#define RT5660_SCLK_S_RCCLK 2 + +/* PLL1 Source */ +#define RT5660_PLL1_S_MCLK 0 +#define RT5660_PLL1_S_BCLK 1 + +enum { + RT5660_AIF1, + RT5660_AIFS, +}; + +struct rt5660_priv { + struct snd_soc_codec *codec; + struct rt5660_platform_data pdata; + struct regmap *regmap; + struct clk *mclk; + + int sysclk; + int sysclk_src; + int lrck[RT5660_AIFS]; + int bclk[RT5660_AIFS]; + int master[RT5660_AIFS]; + + int pll_src; + int pll_in; + int pll_out; +}; + +#endif diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c new file mode 100644 index 000000000000..a32508d7dcfd --- /dev/null +++ b/sound/soc/codecs/rt5663.c @@ -0,0 +1,3225 @@ +/* + * rt5663.c -- RT5663 ALSA SoC audio codec driver + * + * Copyright 2016 Realtek Semiconductor Corp. + * Author: Jack Yu <jack.yu@realtek.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/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/acpi.h> +#include <linux/workqueue.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/jack.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include "rt5663.h" +#include "rl6231.h" + +#define RT5663_DEVICE_ID_2 0x6451 +#define RT5663_DEVICE_ID_1 0x6406 + +enum { + CODEC_VER_1, + CODEC_VER_0, +}; + +struct rt5663_priv { + struct snd_soc_codec *codec; + struct regmap *regmap; + struct delayed_work jack_detect_work; + struct snd_soc_jack *hs_jack; + struct timer_list btn_check_timer; + + int codec_ver; + int sysclk; + int sysclk_src; + int lrck; + + int pll_src; + int pll_in; + int pll_out; + + int jack_type; +}; + +static const struct reg_default rt5663_v2_reg[] = { + { 0x0000, 0x0000 }, + { 0x0001, 0xc8c8 }, + { 0x0002, 0x8080 }, + { 0x0003, 0x8000 }, + { 0x0004, 0xc80a }, + { 0x0005, 0x0000 }, + { 0x0006, 0x0000 }, + { 0x0007, 0x0000 }, + { 0x000a, 0x0000 }, + { 0x000b, 0x0000 }, + { 0x000c, 0x0000 }, + { 0x000d, 0x0000 }, + { 0x000f, 0x0808 }, + { 0x0010, 0x4000 }, + { 0x0011, 0x0000 }, + { 0x0012, 0x1404 }, + { 0x0013, 0x1000 }, + { 0x0014, 0xa00a }, + { 0x0015, 0x0404 }, + { 0x0016, 0x0404 }, + { 0x0017, 0x0011 }, + { 0x0018, 0xafaf }, + { 0x0019, 0xafaf }, + { 0x001a, 0xafaf }, + { 0x001b, 0x0011 }, + { 0x001c, 0x2f2f }, + { 0x001d, 0x2f2f }, + { 0x001e, 0x2f2f }, + { 0x001f, 0x0000 }, + { 0x0020, 0x0000 }, + { 0x0021, 0x0000 }, + { 0x0022, 0x5757 }, + { 0x0023, 0x0039 }, + { 0x0024, 0x000b }, + { 0x0026, 0xc0c0 }, + { 0x0027, 0xc0c0 }, + { 0x0028, 0xc0c0 }, + { 0x0029, 0x8080 }, + { 0x002a, 0xaaaa }, + { 0x002b, 0xaaaa }, + { 0x002c, 0xaba8 }, + { 0x002d, 0x0000 }, + { 0x002e, 0x0000 }, + { 0x002f, 0x0000 }, + { 0x0030, 0x0000 }, + { 0x0031, 0x5000 }, + { 0x0032, 0x0000 }, + { 0x0033, 0x0000 }, + { 0x0034, 0x0000 }, + { 0x0035, 0x0000 }, + { 0x003a, 0x0000 }, + { 0x003b, 0x0000 }, + { 0x003c, 0x00ff }, + { 0x003d, 0x0000 }, + { 0x003e, 0x00ff }, + { 0x003f, 0x0000 }, + { 0x0040, 0x0000 }, + { 0x0041, 0x00ff }, + { 0x0042, 0x0000 }, + { 0x0043, 0x00ff }, + { 0x0044, 0x0c0c }, + { 0x0049, 0xc00b }, + { 0x004a, 0x0000 }, + { 0x004b, 0x031f }, + { 0x004d, 0x0000 }, + { 0x004e, 0x001f }, + { 0x004f, 0x0000 }, + { 0x0050, 0x001f }, + { 0x0052, 0xf000 }, + { 0x0061, 0x0000 }, + { 0x0062, 0x0000 }, + { 0x0063, 0x003e }, + { 0x0064, 0x0000 }, + { 0x0065, 0x0000 }, + { 0x0066, 0x003f }, + { 0x0067, 0x0000 }, + { 0x006b, 0x0000 }, + { 0x006d, 0xff00 }, + { 0x006e, 0x2808 }, + { 0x006f, 0x000a }, + { 0x0070, 0x8000 }, + { 0x0071, 0x8000 }, + { 0x0072, 0x8000 }, + { 0x0073, 0x7000 }, + { 0x0074, 0x7770 }, + { 0x0075, 0x0002 }, + { 0x0076, 0x0001 }, + { 0x0078, 0x00f0 }, + { 0x0079, 0x0000 }, + { 0x007a, 0x0000 }, + { 0x007b, 0x0000 }, + { 0x007c, 0x0000 }, + { 0x007d, 0x0123 }, + { 0x007e, 0x4500 }, + { 0x007f, 0x8003 }, + { 0x0080, 0x0000 }, + { 0x0081, 0x0000 }, + { 0x0082, 0x0000 }, + { 0x0083, 0x0000 }, + { 0x0084, 0x0000 }, + { 0x0085, 0x0000 }, + { 0x0086, 0x0008 }, + { 0x0087, 0x0000 }, + { 0x0088, 0x0000 }, + { 0x0089, 0x0000 }, + { 0x008a, 0x0000 }, + { 0x008b, 0x0000 }, + { 0x008c, 0x0003 }, + { 0x008e, 0x0060 }, + { 0x008f, 0x1000 }, + { 0x0091, 0x0c26 }, + { 0x0092, 0x0073 }, + { 0x0093, 0x0000 }, + { 0x0094, 0x0080 }, + { 0x0098, 0x0000 }, + { 0x0099, 0x0000 }, + { 0x009a, 0x0007 }, + { 0x009f, 0x0000 }, + { 0x00a0, 0x0000 }, + { 0x00a1, 0x0002 }, + { 0x00a2, 0x0001 }, + { 0x00a3, 0x0002 }, + { 0x00a4, 0x0001 }, + { 0x00ae, 0x2040 }, + { 0x00af, 0x0000 }, + { 0x00b6, 0x0000 }, + { 0x00b7, 0x0000 }, + { 0x00b8, 0x0000 }, + { 0x00b9, 0x0000 }, + { 0x00ba, 0x0002 }, + { 0x00bb, 0x0000 }, + { 0x00be, 0x0000 }, + { 0x00c0, 0x0000 }, + { 0x00c1, 0x0aaa }, + { 0x00c2, 0xaa80 }, + { 0x00c3, 0x0003 }, + { 0x00c4, 0x0000 }, + { 0x00d0, 0x0000 }, + { 0x00d1, 0x2244 }, + { 0x00d2, 0x0000 }, + { 0x00d3, 0x3300 }, + { 0x00d4, 0x2200 }, + { 0x00d9, 0x0809 }, + { 0x00da, 0x0000 }, + { 0x00db, 0x0008 }, + { 0x00dc, 0x00c0 }, + { 0x00dd, 0x6724 }, + { 0x00de, 0x3131 }, + { 0x00df, 0x0008 }, + { 0x00e0, 0x4000 }, + { 0x00e1, 0x3131 }, + { 0x00e2, 0x600c }, + { 0x00ea, 0xb320 }, + { 0x00eb, 0x0000 }, + { 0x00ec, 0xb300 }, + { 0x00ed, 0x0000 }, + { 0x00ee, 0xb320 }, + { 0x00ef, 0x0000 }, + { 0x00f0, 0x0201 }, + { 0x00f1, 0x0ddd }, + { 0x00f2, 0x0ddd }, + { 0x00f6, 0x0000 }, + { 0x00f7, 0x0000 }, + { 0x00f8, 0x0000 }, + { 0x00fa, 0x0000 }, + { 0x00fb, 0x0000 }, + { 0x00fc, 0x0000 }, + { 0x00fd, 0x0000 }, + { 0x00fe, 0x10ec }, + { 0x00ff, 0x6451 }, + { 0x0100, 0xaaaa }, + { 0x0101, 0x000a }, + { 0x010a, 0xaaaa }, + { 0x010b, 0xa0a0 }, + { 0x010c, 0xaeae }, + { 0x010d, 0xaaaa }, + { 0x010e, 0xaaaa }, + { 0x010f, 0xaaaa }, + { 0x0110, 0xe002 }, + { 0x0111, 0xa602 }, + { 0x0112, 0xaaaa }, + { 0x0113, 0x2000 }, + { 0x0117, 0x0f00 }, + { 0x0125, 0x0420 }, + { 0x0132, 0x0000 }, + { 0x0133, 0x0000 }, + { 0x0136, 0x5555 }, + { 0x0137, 0x5540 }, + { 0x0138, 0x3700 }, + { 0x0139, 0x79a1 }, + { 0x013a, 0x2020 }, + { 0x013b, 0x2020 }, + { 0x013c, 0x2005 }, + { 0x013f, 0x0000 }, + { 0x0145, 0x0002 }, + { 0x0146, 0x0000 }, + { 0x0147, 0x0000 }, + { 0x0148, 0x0000 }, + { 0x0160, 0x4ec0 }, + { 0x0161, 0x0080 }, + { 0x0162, 0x0200 }, + { 0x0163, 0x0800 }, + { 0x0164, 0x0000 }, + { 0x0165, 0x0000 }, + { 0x0166, 0x0000 }, + { 0x0167, 0x000f }, + { 0x0168, 0x000f }, + { 0x0170, 0x4e80 }, + { 0x0171, 0x0080 }, + { 0x0172, 0x0200 }, + { 0x0173, 0x0800 }, + { 0x0174, 0x00ff }, + { 0x0175, 0x0000 }, + { 0x0190, 0x4131 }, + { 0x0191, 0x4131 }, + { 0x0192, 0x4131 }, + { 0x0193, 0x4131 }, + { 0x0194, 0x0000 }, + { 0x0195, 0x0000 }, + { 0x0196, 0x0000 }, + { 0x0197, 0x0000 }, + { 0x0198, 0x0000 }, + { 0x0199, 0x0000 }, + { 0x01a0, 0x1e64 }, + { 0x01a1, 0x06a3 }, + { 0x01a2, 0x0000 }, + { 0x01a3, 0x0000 }, + { 0x01a4, 0x0000 }, + { 0x01a5, 0x0000 }, + { 0x01a6, 0x0000 }, + { 0x01a7, 0x0000 }, + { 0x01a8, 0x0000 }, + { 0x01a9, 0x0000 }, + { 0x01aa, 0x0000 }, + { 0x01ab, 0x0000 }, + { 0x01b5, 0x0000 }, + { 0x01b6, 0x01c3 }, + { 0x01b7, 0x02a0 }, + { 0x01b8, 0x03e9 }, + { 0x01b9, 0x1389 }, + { 0x01ba, 0xc351 }, + { 0x01bb, 0x0009 }, + { 0x01bc, 0x0018 }, + { 0x01bd, 0x002a }, + { 0x01be, 0x004c }, + { 0x01bf, 0x0097 }, + { 0x01c0, 0x433d }, + { 0x01c1, 0x0000 }, + { 0x01c2, 0x0000 }, + { 0x01c3, 0x0000 }, + { 0x01c4, 0x0000 }, + { 0x01c5, 0x0000 }, + { 0x01c6, 0x0000 }, + { 0x01c7, 0x0000 }, + { 0x01c8, 0x40af }, + { 0x01c9, 0x0702 }, + { 0x01ca, 0x0000 }, + { 0x01cb, 0x0000 }, + { 0x01cc, 0x5757 }, + { 0x01cd, 0x5757 }, + { 0x01ce, 0x5757 }, + { 0x01cf, 0x5757 }, + { 0x01d0, 0x5757 }, + { 0x01d1, 0x5757 }, + { 0x01d2, 0x5757 }, + { 0x01d3, 0x5757 }, + { 0x01d4, 0x5757 }, + { 0x01d5, 0x5757 }, + { 0x01d6, 0x003c }, + { 0x01da, 0x0000 }, + { 0x01db, 0x0000 }, + { 0x01dc, 0x0000 }, + { 0x01de, 0x7c00 }, + { 0x01df, 0x0320 }, + { 0x01e0, 0x06a1 }, + { 0x01e1, 0x0000 }, + { 0x01e2, 0x0000 }, + { 0x01e3, 0x0000 }, + { 0x01e4, 0x0000 }, + { 0x01e5, 0x0000 }, + { 0x01e6, 0x0001 }, + { 0x01e7, 0x0000 }, + { 0x01e8, 0x0000 }, + { 0x01ea, 0x0000 }, + { 0x01eb, 0x0000 }, + { 0x01ec, 0x0000 }, + { 0x01ed, 0x0000 }, + { 0x01ee, 0x0000 }, + { 0x01ef, 0x0000 }, + { 0x01f0, 0x0000 }, + { 0x01f1, 0x0000 }, + { 0x01f2, 0x0000 }, + { 0x01f3, 0x0000 }, + { 0x01f4, 0x0000 }, + { 0x0200, 0x0000 }, + { 0x0201, 0x0000 }, + { 0x0202, 0x0000 }, + { 0x0203, 0x0000 }, + { 0x0204, 0x0000 }, + { 0x0205, 0x0000 }, + { 0x0206, 0x0000 }, + { 0x0207, 0x0000 }, + { 0x0208, 0x0000 }, + { 0x0210, 0x60b1 }, + { 0x0211, 0xa000 }, + { 0x0212, 0x024c }, + { 0x0213, 0xf7ff }, + { 0x0214, 0x024c }, + { 0x0215, 0x0102 }, + { 0x0216, 0x00a3 }, + { 0x0217, 0x0048 }, + { 0x0218, 0x92c0 }, + { 0x0219, 0x0000 }, + { 0x021a, 0x00c8 }, + { 0x021b, 0x0020 }, + { 0x02fa, 0x0000 }, + { 0x02fb, 0x0000 }, + { 0x02fc, 0x0000 }, + { 0x02ff, 0x0110 }, + { 0x0300, 0x001f }, + { 0x0301, 0x032c }, + { 0x0302, 0x5f21 }, + { 0x0303, 0x4000 }, + { 0x0304, 0x4000 }, + { 0x0305, 0x06d5 }, + { 0x0306, 0x8000 }, + { 0x0307, 0x0700 }, + { 0x0310, 0x4560 }, + { 0x0311, 0xa4a8 }, + { 0x0312, 0x7418 }, + { 0x0313, 0x0000 }, + { 0x0314, 0x0006 }, + { 0x0315, 0xffff }, + { 0x0316, 0xc400 }, + { 0x0317, 0x0000 }, + { 0x0330, 0x00a6 }, + { 0x0331, 0x04c3 }, + { 0x0332, 0x27c8 }, + { 0x0333, 0xbf50 }, + { 0x0334, 0x0045 }, + { 0x0335, 0x0007 }, + { 0x0336, 0x7418 }, + { 0x0337, 0x0501 }, + { 0x0338, 0x0000 }, + { 0x0339, 0x0010 }, + { 0x033a, 0x1010 }, + { 0x03c0, 0x7e00 }, + { 0x03c1, 0x8000 }, + { 0x03c2, 0x8000 }, + { 0x03c3, 0x8000 }, + { 0x03c4, 0x8000 }, + { 0x03c5, 0x8000 }, + { 0x03c6, 0x8000 }, + { 0x03c7, 0x8000 }, + { 0x03c8, 0x8000 }, + { 0x03c9, 0x8000 }, + { 0x03ca, 0x8000 }, + { 0x03cb, 0x8000 }, + { 0x03cc, 0x8000 }, + { 0x03d0, 0x0000 }, + { 0x03d1, 0x0000 }, + { 0x03d2, 0x0000 }, + { 0x03d3, 0x0000 }, + { 0x03d4, 0x2000 }, + { 0x03d5, 0x2000 }, + { 0x03d6, 0x0000 }, + { 0x03d7, 0x0000 }, + { 0x03d8, 0x2000 }, + { 0x03d9, 0x2000 }, + { 0x03da, 0x2000 }, + { 0x03db, 0x2000 }, + { 0x03dc, 0x0000 }, + { 0x03dd, 0x0000 }, + { 0x03de, 0x0000 }, + { 0x03df, 0x2000 }, + { 0x03e0, 0x0000 }, + { 0x03e1, 0x0000 }, + { 0x03e2, 0x0000 }, + { 0x03e3, 0x0000 }, + { 0x03e4, 0x0000 }, + { 0x03e5, 0x0000 }, + { 0x03e6, 0x0000 }, + { 0x03e7, 0x0000 }, + { 0x03e8, 0x0000 }, + { 0x03e9, 0x0000 }, + { 0x03ea, 0x0000 }, + { 0x03eb, 0x0000 }, + { 0x03ec, 0x0000 }, + { 0x03ed, 0x0000 }, + { 0x03ee, 0x0000 }, + { 0x03ef, 0x0000 }, + { 0x03f0, 0x0800 }, + { 0x03f1, 0x0800 }, + { 0x03f2, 0x0800 }, + { 0x03f3, 0x0800 }, + { 0x03fe, 0x0000 }, + { 0x03ff, 0x0000 }, + { 0x07f0, 0x0000 }, + { 0x07fa, 0x0000 }, +}; + +static const struct reg_default rt5663_reg[] = { + { 0x0000, 0x0000 }, + { 0x0002, 0x0008 }, + { 0x0005, 0x1000 }, + { 0x0006, 0x1000 }, + { 0x000a, 0x0000 }, + { 0x0010, 0x000f }, + { 0x0015, 0x42c1 }, + { 0x0016, 0x0000 }, + { 0x0018, 0x000b }, + { 0x0019, 0xafaf }, + { 0x001c, 0x2f2f }, + { 0x001f, 0x0000 }, + { 0x0022, 0x5757 }, + { 0x0023, 0x0039 }, + { 0x0026, 0xc0c0 }, + { 0x0029, 0x8080 }, + { 0x002a, 0xa0a0 }, + { 0x002c, 0x000c }, + { 0x002d, 0x0000 }, + { 0x0040, 0x0808 }, + { 0x0061, 0x0000 }, + { 0x0062, 0x0000 }, + { 0x0063, 0x003e }, + { 0x0064, 0x0000 }, + { 0x0065, 0x0000 }, + { 0x0066, 0x0000 }, + { 0x006b, 0x0000 }, + { 0x006e, 0x0000 }, + { 0x006f, 0x0000 }, + { 0x0070, 0x8020 }, + { 0x0073, 0x1000 }, + { 0x0074, 0xe400 }, + { 0x0075, 0x0002 }, + { 0x0076, 0x0001 }, + { 0x0077, 0x00f0 }, + { 0x0078, 0x0000 }, + { 0x0079, 0x0000 }, + { 0x007a, 0x0123 }, + { 0x007b, 0x8003 }, + { 0x0080, 0x0000 }, + { 0x0081, 0x0000 }, + { 0x0082, 0x0000 }, + { 0x0083, 0x0000 }, + { 0x0084, 0x0000 }, + { 0x0086, 0x0008 }, + { 0x0087, 0x0000 }, + { 0x008a, 0x0000 }, + { 0x008b, 0x0000 }, + { 0x008c, 0x0003 }, + { 0x008e, 0x0004 }, + { 0x008f, 0x1000 }, + { 0x0090, 0x0646 }, + { 0x0091, 0x0e3e }, + { 0x0092, 0x1071 }, + { 0x0093, 0x0000 }, + { 0x0094, 0x0080 }, + { 0x0097, 0x0000 }, + { 0x0098, 0x0000 }, + { 0x009a, 0x0000 }, + { 0x009f, 0x0000 }, + { 0x00ae, 0x2000 }, + { 0x00af, 0x0000 }, + { 0x00b6, 0x0000 }, + { 0x00b7, 0x0000 }, + { 0x00b8, 0x0000 }, + { 0x00ba, 0x0000 }, + { 0x00bb, 0x0000 }, + { 0x00be, 0x0000 }, + { 0x00bf, 0x0000 }, + { 0x00c0, 0x0000 }, + { 0x00c1, 0x0000 }, + { 0x00c5, 0x0000 }, + { 0x00cb, 0xa02f }, + { 0x00cc, 0x0000 }, + { 0x00cd, 0x0e02 }, + { 0x00d9, 0x08f9 }, + { 0x00db, 0x0008 }, + { 0x00dc, 0x00c0 }, + { 0x00dd, 0x6724 }, + { 0x00de, 0x3131 }, + { 0x00df, 0x0008 }, + { 0x00e0, 0x4000 }, + { 0x00e1, 0x3131 }, + { 0x00e2, 0x0043 }, + { 0x00e4, 0x400b }, + { 0x00e5, 0x8031 }, + { 0x00e6, 0x3080 }, + { 0x00e7, 0x4100 }, + { 0x00e8, 0x1400 }, + { 0x00e9, 0xe00a }, + { 0x00ea, 0x0404 }, + { 0x00eb, 0x0404 }, + { 0x00ec, 0xb320 }, + { 0x00ed, 0x0000 }, + { 0x00f4, 0x0000 }, + { 0x00f6, 0x0000 }, + { 0x00f8, 0x0000 }, + { 0x00fa, 0x8000 }, + { 0x00fd, 0x0001 }, + { 0x00fe, 0x10ec }, + { 0x00ff, 0x6406 }, + { 0x0100, 0xa0a0 }, + { 0x0108, 0x4444 }, + { 0x0109, 0x4444 }, + { 0x010a, 0xaaaa }, + { 0x010b, 0x00a0 }, + { 0x010c, 0x8aaa }, + { 0x010d, 0xaaaa }, + { 0x010e, 0x2aaa }, + { 0x010f, 0x002a }, + { 0x0110, 0xa0a4 }, + { 0x0111, 0x4602 }, + { 0x0112, 0x0101 }, + { 0x0113, 0x2000 }, + { 0x0114, 0x0000 }, + { 0x0116, 0x0000 }, + { 0x0117, 0x0f00 }, + { 0x0118, 0x0006 }, + { 0x0125, 0x2224 }, + { 0x0126, 0x5550 }, + { 0x0127, 0x0400 }, + { 0x0128, 0x7711 }, + { 0x0132, 0x0004 }, + { 0x0137, 0x5441 }, + { 0x0139, 0x79a1 }, + { 0x013a, 0x30c0 }, + { 0x013b, 0x2000 }, + { 0x013c, 0x2005 }, + { 0x013d, 0x30c0 }, + { 0x013e, 0x0000 }, + { 0x0140, 0x3700 }, + { 0x0141, 0x1f00 }, + { 0x0144, 0x0000 }, + { 0x0145, 0x0002 }, + { 0x0146, 0x0000 }, + { 0x0160, 0x0e80 }, + { 0x0161, 0x0020 }, + { 0x0162, 0x0080 }, + { 0x0163, 0x0800 }, + { 0x0164, 0x0000 }, + { 0x0165, 0x0000 }, + { 0x0166, 0x0000 }, + { 0x0167, 0x1417 }, + { 0x0168, 0x0017 }, + { 0x0169, 0x0017 }, + { 0x0180, 0x2000 }, + { 0x0181, 0x0000 }, + { 0x0182, 0x0000 }, + { 0x0183, 0x2000 }, + { 0x0184, 0x0000 }, + { 0x0185, 0x0000 }, + { 0x01b0, 0x4b30 }, + { 0x01b1, 0x0000 }, + { 0x01b2, 0xd870 }, + { 0x01b3, 0x0000 }, + { 0x01b4, 0x0030 }, + { 0x01b5, 0x5757 }, + { 0x01b6, 0x5757 }, + { 0x01b7, 0x5757 }, + { 0x01b8, 0x5757 }, + { 0x01c0, 0x433d }, + { 0x01c1, 0x0540 }, + { 0x01c2, 0x0000 }, + { 0x01c3, 0x0000 }, + { 0x01c4, 0x0000 }, + { 0x01c5, 0x0009 }, + { 0x01c6, 0x0018 }, + { 0x01c7, 0x002a }, + { 0x01c8, 0x004c }, + { 0x01c9, 0x0097 }, + { 0x01ca, 0x01c3 }, + { 0x01cb, 0x03e9 }, + { 0x01cc, 0x1389 }, + { 0x01cd, 0xc351 }, + { 0x01ce, 0x0000 }, + { 0x01cf, 0x0000 }, + { 0x01d0, 0x0000 }, + { 0x01d1, 0x0000 }, + { 0x01d2, 0x0000 }, + { 0x01d3, 0x003c }, + { 0x01d4, 0x5757 }, + { 0x01d5, 0x5757 }, + { 0x01d6, 0x5757 }, + { 0x01d7, 0x5757 }, + { 0x01d8, 0x5757 }, + { 0x01d9, 0x5757 }, + { 0x01da, 0x0000 }, + { 0x01db, 0x0000 }, + { 0x01dd, 0x0009 }, + { 0x01de, 0x7f00 }, + { 0x01df, 0x00c8 }, + { 0x01e0, 0x0691 }, + { 0x01e1, 0x0000 }, + { 0x01e2, 0x0000 }, + { 0x01e3, 0x0000 }, + { 0x01e4, 0x0000 }, + { 0x01e5, 0x0040 }, + { 0x01e6, 0x0000 }, + { 0x01e7, 0x0000 }, + { 0x01e8, 0x0000 }, + { 0x01ea, 0x0000 }, + { 0x01eb, 0x0000 }, + { 0x01ec, 0x0000 }, + { 0x01ed, 0x0000 }, + { 0x01ee, 0x0000 }, + { 0x01ef, 0x0000 }, + { 0x01f0, 0x0000 }, + { 0x01f1, 0x0000 }, + { 0x01f2, 0x0000 }, + { 0x0200, 0x0000 }, + { 0x0201, 0x2244 }, + { 0x0202, 0xaaaa }, + { 0x0250, 0x8010 }, + { 0x0251, 0x0000 }, + { 0x0252, 0x028a }, + { 0x02fa, 0x0000 }, + { 0x02fb, 0x0000 }, + { 0x02fc, 0x0000 }, + { 0x0300, 0x0000 }, + { 0x03d0, 0x0000 }, + { 0x03d1, 0x0000 }, + { 0x03d2, 0x0000 }, + { 0x03d3, 0x0000 }, + { 0x03d4, 0x2000 }, + { 0x03d5, 0x2000 }, + { 0x03d6, 0x0000 }, + { 0x03d7, 0x0000 }, + { 0x03d8, 0x2000 }, + { 0x03d9, 0x2000 }, + { 0x03da, 0x2000 }, + { 0x03db, 0x2000 }, + { 0x03dc, 0x0000 }, + { 0x03dd, 0x0000 }, + { 0x03de, 0x0000 }, + { 0x03df, 0x2000 }, + { 0x03e0, 0x0000 }, + { 0x03e1, 0x0000 }, + { 0x03e2, 0x0000 }, + { 0x03e3, 0x0000 }, + { 0x03e4, 0x0000 }, + { 0x03e5, 0x0000 }, + { 0x03e6, 0x0000 }, + { 0x03e7, 0x0000 }, + { 0x03e8, 0x0000 }, + { 0x03e9, 0x0000 }, + { 0x03ea, 0x0000 }, + { 0x03eb, 0x0000 }, + { 0x03ec, 0x0000 }, + { 0x03ed, 0x0000 }, + { 0x03ee, 0x0000 }, + { 0x03ef, 0x0000 }, + { 0x03f0, 0x0800 }, + { 0x03f1, 0x0800 }, + { 0x03f2, 0x0800 }, + { 0x03f3, 0x0800 }, +}; + +static bool rt5663_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5663_RESET: + case RT5663_SIL_DET_CTL: + case RT5663_HP_IMP_GAIN_2: + case RT5663_AD_DA_MIXER: + case RT5663_FRAC_DIV_2: + case RT5663_MICBIAS_1: + case RT5663_ASRC_11_2: + case RT5663_ADC_EQ_1: + case RT5663_INT_ST_1: + case RT5663_INT_ST_2: + case RT5663_GPIO_STA1: + case RT5663_SIN_GEN_1: + case RT5663_IL_CMD_1: + case RT5663_IL_CMD_5: + case RT5663_IL_CMD_PWRSAV1: + case RT5663_EM_JACK_TYPE_1: + case RT5663_EM_JACK_TYPE_2: + case RT5663_EM_JACK_TYPE_3: + case RT5663_JD_CTRL2: + case RT5663_VENDOR_ID: + case RT5663_VENDOR_ID_1: + case RT5663_VENDOR_ID_2: + case RT5663_PLL_INT_REG: + case RT5663_SOFT_RAMP: + case RT5663_STO_DRE_1: + case RT5663_STO_DRE_5: + case RT5663_STO_DRE_6: + case RT5663_STO_DRE_7: + case RT5663_MIC_DECRO_1: + case RT5663_MIC_DECRO_4: + case RT5663_HP_IMP_SEN_1: + case RT5663_HP_IMP_SEN_3: + case RT5663_HP_IMP_SEN_4: + case RT5663_HP_IMP_SEN_5: + case RT5663_HP_CALIB_1_1: + case RT5663_HP_CALIB_9: + case RT5663_HP_CALIB_ST1: + case RT5663_HP_CALIB_ST2: + case RT5663_HP_CALIB_ST3: + case RT5663_HP_CALIB_ST4: + case RT5663_HP_CALIB_ST5: + case RT5663_HP_CALIB_ST6: + case RT5663_HP_CALIB_ST7: + case RT5663_HP_CALIB_ST8: + case RT5663_HP_CALIB_ST9: + case RT5663_ANA_JD: + return true; + default: + return false; + } +} + +static bool rt5663_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5663_RESET: + case RT5663_HP_OUT_EN: + case RT5663_HP_LCH_DRE: + case RT5663_HP_RCH_DRE: + case RT5663_CALIB_BST: + case RT5663_RECMIX: + case RT5663_SIL_DET_CTL: + case RT5663_PWR_SAV_SILDET: + case RT5663_SIDETONE_CTL: + case RT5663_STO1_DAC_DIG_VOL: + case RT5663_STO1_ADC_DIG_VOL: + case RT5663_STO1_BOOST: + case RT5663_HP_IMP_GAIN_1: + case RT5663_HP_IMP_GAIN_2: + case RT5663_STO1_ADC_MIXER: + case RT5663_AD_DA_MIXER: + case RT5663_STO_DAC_MIXER: + case RT5663_DIG_SIDE_MIXER: + case RT5663_BYPASS_STO_DAC: + case RT5663_CALIB_REC_MIX: + case RT5663_PWR_DIG_1: + case RT5663_PWR_DIG_2: + case RT5663_PWR_ANLG_1: + case RT5663_PWR_ANLG_2: + case RT5663_PWR_ANLG_3: + case RT5663_PWR_MIXER: + case RT5663_SIG_CLK_DET: + case RT5663_PRE_DIV_GATING_1: + case RT5663_PRE_DIV_GATING_2: + case RT5663_I2S1_SDP: + case RT5663_ADDA_CLK_1: + case RT5663_ADDA_RST: + case RT5663_FRAC_DIV_1: + case RT5663_FRAC_DIV_2: + case RT5663_TDM_1: + case RT5663_TDM_2: + case RT5663_TDM_3: + case RT5663_TDM_4: + case RT5663_TDM_5: + case RT5663_GLB_CLK: + case RT5663_PLL_1: + case RT5663_PLL_2: + case RT5663_ASRC_1: + case RT5663_ASRC_2: + case RT5663_ASRC_4: + case RT5663_DUMMY_REG: + case RT5663_ASRC_8: + case RT5663_ASRC_9: + case RT5663_ASRC_11: + case RT5663_DEPOP_1: + case RT5663_DEPOP_2: + case RT5663_DEPOP_3: + case RT5663_HP_CHARGE_PUMP_1: + case RT5663_HP_CHARGE_PUMP_2: + case RT5663_MICBIAS_1: + case RT5663_RC_CLK: + case RT5663_ASRC_11_2: + case RT5663_DUMMY_REG_2: + case RT5663_REC_PATH_GAIN: + case RT5663_AUTO_1MRC_CLK: + case RT5663_ADC_EQ_1: + case RT5663_ADC_EQ_2: + case RT5663_IRQ_1: + case RT5663_IRQ_2: + case RT5663_IRQ_3: + case RT5663_IRQ_4: + case RT5663_IRQ_5: + case RT5663_INT_ST_1: + case RT5663_INT_ST_2: + case RT5663_GPIO_1: + case RT5663_GPIO_2: + case RT5663_GPIO_STA1: + case RT5663_SIN_GEN_1: + case RT5663_SIN_GEN_2: + case RT5663_SIN_GEN_3: + case RT5663_SOF_VOL_ZC1: + case RT5663_IL_CMD_1: + case RT5663_IL_CMD_2: + case RT5663_IL_CMD_3: + case RT5663_IL_CMD_4: + case RT5663_IL_CMD_5: + case RT5663_IL_CMD_6: + case RT5663_IL_CMD_7: + case RT5663_IL_CMD_8: + case RT5663_IL_CMD_PWRSAV1: + case RT5663_IL_CMD_PWRSAV2: + case RT5663_EM_JACK_TYPE_1: + case RT5663_EM_JACK_TYPE_2: + case RT5663_EM_JACK_TYPE_3: + case RT5663_EM_JACK_TYPE_4: + case RT5663_EM_JACK_TYPE_5: + case RT5663_EM_JACK_TYPE_6: + case RT5663_STO1_HPF_ADJ1: + case RT5663_STO1_HPF_ADJ2: + case RT5663_FAST_OFF_MICBIAS: + case RT5663_JD_CTRL1: + case RT5663_JD_CTRL2: + case RT5663_DIG_MISC: + case RT5663_VENDOR_ID: + case RT5663_VENDOR_ID_1: + case RT5663_VENDOR_ID_2: + case RT5663_DIG_VOL_ZCD: + case RT5663_ANA_BIAS_CUR_1: + case RT5663_ANA_BIAS_CUR_2: + case RT5663_ANA_BIAS_CUR_3: + case RT5663_ANA_BIAS_CUR_4: + case RT5663_ANA_BIAS_CUR_5: + case RT5663_ANA_BIAS_CUR_6: + case RT5663_BIAS_CUR_5: + case RT5663_BIAS_CUR_6: + case RT5663_BIAS_CUR_7: + case RT5663_BIAS_CUR_8: + case RT5663_DACREF_LDO: + case RT5663_DUMMY_REG_3: + case RT5663_BIAS_CUR_9: + case RT5663_DUMMY_REG_4: + case RT5663_VREFADJ_OP: + case RT5663_VREF_RECMIX: + case RT5663_CHARGE_PUMP_1: + case RT5663_CHARGE_PUMP_1_2: + case RT5663_CHARGE_PUMP_1_3: + case RT5663_CHARGE_PUMP_2: + case RT5663_DIG_IN_PIN1: + case RT5663_PAD_DRV_CTL: + case RT5663_PLL_INT_REG: + case RT5663_CHOP_DAC_L: + case RT5663_CHOP_ADC: + case RT5663_CALIB_ADC: + case RT5663_CHOP_DAC_R: + case RT5663_DUMMY_CTL_DACLR: + case RT5663_DUMMY_REG_5: + case RT5663_SOFT_RAMP: + case RT5663_TEST_MODE_1: + case RT5663_TEST_MODE_2: + case RT5663_TEST_MODE_3: + case RT5663_STO_DRE_1: + case RT5663_STO_DRE_2: + case RT5663_STO_DRE_3: + case RT5663_STO_DRE_4: + case RT5663_STO_DRE_5: + case RT5663_STO_DRE_6: + case RT5663_STO_DRE_7: + case RT5663_STO_DRE_8: + case RT5663_STO_DRE_9: + case RT5663_STO_DRE_10: + case RT5663_MIC_DECRO_1: + case RT5663_MIC_DECRO_2: + case RT5663_MIC_DECRO_3: + case RT5663_MIC_DECRO_4: + case RT5663_MIC_DECRO_5: + case RT5663_MIC_DECRO_6: + case RT5663_HP_DECRO_1: + case RT5663_HP_DECRO_2: + case RT5663_HP_DECRO_3: + case RT5663_HP_DECRO_4: + case RT5663_HP_DECOUP: + case RT5663_HP_IMP_SEN_MAP8: + case RT5663_HP_IMP_SEN_MAP9: + case RT5663_HP_IMP_SEN_MAP10: + case RT5663_HP_IMP_SEN_MAP11: + case RT5663_HP_IMP_SEN_1: + case RT5663_HP_IMP_SEN_2: + case RT5663_HP_IMP_SEN_3: + case RT5663_HP_IMP_SEN_4: + case RT5663_HP_IMP_SEN_5: + case RT5663_HP_IMP_SEN_6: + case RT5663_HP_IMP_SEN_7: + case RT5663_HP_IMP_SEN_8: + case RT5663_HP_IMP_SEN_9: + case RT5663_HP_IMP_SEN_10: + case RT5663_HP_IMP_SEN_11: + case RT5663_HP_IMP_SEN_12: + case RT5663_HP_IMP_SEN_13: + case RT5663_HP_IMP_SEN_14: + case RT5663_HP_IMP_SEN_15: + case RT5663_HP_IMP_SEN_16: + case RT5663_HP_IMP_SEN_17: + case RT5663_HP_IMP_SEN_18: + case RT5663_HP_IMP_SEN_19: + case RT5663_HP_IMPSEN_DIG5: + case RT5663_HP_IMPSEN_MAP1: + case RT5663_HP_IMPSEN_MAP2: + case RT5663_HP_IMPSEN_MAP3: + case RT5663_HP_IMPSEN_MAP4: + case RT5663_HP_IMPSEN_MAP5: + case RT5663_HP_IMPSEN_MAP7: + case RT5663_HP_LOGIC_1: + case RT5663_HP_LOGIC_2: + case RT5663_HP_CALIB_1: + case RT5663_HP_CALIB_1_1: + case RT5663_HP_CALIB_2: + case RT5663_HP_CALIB_3: + case RT5663_HP_CALIB_4: + case RT5663_HP_CALIB_5: + case RT5663_HP_CALIB_5_1: + case RT5663_HP_CALIB_6: + case RT5663_HP_CALIB_7: + case RT5663_HP_CALIB_9: + case RT5663_HP_CALIB_10: + case RT5663_HP_CALIB_11: + case RT5663_HP_CALIB_ST1: + case RT5663_HP_CALIB_ST2: + case RT5663_HP_CALIB_ST3: + case RT5663_HP_CALIB_ST4: + case RT5663_HP_CALIB_ST5: + case RT5663_HP_CALIB_ST6: + case RT5663_HP_CALIB_ST7: + case RT5663_HP_CALIB_ST8: + case RT5663_HP_CALIB_ST9: + case RT5663_HP_AMP_DET: + case RT5663_DUMMY_REG_6: + case RT5663_HP_BIAS: + case RT5663_CBJ_1: + case RT5663_CBJ_2: + case RT5663_CBJ_3: + case RT5663_DUMMY_1: + case RT5663_DUMMY_2: + case RT5663_DUMMY_3: + case RT5663_ANA_JD: + case RT5663_ADC_LCH_LPF1_A1: + case RT5663_ADC_RCH_LPF1_A1: + case RT5663_ADC_LCH_LPF1_H0: + case RT5663_ADC_RCH_LPF1_H0: + case RT5663_ADC_LCH_BPF1_A1: + case RT5663_ADC_RCH_BPF1_A1: + case RT5663_ADC_LCH_BPF1_A2: + case RT5663_ADC_RCH_BPF1_A2: + case RT5663_ADC_LCH_BPF1_H0: + case RT5663_ADC_RCH_BPF1_H0: + case RT5663_ADC_LCH_BPF2_A1: + case RT5663_ADC_RCH_BPF2_A1: + case RT5663_ADC_LCH_BPF2_A2: + case RT5663_ADC_RCH_BPF2_A2: + case RT5663_ADC_LCH_BPF2_H0: + case RT5663_ADC_RCH_BPF2_H0: + case RT5663_ADC_LCH_BPF3_A1: + case RT5663_ADC_RCH_BPF3_A1: + case RT5663_ADC_LCH_BPF3_A2: + case RT5663_ADC_RCH_BPF3_A2: + case RT5663_ADC_LCH_BPF3_H0: + case RT5663_ADC_RCH_BPF3_H0: + case RT5663_ADC_LCH_BPF4_A1: + case RT5663_ADC_RCH_BPF4_A1: + case RT5663_ADC_LCH_BPF4_A2: + case RT5663_ADC_RCH_BPF4_A2: + case RT5663_ADC_LCH_BPF4_H0: + case RT5663_ADC_RCH_BPF4_H0: + case RT5663_ADC_LCH_HPF1_A1: + case RT5663_ADC_RCH_HPF1_A1: + case RT5663_ADC_LCH_HPF1_H0: + case RT5663_ADC_RCH_HPF1_H0: + case RT5663_ADC_EQ_PRE_VOL_L: + case RT5663_ADC_EQ_PRE_VOL_R: + case RT5663_ADC_EQ_POST_VOL_L: + case RT5663_ADC_EQ_POST_VOL_R: + return true; + default: + return false; + } +} + +static bool rt5663_v2_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5663_RESET: + case RT5663_CBJ_TYPE_2: + case RT5663_PDM_OUT_CTL: + case RT5663_PDM_I2C_DATA_CTL1: + case RT5663_PDM_I2C_DATA_CTL4: + case RT5663_ALC_BK_GAIN: + case RT5663_PLL_2: + case RT5663_MICBIAS_1: + case RT5663_ADC_EQ_1: + case RT5663_INT_ST_1: + case RT5663_GPIO_STA2: + case RT5663_IL_CMD_1: + case RT5663_IL_CMD_5: + case RT5663_A_JD_CTRL: + case RT5663_JD_CTRL2: + case RT5663_VENDOR_ID: + case RT5663_VENDOR_ID_1: + case RT5663_VENDOR_ID_2: + case RT5663_STO_DRE_1: + case RT5663_STO_DRE_5: + case RT5663_STO_DRE_6: + case RT5663_STO_DRE_7: + case RT5663_MONO_DYNA_6: + case RT5663_STO1_SIL_DET: + case RT5663_MONOL_SIL_DET: + case RT5663_MONOR_SIL_DET: + case RT5663_STO2_DAC_SIL: + case RT5663_MONO_AMP_CAL_ST1: + case RT5663_MONO_AMP_CAL_ST2: + case RT5663_MONO_AMP_CAL_ST3: + case RT5663_MONO_AMP_CAL_ST4: + case RT5663_HP_IMP_SEN_2: + case RT5663_HP_IMP_SEN_3: + case RT5663_HP_IMP_SEN_4: + case RT5663_HP_IMP_SEN_10: + case RT5663_HP_CALIB_1: + case RT5663_HP_CALIB_10: + case RT5663_HP_CALIB_ST1: + case RT5663_HP_CALIB_ST4: + case RT5663_HP_CALIB_ST5: + case RT5663_HP_CALIB_ST6: + case RT5663_HP_CALIB_ST7: + case RT5663_HP_CALIB_ST8: + case RT5663_HP_CALIB_ST9: + case RT5663_HP_CALIB_ST10: + case RT5663_HP_CALIB_ST11: + return true; + default: + return false; + } +} + +static bool rt5663_v2_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5663_LOUT_CTRL: + case RT5663_HP_AMP_2: + case RT5663_MONO_OUT: + case RT5663_MONO_GAIN: + case RT5663_AEC_BST: + case RT5663_IN1_IN2: + case RT5663_IN3_IN4: + case RT5663_INL1_INR1: + case RT5663_CBJ_TYPE_2: + case RT5663_CBJ_TYPE_3: + case RT5663_CBJ_TYPE_4: + case RT5663_CBJ_TYPE_5: + case RT5663_CBJ_TYPE_8: + case RT5663_DAC3_DIG_VOL: + case RT5663_DAC3_CTRL: + case RT5663_MONO_ADC_DIG_VOL: + case RT5663_STO2_ADC_DIG_VOL: + case RT5663_MONO_ADC_BST_GAIN: + case RT5663_STO2_ADC_BST_GAIN: + case RT5663_SIDETONE_CTRL: + case RT5663_MONO1_ADC_MIXER: + case RT5663_STO2_ADC_MIXER: + case RT5663_MONO_DAC_MIXER: + case RT5663_DAC2_SRC_CTRL: + case RT5663_IF_3_4_DATA_CTL: + case RT5663_IF_5_DATA_CTL: + case RT5663_PDM_OUT_CTL: + case RT5663_PDM_I2C_DATA_CTL1: + case RT5663_PDM_I2C_DATA_CTL2: + case RT5663_PDM_I2C_DATA_CTL3: + case RT5663_PDM_I2C_DATA_CTL4: + case RT5663_RECMIX1_NEW: + case RT5663_RECMIX1L_0: + case RT5663_RECMIX1L: + case RT5663_RECMIX1R_0: + case RT5663_RECMIX1R: + case RT5663_RECMIX2_NEW: + case RT5663_RECMIX2_L_2: + case RT5663_RECMIX2_R: + case RT5663_RECMIX2_R_2: + case RT5663_CALIB_REC_LR: + case RT5663_ALC_BK_GAIN: + case RT5663_MONOMIX_GAIN: + case RT5663_MONOMIX_IN_GAIN: + case RT5663_OUT_MIXL_GAIN: + case RT5663_OUT_LMIX_IN_GAIN: + case RT5663_OUT_RMIX_IN_GAIN: + case RT5663_OUT_RMIX_IN_GAIN1: + case RT5663_LOUT_MIXER_CTRL: + case RT5663_PWR_VOL: + case RT5663_ADCDAC_RST: + case RT5663_I2S34_SDP: + case RT5663_I2S5_SDP: + case RT5663_TDM_6: + case RT5663_TDM_7: + case RT5663_TDM_8: + case RT5663_TDM_9: + case RT5663_ASRC_3: + case RT5663_ASRC_6: + case RT5663_ASRC_7: + case RT5663_PLL_TRK_13: + case RT5663_I2S_M_CLK_CTL: + case RT5663_FDIV_I2S34_M_CLK: + case RT5663_FDIV_I2S34_M_CLK2: + case RT5663_FDIV_I2S5_M_CLK: + case RT5663_FDIV_I2S5_M_CLK2: + case RT5663_V2_IRQ_4: + case RT5663_GPIO_3: + case RT5663_GPIO_4: + case RT5663_GPIO_STA2: + case RT5663_HP_AMP_DET1: + case RT5663_HP_AMP_DET2: + case RT5663_HP_AMP_DET3: + case RT5663_MID_BD_HP_AMP: + case RT5663_LOW_BD_HP_AMP: + case RT5663_SOF_VOL_ZC2: + case RT5663_ADC_STO2_ADJ1: + case RT5663_ADC_STO2_ADJ2: + case RT5663_A_JD_CTRL: + case RT5663_JD1_TRES_CTRL: + case RT5663_JD2_TRES_CTRL: + case RT5663_V2_JD_CTRL2: + case RT5663_DUM_REG_2: + case RT5663_DUM_REG_3: + case RT5663_VENDOR_ID: + case RT5663_VENDOR_ID_1: + case RT5663_VENDOR_ID_2: + case RT5663_DACADC_DIG_VOL2: + case RT5663_DIG_IN_PIN2: + case RT5663_PAD_DRV_CTL1: + case RT5663_SOF_RAM_DEPOP: + case RT5663_VOL_TEST: + case RT5663_TEST_MODE_4: + case RT5663_TEST_MODE_5: + case RT5663_STO_DRE_9: + case RT5663_MONO_DYNA_1: + case RT5663_MONO_DYNA_2: + case RT5663_MONO_DYNA_3: + case RT5663_MONO_DYNA_4: + case RT5663_MONO_DYNA_5: + case RT5663_MONO_DYNA_6: + case RT5663_STO1_SIL_DET: + case RT5663_MONOL_SIL_DET: + case RT5663_MONOR_SIL_DET: + case RT5663_STO2_DAC_SIL: + case RT5663_PWR_SAV_CTL1: + case RT5663_PWR_SAV_CTL2: + case RT5663_PWR_SAV_CTL3: + case RT5663_PWR_SAV_CTL4: + case RT5663_PWR_SAV_CTL5: + case RT5663_PWR_SAV_CTL6: + case RT5663_MONO_AMP_CAL1: + case RT5663_MONO_AMP_CAL2: + case RT5663_MONO_AMP_CAL3: + case RT5663_MONO_AMP_CAL4: + case RT5663_MONO_AMP_CAL5: + case RT5663_MONO_AMP_CAL6: + case RT5663_MONO_AMP_CAL7: + case RT5663_MONO_AMP_CAL_ST1: + case RT5663_MONO_AMP_CAL_ST2: + case RT5663_MONO_AMP_CAL_ST3: + case RT5663_MONO_AMP_CAL_ST4: + case RT5663_MONO_AMP_CAL_ST5: + case RT5663_V2_HP_IMP_SEN_13: + case RT5663_V2_HP_IMP_SEN_14: + case RT5663_V2_HP_IMP_SEN_6: + case RT5663_V2_HP_IMP_SEN_7: + case RT5663_V2_HP_IMP_SEN_8: + case RT5663_V2_HP_IMP_SEN_9: + case RT5663_V2_HP_IMP_SEN_10: + case RT5663_HP_LOGIC_3: + case RT5663_HP_CALIB_ST10: + case RT5663_HP_CALIB_ST11: + case RT5663_PRO_REG_TBL_4: + case RT5663_PRO_REG_TBL_5: + case RT5663_PRO_REG_TBL_6: + case RT5663_PRO_REG_TBL_7: + case RT5663_PRO_REG_TBL_8: + case RT5663_PRO_REG_TBL_9: + case RT5663_SAR_ADC_INL_1: + case RT5663_SAR_ADC_INL_2: + case RT5663_SAR_ADC_INL_3: + case RT5663_SAR_ADC_INL_4: + case RT5663_SAR_ADC_INL_5: + case RT5663_SAR_ADC_INL_6: + case RT5663_SAR_ADC_INL_7: + case RT5663_SAR_ADC_INL_8: + case RT5663_SAR_ADC_INL_9: + case RT5663_SAR_ADC_INL_10: + case RT5663_SAR_ADC_INL_11: + case RT5663_SAR_ADC_INL_12: + case RT5663_DRC_CTRL_1: + case RT5663_DRC1_CTRL_2: + case RT5663_DRC1_CTRL_3: + case RT5663_DRC1_CTRL_4: + case RT5663_DRC1_CTRL_5: + case RT5663_DRC1_CTRL_6: + case RT5663_DRC1_HD_CTRL_1: + case RT5663_DRC1_HD_CTRL_2: + case RT5663_DRC1_PRI_REG_1: + case RT5663_DRC1_PRI_REG_2: + case RT5663_DRC1_PRI_REG_3: + case RT5663_DRC1_PRI_REG_4: + case RT5663_DRC1_PRI_REG_5: + case RT5663_DRC1_PRI_REG_6: + case RT5663_DRC1_PRI_REG_7: + case RT5663_DRC1_PRI_REG_8: + case RT5663_ALC_PGA_CTL_1: + case RT5663_ALC_PGA_CTL_2: + case RT5663_ALC_PGA_CTL_3: + case RT5663_ALC_PGA_CTL_4: + case RT5663_ALC_PGA_CTL_5: + case RT5663_ALC_PGA_CTL_6: + case RT5663_ALC_PGA_CTL_7: + case RT5663_ALC_PGA_CTL_8: + case RT5663_ALC_PGA_REG_1: + case RT5663_ALC_PGA_REG_2: + case RT5663_ALC_PGA_REG_3: + case RT5663_ADC_EQ_RECOV_1: + case RT5663_ADC_EQ_RECOV_2: + case RT5663_ADC_EQ_RECOV_3: + case RT5663_ADC_EQ_RECOV_4: + case RT5663_ADC_EQ_RECOV_5: + case RT5663_ADC_EQ_RECOV_6: + case RT5663_ADC_EQ_RECOV_7: + case RT5663_ADC_EQ_RECOV_8: + case RT5663_ADC_EQ_RECOV_9: + case RT5663_ADC_EQ_RECOV_10: + case RT5663_ADC_EQ_RECOV_11: + case RT5663_ADC_EQ_RECOV_12: + case RT5663_ADC_EQ_RECOV_13: + case RT5663_VID_HIDDEN: + case RT5663_VID_CUSTOMER: + case RT5663_SCAN_MODE: + case RT5663_I2C_BYPA: + return true; + case RT5663_TDM_1: + case RT5663_DEPOP_3: + case RT5663_ASRC_11_2: + case RT5663_INT_ST_2: + case RT5663_GPIO_STA1: + case RT5663_SIN_GEN_1: + case RT5663_SIN_GEN_2: + case RT5663_SIN_GEN_3: + case RT5663_IL_CMD_PWRSAV1: + case RT5663_IL_CMD_PWRSAV2: + case RT5663_EM_JACK_TYPE_1: + case RT5663_EM_JACK_TYPE_2: + case RT5663_EM_JACK_TYPE_3: + case RT5663_EM_JACK_TYPE_4: + case RT5663_FAST_OFF_MICBIAS: + case RT5663_ANA_BIAS_CUR_1: + case RT5663_ANA_BIAS_CUR_2: + case RT5663_BIAS_CUR_9: + case RT5663_DUMMY_REG_4: + case RT5663_VREF_RECMIX: + case RT5663_CHARGE_PUMP_1_2: + case RT5663_CHARGE_PUMP_1_3: + case RT5663_CHARGE_PUMP_2: + case RT5663_CHOP_DAC_R: + case RT5663_DUMMY_CTL_DACLR: + case RT5663_DUMMY_REG_5: + case RT5663_SOFT_RAMP: + case RT5663_TEST_MODE_1: + case RT5663_STO_DRE_10: + case RT5663_MIC_DECRO_1: + case RT5663_MIC_DECRO_2: + case RT5663_MIC_DECRO_3: + case RT5663_MIC_DECRO_4: + case RT5663_MIC_DECRO_5: + case RT5663_MIC_DECRO_6: + case RT5663_HP_DECRO_1: + case RT5663_HP_DECRO_2: + case RT5663_HP_DECRO_3: + case RT5663_HP_DECRO_4: + case RT5663_HP_DECOUP: + case RT5663_HP_IMPSEN_MAP4: + case RT5663_HP_IMPSEN_MAP5: + case RT5663_HP_IMPSEN_MAP7: + case RT5663_HP_CALIB_1: + case RT5663_CBJ_1: + case RT5663_CBJ_2: + case RT5663_CBJ_3: + return false; + default: + return rt5663_readable_register(dev, reg); + } +} + +static const DECLARE_TLV_DB_SCALE(rt5663_hp_vol_tlv, -2400, 150, 0); +static const DECLARE_TLV_DB_SCALE(rt5663_v2_hp_vol_tlv, -2250, 150, 0); +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6525, 75, 0); +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1725, 75, 0); + +/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ +static const DECLARE_TLV_DB_RANGE(in_bst_tlv, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), + 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0), + 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0) +); + +/* Interface data select */ +static const char * const rt5663_if1_adc_data_select[] = { + "L/R", "R/L", "L/L", "R/R" +}; + +static SOC_ENUM_SINGLE_DECL(rt5663_if1_adc_enum, RT5663_TDM_2, + RT5663_DATA_SWAP_ADCDAT1_SHIFT, rt5663_if1_adc_data_select); + +static void rt5663_enable_push_button_irq(struct snd_soc_codec *codec, + bool enable) +{ + struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec); + + if (enable) { + snd_soc_update_bits(codec, RT5663_IL_CMD_6, + RT5663_EN_4BTN_INL_MASK, RT5663_EN_4BTN_INL_EN); + /* reset in-line command */ + snd_soc_update_bits(codec, RT5663_IL_CMD_6, + RT5663_RESET_4BTN_INL_MASK, + RT5663_RESET_4BTN_INL_RESET); + snd_soc_update_bits(codec, RT5663_IL_CMD_6, + RT5663_RESET_4BTN_INL_MASK, + RT5663_RESET_4BTN_INL_NOR); + switch (rt5663->codec_ver) { + case CODEC_VER_1: + snd_soc_update_bits(codec, RT5663_IRQ_3, + RT5663_V2_EN_IRQ_INLINE_MASK, + RT5663_V2_EN_IRQ_INLINE_NOR); + break; + case CODEC_VER_0: + snd_soc_update_bits(codec, RT5663_IRQ_2, + RT5663_EN_IRQ_INLINE_MASK, + RT5663_EN_IRQ_INLINE_NOR); + break; + default: + dev_err(codec->dev, "Unknown CODEC Version\n"); + } + } else { + switch (rt5663->codec_ver) { + case CODEC_VER_1: + snd_soc_update_bits(codec, RT5663_IRQ_3, + RT5663_V2_EN_IRQ_INLINE_MASK, + RT5663_V2_EN_IRQ_INLINE_BYP); + break; + case CODEC_VER_0: + snd_soc_update_bits(codec, RT5663_IRQ_2, + RT5663_EN_IRQ_INLINE_MASK, + RT5663_EN_IRQ_INLINE_BYP); + break; + default: + dev_err(codec->dev, "Unknown CODEC Version\n"); + } + snd_soc_update_bits(codec, RT5663_IL_CMD_6, + RT5663_EN_4BTN_INL_MASK, RT5663_EN_4BTN_INL_DIS); + /* reset in-line command */ + snd_soc_update_bits(codec, RT5663_IL_CMD_6, + RT5663_RESET_4BTN_INL_MASK, + RT5663_RESET_4BTN_INL_RESET); + snd_soc_update_bits(codec, RT5663_IL_CMD_6, + RT5663_RESET_4BTN_INL_MASK, + RT5663_RESET_4BTN_INL_NOR); + } +} + +/** + * rt5663_v2_jack_detect - Detect headset. + * @codec: SoC audio codec device. + * @jack_insert: Jack insert or not. + * + * Detect whether is headset or not when jack inserted. + * + * Returns detect status. + */ + +static int rt5663_v2_jack_detect(struct snd_soc_codec *codec, int jack_insert) +{ + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec); + int val, i = 0, sleep_time[5] = {300, 150, 100, 50, 30}; + + dev_dbg(codec->dev, "%s jack_insert:%d\n", __func__, jack_insert); + if (jack_insert) { + snd_soc_write(codec, RT5663_CBJ_TYPE_2, 0x8040); + snd_soc_write(codec, RT5663_CBJ_TYPE_3, 0x1484); + + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1"); + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS2"); + snd_soc_dapm_force_enable_pin(dapm, "Mic Det Power"); + snd_soc_dapm_force_enable_pin(dapm, "CBJ Power"); + snd_soc_dapm_sync(dapm); + snd_soc_update_bits(codec, RT5663_RC_CLK, + RT5663_DIG_1M_CLK_MASK, RT5663_DIG_1M_CLK_EN); + snd_soc_update_bits(codec, RT5663_RECMIX, 0x8, 0x8); + + while (i < 5) { + msleep(sleep_time[i]); + val = snd_soc_read(codec, RT5663_CBJ_TYPE_2) & 0x0003; + if (val == 0x1 || val == 0x2 || val == 0x3) + break; + dev_dbg(codec->dev, "%s: MX-0011 val=%x sleep %d\n", + __func__, val, sleep_time[i]); + i++; + } + dev_dbg(codec->dev, "%s val = %d\n", __func__, val); + switch (val) { + case 1: + case 2: + rt5663->jack_type = SND_JACK_HEADSET; + rt5663_enable_push_button_irq(codec, true); + break; + default: + snd_soc_dapm_disable_pin(dapm, "MICBIAS1"); + snd_soc_dapm_disable_pin(dapm, "MICBIAS2"); + snd_soc_dapm_disable_pin(dapm, "Mic Det Power"); + snd_soc_dapm_disable_pin(dapm, "CBJ Power"); + snd_soc_dapm_sync(dapm); + rt5663->jack_type = SND_JACK_HEADPHONE; + break; + } + } else { + snd_soc_update_bits(codec, RT5663_RECMIX, 0x8, 0x0); + + if (rt5663->jack_type == SND_JACK_HEADSET) { + rt5663_enable_push_button_irq(codec, false); + snd_soc_dapm_disable_pin(dapm, "MICBIAS1"); + snd_soc_dapm_disable_pin(dapm, "MICBIAS2"); + snd_soc_dapm_disable_pin(dapm, "Mic Det Power"); + snd_soc_dapm_disable_pin(dapm, "CBJ Power"); + snd_soc_dapm_sync(dapm); + } + rt5663->jack_type = 0; + } + + dev_dbg(codec->dev, "jack_type = %d\n", rt5663->jack_type); + return rt5663->jack_type; +} + +/** + * rt5663_jack_detect - Detect headset. + * @codec: SoC audio codec device. + * @jack_insert: Jack insert or not. + * + * Detect whether is headset or not when jack inserted. + * + * Returns detect status. + */ +static int rt5663_jack_detect(struct snd_soc_codec *codec, int jack_insert) +{ + struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec); + int val, i = 0, sleep_time[5] = {300, 150, 100, 50, 30}; + + dev_dbg(codec->dev, "%s jack_insert:%d\n", __func__, jack_insert); + + if (jack_insert) { + snd_soc_update_bits(codec, RT5663_DIG_MISC, + RT5663_DIG_GATE_CTRL_MASK, RT5663_DIG_GATE_CTRL_EN); + snd_soc_update_bits(codec, RT5663_HP_CHARGE_PUMP_1, + RT5663_SI_HP_MASK | RT5663_OSW_HP_L_MASK | + RT5663_OSW_HP_R_MASK, RT5663_SI_HP_EN | + RT5663_OSW_HP_L_DIS | RT5663_OSW_HP_R_DIS); + snd_soc_update_bits(codec, RT5663_DUMMY_1, + RT5663_EMB_CLK_MASK | RT5663_HPA_CPL_BIAS_MASK | + RT5663_HPA_CPR_BIAS_MASK, RT5663_EMB_CLK_EN | + RT5663_HPA_CPL_BIAS_1 | RT5663_HPA_CPR_BIAS_1); + snd_soc_update_bits(codec, RT5663_CBJ_1, + RT5663_INBUF_CBJ_BST1_MASK | RT5663_CBJ_SENSE_BST1_MASK, + RT5663_INBUF_CBJ_BST1_ON | RT5663_CBJ_SENSE_BST1_L); + snd_soc_update_bits(codec, RT5663_IL_CMD_2, + RT5663_PWR_MIC_DET_MASK, RT5663_PWR_MIC_DET_ON); + /* BST1 power on for JD */ + snd_soc_update_bits(codec, RT5663_PWR_ANLG_2, + RT5663_PWR_BST1_MASK, RT5663_PWR_BST1_ON); + snd_soc_update_bits(codec, RT5663_EM_JACK_TYPE_1, + RT5663_CBJ_DET_MASK | RT5663_EXT_JD_MASK | + RT5663_POL_EXT_JD_MASK, RT5663_CBJ_DET_EN | + RT5663_EXT_JD_EN | RT5663_POL_EXT_JD_EN); + snd_soc_update_bits(codec, RT5663_PWR_ANLG_1, + RT5663_PWR_MB_MASK | RT5663_LDO1_DVO_MASK | + RT5663_AMP_HP_MASK, RT5663_PWR_MB | + RT5663_LDO1_DVO_0_9V | RT5663_AMP_HP_3X); + snd_soc_update_bits(codec, RT5663_AUTO_1MRC_CLK, + RT5663_IRQ_POW_SAV_MASK, RT5663_IRQ_POW_SAV_EN); + snd_soc_update_bits(codec, RT5663_IRQ_1, + RT5663_EN_IRQ_JD1_MASK, RT5663_EN_IRQ_JD1_EN); + while (i < 5) { + msleep(sleep_time[i]); + val = snd_soc_read(codec, RT5663_EM_JACK_TYPE_2) & + 0x0003; + dev_dbg(codec->dev, "%s: MX-00e7 val=%x sleep %d\n", + __func__, val, sleep_time[i]); + i++; + if (val == 0x1 || val == 0x2 || val == 0x3) + break; + } + dev_dbg(codec->dev, "%s val = %d\n", __func__, val); + switch (val) { + case 1: + case 2: + rt5663->jack_type = SND_JACK_HEADSET; + rt5663_enable_push_button_irq(codec, true); + break; + default: + rt5663->jack_type = SND_JACK_HEADPHONE; + break; + } + } else { + if (rt5663->jack_type == SND_JACK_HEADSET) + rt5663_enable_push_button_irq(codec, false); + rt5663->jack_type = 0; + } + + dev_dbg(codec->dev, "jack_type = %d\n", rt5663->jack_type); + return rt5663->jack_type; +} + +static int rt5663_button_detect(struct snd_soc_codec *codec) +{ + int btn_type, val; + + val = snd_soc_read(codec, RT5663_IL_CMD_5); + dev_dbg(codec->dev, "%s: val=0x%x\n", __func__, val); + btn_type = val & 0xfff0; + snd_soc_write(codec, RT5663_IL_CMD_5, val); + + return btn_type; +} + +static irqreturn_t rt5663_irq(int irq, void *data) +{ + struct rt5663_priv *rt5663 = data; + + dev_dbg(rt5663->codec->dev, "%s IRQ queue work\n", __func__); + + queue_delayed_work(system_wq, &rt5663->jack_detect_work, + msecs_to_jiffies(250)); + + return IRQ_HANDLED; +} + +int rt5663_set_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *hs_jack) +{ + struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec); + + rt5663->hs_jack = hs_jack; + + rt5663_irq(0, rt5663); + + return 0; +} +EXPORT_SYMBOL_GPL(rt5663_set_jack_detect); + +static bool rt5663_check_jd_status(struct snd_soc_codec *codec) +{ + struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec); + int val = snd_soc_read(codec, RT5663_INT_ST_1); + + dev_dbg(codec->dev, "%s val=%x\n", __func__, val); + + /* JD1 */ + switch (rt5663->codec_ver) { + case CODEC_VER_1: + return !(val & 0x2000); + case CODEC_VER_0: + return !(val & 0x1000); + default: + dev_err(codec->dev, "Unknown CODEC Version\n"); + } + + return false; +} + +static void rt5663_jack_detect_work(struct work_struct *work) +{ + struct rt5663_priv *rt5663 = + container_of(work, struct rt5663_priv, jack_detect_work.work); + struct snd_soc_codec *codec = rt5663->codec; + int btn_type, report = 0; + + if (!codec) + return; + + if (rt5663_check_jd_status(codec)) { + /* jack in */ + if (rt5663->jack_type == 0) { + /* jack was out, report jack type */ + switch (rt5663->codec_ver) { + case CODEC_VER_1: + report = rt5663_v2_jack_detect( + rt5663->codec, 1); + break; + case CODEC_VER_0: + report = rt5663_jack_detect(rt5663->codec, 1); + break; + default: + dev_err(codec->dev, "Unknown CODEC Version\n"); + } + } else { + /* jack is already in, report button event */ + report = SND_JACK_HEADSET; + btn_type = rt5663_button_detect(rt5663->codec); + /** + * rt5663 can report three kinds of button behavior, + * one click, double click and hold. However, + * currently we will report button pressed/released + * event. So all the three button behaviors are + * treated as button pressed. + */ + switch (btn_type) { + case 0x8000: + case 0x4000: + case 0x2000: + report |= SND_JACK_BTN_0; + break; + case 0x1000: + case 0x0800: + case 0x0400: + report |= SND_JACK_BTN_1; + break; + case 0x0200: + case 0x0100: + case 0x0080: + report |= SND_JACK_BTN_2; + break; + case 0x0040: + case 0x0020: + case 0x0010: + report |= SND_JACK_BTN_3; + break; + case 0x0000: /* unpressed */ + break; + default: + btn_type = 0; + dev_err(rt5663->codec->dev, + "Unexpected button code 0x%04x\n", + btn_type); + break; + } + /* button release or spurious interrput*/ + if (btn_type == 0) + report = rt5663->jack_type; + } + } else { + /* jack out */ + switch (rt5663->codec_ver) { + case CODEC_VER_1: + report = rt5663_v2_jack_detect(rt5663->codec, 0); + break; + case CODEC_VER_0: + report = rt5663_jack_detect(rt5663->codec, 0); + break; + default: + dev_err(codec->dev, "Unknown CODEC Version\n"); + } + } + dev_dbg(codec->dev, "%s jack report: 0x%04x\n", __func__, report); + snd_soc_jack_report(rt5663->hs_jack, report, SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); +} + +static const struct snd_kcontrol_new rt5663_snd_controls[] = { + /* DAC Digital Volume */ + SOC_DOUBLE_TLV("DAC Playback Volume", RT5663_STO1_DAC_DIG_VOL, + RT5663_DAC_L1_VOL_SHIFT + 1, RT5663_DAC_R1_VOL_SHIFT + 1, + 87, 0, dac_vol_tlv), + /* ADC Digital Volume Control */ + SOC_DOUBLE("ADC Capture Switch", RT5663_STO1_ADC_DIG_VOL, + RT5663_ADC_L_MUTE_SHIFT, RT5663_ADC_R_MUTE_SHIFT, 1, 1), + SOC_DOUBLE_TLV("ADC Capture Volume", RT5663_STO1_ADC_DIG_VOL, + RT5663_ADC_L_VOL_SHIFT + 1, RT5663_ADC_R_VOL_SHIFT + 1, + 63, 0, adc_vol_tlv), +}; + +static const struct snd_kcontrol_new rt5663_v2_specific_controls[] = { + /* Headphone Output Volume */ + SOC_DOUBLE_R_TLV("Headphone Playback Volume", RT5663_HP_LCH_DRE, + RT5663_HP_RCH_DRE, RT5663_GAIN_HP_SHIFT, 15, 1, + rt5663_v2_hp_vol_tlv), + /* Mic Boost Volume */ + SOC_SINGLE_TLV("IN1 Capture Volume", RT5663_AEC_BST, + RT5663_GAIN_CBJ_SHIFT, 8, 0, in_bst_tlv), +}; + +static const struct snd_kcontrol_new rt5663_specific_controls[] = { + /* Headphone Output Volume */ + SOC_DOUBLE_R_TLV("Headphone Playback Volume", RT5663_STO_DRE_9, + RT5663_STO_DRE_10, RT5663_DRE_GAIN_HP_SHIFT, 23, 1, + rt5663_hp_vol_tlv), + /* Mic Boost Volume*/ + SOC_SINGLE_TLV("IN1 Capture Volume", RT5663_CBJ_2, + RT5663_GAIN_BST1_SHIFT, 8, 0, in_bst_tlv), + /* Data Swap for Slot0/1 in ADCDAT1 */ + SOC_ENUM("IF1 ADC Data Swap", rt5663_if1_adc_enum), +}; + +static int rt5663_is_sys_clk_from_pll(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_widget *sink) +{ + unsigned int val; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + val = snd_soc_read(codec, RT5663_GLB_CLK); + val &= RT5663_SCLK_SRC_MASK; + if (val == RT5663_SCLK_SRC_PLL1) + return 1; + else + return 0; +} + +static int rt5663_is_using_asrc(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_widget *sink) +{ + unsigned int reg, shift, val; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec); + + if (rt5663->codec_ver == CODEC_VER_1) { + switch (w->shift) { + case RT5663_ADC_STO1_ASRC_SHIFT: + reg = RT5663_ASRC_3; + shift = RT5663_V2_AD_STO1_TRACK_SHIFT; + break; + case RT5663_DAC_STO1_ASRC_SHIFT: + reg = RT5663_ASRC_2; + shift = RT5663_DA_STO1_TRACK_SHIFT; + break; + default: + return 0; + } + } else { + switch (w->shift) { + case RT5663_ADC_STO1_ASRC_SHIFT: + reg = RT5663_ASRC_2; + shift = RT5663_AD_STO1_TRACK_SHIFT; + break; + case RT5663_DAC_STO1_ASRC_SHIFT: + reg = RT5663_ASRC_2; + shift = RT5663_DA_STO1_TRACK_SHIFT; + break; + default: + return 0; + } + } + + val = (snd_soc_read(codec, reg) >> shift) & 0x7; + + if (val) + return 1; + + return 0; +} + +static int rt5663_i2s_use_asrc(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm); + struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec); + int da_asrc_en, ad_asrc_en; + + da_asrc_en = (snd_soc_read(codec, RT5663_ASRC_2) & + RT5663_DA_STO1_TRACK_MASK) ? 1 : 0; + switch (rt5663->codec_ver) { + case CODEC_VER_1: + ad_asrc_en = (snd_soc_read(codec, RT5663_ASRC_3) & + RT5663_V2_AD_STO1_TRACK_MASK) ? 1 : 0; + break; + case CODEC_VER_0: + ad_asrc_en = (snd_soc_read(codec, RT5663_ASRC_2) & + RT5663_AD_STO1_TRACK_MASK) ? 1 : 0; + break; + default: + dev_err(codec->dev, "Unknown CODEC Version\n"); + return 1; + } + + if (da_asrc_en || ad_asrc_en) + if (rt5663->sysclk > rt5663->lrck * 384) + return 1; + + dev_err(codec->dev, "sysclk < 384 x fs, disable i2s asrc\n"); + + return 0; +} + +/** + * rt5663_sel_asrc_clk_src - select ASRC clock source for a set of filters + * @codec: SoC audio codec device. + * @filter_mask: mask of filters. + * @clk_src: clock source + * + * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5663 can + * only support standard 32fs or 64fs i2s format, ASRC should be enabled to + * support special i2s clock format such as Intel's 100fs(100 * sampling rate). + * ASRC function will track i2s clock and generate a corresponding system clock + * for codec. This function provides an API to select the clock source for a + * set of filters specified by the mask. And the codec driver will turn on ASRC + * for these filters if ASRC is selected as their clock source. + */ +int rt5663_sel_asrc_clk_src(struct snd_soc_codec *codec, + unsigned int filter_mask, unsigned int clk_src) +{ + struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec); + unsigned int asrc2_mask = 0; + unsigned int asrc2_value = 0; + unsigned int asrc3_mask = 0; + unsigned int asrc3_value = 0; + + switch (clk_src) { + case RT5663_CLK_SEL_SYS: + case RT5663_CLK_SEL_I2S1_ASRC: + break; + + default: + return -EINVAL; + } + + if (filter_mask & RT5663_DA_STEREO_FILTER) { + asrc2_mask |= RT5663_DA_STO1_TRACK_MASK; + asrc2_value |= clk_src << RT5663_DA_STO1_TRACK_SHIFT; + } + + if (filter_mask & RT5663_AD_STEREO_FILTER) { + switch (rt5663->codec_ver) { + case CODEC_VER_1: + asrc3_mask |= RT5663_V2_AD_STO1_TRACK_MASK; + asrc3_value |= clk_src << RT5663_V2_AD_STO1_TRACK_SHIFT; + break; + case CODEC_VER_0: + asrc2_mask |= RT5663_AD_STO1_TRACK_MASK; + asrc2_value |= clk_src << RT5663_AD_STO1_TRACK_SHIFT; + break; + default: + dev_err(codec->dev, "Unknown CODEC Version\n"); + } + } + + if (asrc2_mask) + snd_soc_update_bits(codec, RT5663_ASRC_2, asrc2_mask, + asrc2_value); + + if (asrc3_mask) + snd_soc_update_bits(codec, RT5663_ASRC_3, asrc3_mask, + asrc3_value); + + return 0; +} +EXPORT_SYMBOL_GPL(rt5663_sel_asrc_clk_src); + +/* Analog Mixer */ +static const struct snd_kcontrol_new rt5663_recmix1l[] = { + SOC_DAPM_SINGLE("BST2 Switch", RT5663_RECMIX1L, + RT5663_RECMIX1L_BST2_SHIFT, 1, 1), + SOC_DAPM_SINGLE("BST1 CBJ Switch", RT5663_RECMIX1L, + RT5663_RECMIX1L_BST1_CBJ_SHIFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5663_recmix1r[] = { + SOC_DAPM_SINGLE("BST2 Switch", RT5663_RECMIX1R, + RT5663_RECMIX1R_BST2_SHIFT, 1, 1), +}; + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt5663_sto1_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5663_STO1_ADC_MIXER, + RT5663_M_STO1_ADC_L1_SHIFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5663_STO1_ADC_MIXER, + RT5663_M_STO1_ADC_L2_SHIFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5663_sto1_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5663_STO1_ADC_MIXER, + RT5663_M_STO1_ADC_R1_SHIFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5663_STO1_ADC_MIXER, + RT5663_M_STO1_ADC_R2_SHIFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5663_adda_l_mix[] = { + SOC_DAPM_SINGLE("ADC L Switch", RT5663_AD_DA_MIXER, + RT5663_M_ADCMIX_L_SHIFT, 1, 1), + SOC_DAPM_SINGLE("DAC L Switch", RT5663_AD_DA_MIXER, + RT5663_M_DAC1_L_SHIFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5663_adda_r_mix[] = { + SOC_DAPM_SINGLE("ADC R Switch", RT5663_AD_DA_MIXER, + RT5663_M_ADCMIX_R_SHIFT, 1, 1), + SOC_DAPM_SINGLE("DAC R Switch", RT5663_AD_DA_MIXER, + RT5663_M_DAC1_R_SHIFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5663_sto1_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L Switch", RT5663_STO_DAC_MIXER, + RT5663_M_DAC_L1_STO_L_SHIFT, 1, 1), + SOC_DAPM_SINGLE("DAC R Switch", RT5663_STO_DAC_MIXER, + RT5663_M_DAC_R1_STO_L_SHIFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5663_sto1_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC L Switch", RT5663_STO_DAC_MIXER, + RT5663_M_DAC_L1_STO_R_SHIFT, 1, 1), + SOC_DAPM_SINGLE("DAC R Switch", RT5663_STO_DAC_MIXER, + RT5663_M_DAC_R1_STO_R_SHIFT, 1, 1), +}; + +/* Out Switch */ +static const struct snd_kcontrol_new rt5663_hpo_switch = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5663_HP_AMP_2, + RT5663_EN_DAC_HPO_SHIFT, 1, 0); + +/* Stereo ADC source */ +static const char * const rt5663_sto1_adc_src[] = { + "ADC L", "ADC R" +}; + +static SOC_ENUM_SINGLE_DECL(rt5663_sto1_adcl_enum, RT5663_STO1_ADC_MIXER, + RT5663_STO1_ADC_L_SRC_SHIFT, rt5663_sto1_adc_src); + +static const struct snd_kcontrol_new rt5663_sto1_adcl_mux = + SOC_DAPM_ENUM("STO1 ADC L Mux", rt5663_sto1_adcl_enum); + +static SOC_ENUM_SINGLE_DECL(rt5663_sto1_adcr_enum, RT5663_STO1_ADC_MIXER, + RT5663_STO1_ADC_R_SRC_SHIFT, rt5663_sto1_adc_src); + +static const struct snd_kcontrol_new rt5663_sto1_adcr_mux = + SOC_DAPM_ENUM("STO1 ADC R Mux", rt5663_sto1_adcr_enum); + +/* RT5663: Analog DACL1 input source */ +static const char * const rt5663_alg_dacl_src[] = { + "DAC L", "STO DAC MIXL" +}; + +static SOC_ENUM_SINGLE_DECL(rt5663_alg_dacl_enum, RT5663_BYPASS_STO_DAC, + RT5663_DACL1_SRC_SHIFT, rt5663_alg_dacl_src); + +static const struct snd_kcontrol_new rt5663_alg_dacl_mux = + SOC_DAPM_ENUM("DAC L Mux", rt5663_alg_dacl_enum); + +/* RT5663: Analog DACR1 input source */ +static const char * const rt5663_alg_dacr_src[] = { + "DAC R", "STO DAC MIXR" +}; + +static SOC_ENUM_SINGLE_DECL(rt5663_alg_dacr_enum, RT5663_BYPASS_STO_DAC, + RT5663_DACR1_SRC_SHIFT, rt5663_alg_dacr_src); + +static const struct snd_kcontrol_new rt5663_alg_dacr_mux = + SOC_DAPM_ENUM("DAC R Mux", rt5663_alg_dacr_enum); + +static int rt5663_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (rt5663->codec_ver == CODEC_VER_1) { + snd_soc_update_bits(codec, RT5663_HP_CHARGE_PUMP_1, + RT5663_SEL_PM_HP_SHIFT, RT5663_SEL_PM_HP_HIGH); + snd_soc_update_bits(codec, RT5663_HP_LOGIC_2, + RT5663_HP_SIG_SRC1_MASK, + RT5663_HP_SIG_SRC1_SILENCE); + } else { + snd_soc_write(codec, RT5663_DEPOP_2, 0x3003); + snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x000b, + 0x000b); + snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0030, + 0x0030); + snd_soc_update_bits(codec, RT5663_HP_CHARGE_PUMP_1, + RT5663_OVCD_HP_MASK, RT5663_OVCD_HP_DIS); + snd_soc_write(codec, RT5663_HP_CHARGE_PUMP_2, 0x1371); + snd_soc_write(codec, RT5663_HP_BIAS, 0xabba); + snd_soc_write(codec, RT5663_CHARGE_PUMP_1, 0x2224); + snd_soc_write(codec, RT5663_ANA_BIAS_CUR_1, 0x7766); + snd_soc_write(codec, RT5663_HP_BIAS, 0xafaa); + snd_soc_write(codec, RT5663_CHARGE_PUMP_2, 0x7777); + snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x3000, + 0x3000); + } + break; + + case SND_SOC_DAPM_PRE_PMD: + if (rt5663->codec_ver == CODEC_VER_1) { + snd_soc_update_bits(codec, RT5663_HP_LOGIC_2, + RT5663_HP_SIG_SRC1_MASK, + RT5663_HP_SIG_SRC1_REG); + } else { + snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x3000, 0x0); + snd_soc_update_bits(codec, RT5663_HP_CHARGE_PUMP_1, + RT5663_OVCD_HP_MASK, RT5663_OVCD_HP_EN); + snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0030, 0x0); + snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x000b, + 0x000b); + } + break; + + default: + return 0; + } + + return 0; +} + +static int rt5663_bst2_power(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, RT5663_PWR_ANLG_2, + RT5663_PWR_BST2_MASK | RT5663_PWR_BST2_OP_MASK, + RT5663_PWR_BST2 | RT5663_PWR_BST2_OP); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, RT5663_PWR_ANLG_2, + RT5663_PWR_BST2_MASK | RT5663_PWR_BST2_OP_MASK, 0); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5663_pre_div_power(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_write(codec, RT5663_PRE_DIV_GATING_1, 0xff00); + snd_soc_write(codec, RT5663_PRE_DIV_GATING_2, 0xfffc); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_write(codec, RT5663_PRE_DIV_GATING_1, 0x0000); + snd_soc_write(codec, RT5663_PRE_DIV_GATING_2, 0x0000); + break; + + default: + return 0; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt5663_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("PLL", RT5663_PWR_ANLG_3, RT5663_PWR_PLL_SHIFT, 0, + NULL, 0), + + /* micbias */ + SND_SOC_DAPM_MICBIAS("MICBIAS1", RT5663_PWR_ANLG_2, + RT5663_PWR_MB1_SHIFT, 0), + SND_SOC_DAPM_MICBIAS("MICBIAS2", RT5663_PWR_ANLG_2, + RT5663_PWR_MB2_SHIFT, 0), + + /* Input Lines */ + SND_SOC_DAPM_INPUT("IN1P"), + SND_SOC_DAPM_INPUT("IN1N"), + + /* REC Mixer Power */ + SND_SOC_DAPM_SUPPLY("RECMIX1L Power", RT5663_PWR_ANLG_2, + RT5663_PWR_RECMIX1_SHIFT, 0, NULL, 0), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SUPPLY("ADC L Power", RT5663_PWR_DIG_1, + RT5663_PWR_ADC_L1_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC Clock", RT5663_CHOP_ADC, + RT5663_CKGEN_ADCC_SHIFT, 0, NULL, 0), + + /* ADC Mixer */ + SND_SOC_DAPM_MIXER("STO1 ADC MIXL", SND_SOC_NOPM, + 0, 0, rt5663_sto1_adc_l_mix, + ARRAY_SIZE(rt5663_sto1_adc_l_mix)), + + /* ADC Filter Power */ + SND_SOC_DAPM_SUPPLY("STO1 ADC Filter", RT5663_PWR_DIG_2, + RT5663_PWR_ADC_S1F_SHIFT, 0, NULL, 0), + + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("I2S", RT5663_PWR_DIG_1, RT5663_PWR_I2S1_SHIFT, 0, + NULL, 0), + SND_SOC_DAPM_PGA("IF DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIFRX", "AIF Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIFTX", "AIF Capture", 0, SND_SOC_NOPM, 0, 0), + + /* DAC mixer before sound effect */ + SND_SOC_DAPM_MIXER("ADDA MIXL", SND_SOC_NOPM, 0, 0, rt5663_adda_l_mix, + ARRAY_SIZE(rt5663_adda_l_mix)), + SND_SOC_DAPM_MIXER("ADDA MIXR", SND_SOC_NOPM, 0, 0, rt5663_adda_r_mix, + ARRAY_SIZE(rt5663_adda_r_mix)), + SND_SOC_DAPM_PGA("DAC L1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DAC R1", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* DAC Mixer */ + SND_SOC_DAPM_SUPPLY("STO1 DAC Filter", RT5663_PWR_DIG_2, + RT5663_PWR_DAC_S1F_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("STO1 DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5663_sto1_dac_l_mix, ARRAY_SIZE(rt5663_sto1_dac_l_mix)), + SND_SOC_DAPM_MIXER("STO1 DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5663_sto1_dac_r_mix, ARRAY_SIZE(rt5663_sto1_dac_r_mix)), + + /* DACs */ + SND_SOC_DAPM_SUPPLY("STO1 DAC L Power", RT5663_PWR_DIG_1, + RT5663_PWR_DAC_L1_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("STO1 DAC R Power", RT5663_PWR_DIG_1, + RT5663_PWR_DAC_R1_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_DAC("DAC L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC R", NULL, SND_SOC_NOPM, 0, 0), + + /* Headphone*/ + SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5663_hp_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), +}; + +static const struct snd_soc_dapm_widget rt5663_v2_specific_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("LDO2", RT5663_PWR_ANLG_3, + RT5663_PWR_LDO2_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Mic Det Power", RT5663_PWR_VOL, + RT5663_V2_PWR_MIC_DET_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("LDO DAC", RT5663_PWR_DIG_1, + RT5663_PWR_LDO_DACREF_SHIFT, 0, NULL, 0), + + /* ASRC */ + SND_SOC_DAPM_SUPPLY("I2S ASRC", RT5663_ASRC_1, + RT5663_I2S1_ASRC_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC ASRC", RT5663_ASRC_1, + RT5663_DAC_STO1_ASRC_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC ASRC", RT5663_ASRC_1, + RT5663_ADC_STO1_ASRC_SHIFT, 0, NULL, 0), + + /* Input Lines */ + SND_SOC_DAPM_INPUT("IN2P"), + SND_SOC_DAPM_INPUT("IN2N"), + + /* Boost */ + SND_SOC_DAPM_PGA("BST1 CBJ", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CBJ Power", RT5663_PWR_ANLG_3, + RT5663_PWR_CBJ_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("BST2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BST2 Power", SND_SOC_NOPM, 0, 0, + rt5663_bst2_power, SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMU), + + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIX1L", SND_SOC_NOPM, 0, 0, rt5663_recmix1l, + ARRAY_SIZE(rt5663_recmix1l)), + SND_SOC_DAPM_MIXER("RECMIX1R", SND_SOC_NOPM, 0, 0, rt5663_recmix1r, + ARRAY_SIZE(rt5663_recmix1r)), + SND_SOC_DAPM_SUPPLY("RECMIX1R Power", RT5663_PWR_ANLG_2, + RT5663_PWR_RECMIX2_SHIFT, 0, NULL, 0), + + /* ADC */ + SND_SOC_DAPM_ADC("ADC R", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SUPPLY("ADC R Power", RT5663_PWR_DIG_1, + RT5663_PWR_ADC_R1_SHIFT, 0, NULL, 0), + + /* ADC Mux */ + SND_SOC_DAPM_PGA("STO1 ADC L1", RT5663_STO1_ADC_MIXER, + RT5663_STO1_ADC_L1_SRC_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("STO1 ADC R1", RT5663_STO1_ADC_MIXER, + RT5663_STO1_ADC_R1_SRC_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("STO1 ADC L2", RT5663_STO1_ADC_MIXER, + RT5663_STO1_ADC_L2_SRC_SHIFT, 1, NULL, 0), + SND_SOC_DAPM_PGA("STO1 ADC R2", RT5663_STO1_ADC_MIXER, + RT5663_STO1_ADC_R2_SRC_SHIFT, 1, NULL, 0), + + SND_SOC_DAPM_MUX("STO1 ADC L Mux", SND_SOC_NOPM, 0, 0, + &rt5663_sto1_adcl_mux), + SND_SOC_DAPM_MUX("STO1 ADC R Mux", SND_SOC_NOPM, 0, 0, + &rt5663_sto1_adcr_mux), + + /* ADC Mix */ + SND_SOC_DAPM_MIXER("STO1 ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5663_sto1_adc_r_mix, ARRAY_SIZE(rt5663_sto1_adc_r_mix)), + + /* Analog DAC Clock */ + SND_SOC_DAPM_SUPPLY("DAC Clock", RT5663_CHOP_DAC_L, + RT5663_CKGEN_DAC1_SHIFT, 0, NULL, 0), + + /* Headphone out */ + SND_SOC_DAPM_SWITCH("HPO Playback", SND_SOC_NOPM, 0, 0, + &rt5663_hpo_switch), +}; + +static const struct snd_soc_dapm_widget rt5663_specific_dapm_widgets[] = { + /* System Clock Pre Divider Gating */ + SND_SOC_DAPM_SUPPLY("Pre Div Power", SND_SOC_NOPM, 0, 0, + rt5663_pre_div_power, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD), + + /* LDO */ + SND_SOC_DAPM_SUPPLY("LDO ADC", RT5663_PWR_DIG_1, + RT5663_PWR_LDO_DACREF_SHIFT, 0, NULL, 0), + + /* ASRC */ + SND_SOC_DAPM_SUPPLY("I2S ASRC", RT5663_ASRC_1, + RT5663_I2S1_ASRC_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC ASRC", RT5663_ASRC_1, + RT5663_DAC_STO1_ASRC_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC ASRC", RT5663_ASRC_1, + RT5663_ADC_STO1_ASRC_SHIFT, 0, NULL, 0), + + /* Boost */ + SND_SOC_DAPM_PGA("BST1", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* STO ADC */ + SND_SOC_DAPM_PGA("STO1 ADC L1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("STO1 ADC L2", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Analog DAC source */ + SND_SOC_DAPM_MUX("DAC L Mux", SND_SOC_NOPM, 0, 0, &rt5663_alg_dacl_mux), + SND_SOC_DAPM_MUX("DAC R Mux", SND_SOC_NOPM, 0, 0, &rt5663_alg_dacr_mux), +}; + +static const struct snd_soc_dapm_route rt5663_dapm_routes[] = { + /* PLL */ + { "I2S", NULL, "PLL", rt5663_is_sys_clk_from_pll }, + + /* ASRC */ + { "STO1 ADC Filter", NULL, "ADC ASRC", rt5663_is_using_asrc }, + { "STO1 DAC Filter", NULL, "DAC ASRC", rt5663_is_using_asrc }, + { "I2S", NULL, "I2S ASRC", rt5663_i2s_use_asrc }, + + { "ADC L", NULL, "ADC L Power" }, + { "ADC L", NULL, "ADC Clock" }, + + { "STO1 ADC L2", NULL, "STO1 DAC MIXL" }, + + { "STO1 ADC MIXL", "ADC1 Switch", "STO1 ADC L1" }, + { "STO1 ADC MIXL", "ADC2 Switch", "STO1 ADC L2" }, + { "STO1 ADC MIXL", NULL, "STO1 ADC Filter" }, + + { "IF1 ADC1", NULL, "STO1 ADC MIXL" }, + { "IF ADC", NULL, "IF1 ADC1" }, + { "AIFTX", NULL, "IF ADC" }, + { "AIFTX", NULL, "I2S" }, + + { "AIFRX", NULL, "I2S" }, + { "IF DAC", NULL, "AIFRX" }, + { "IF1 DAC1 L", NULL, "IF DAC" }, + { "IF1 DAC1 R", NULL, "IF DAC" }, + + { "ADDA MIXL", "ADC L Switch", "STO1 ADC MIXL" }, + { "ADDA MIXL", "DAC L Switch", "IF1 DAC1 L" }, + { "ADDA MIXL", NULL, "STO1 DAC Filter" }, + { "ADDA MIXL", NULL, "STO1 DAC L Power" }, + { "ADDA MIXR", "DAC R Switch", "IF1 DAC1 R" }, + { "ADDA MIXR", NULL, "STO1 DAC Filter" }, + { "ADDA MIXR", NULL, "STO1 DAC R Power" }, + + { "DAC L1", NULL, "ADDA MIXL" }, + { "DAC R1", NULL, "ADDA MIXR" }, + + { "STO1 DAC MIXL", "DAC L Switch", "DAC L1" }, + { "STO1 DAC MIXL", "DAC R Switch", "DAC R1" }, + { "STO1 DAC MIXL", NULL, "STO1 DAC L Power" }, + { "STO1 DAC MIXL", NULL, "STO1 DAC Filter" }, + { "STO1 DAC MIXR", "DAC R Switch", "DAC R1" }, + { "STO1 DAC MIXR", "DAC L Switch", "DAC L1" }, + { "STO1 DAC MIXR", NULL, "STO1 DAC R Power" }, + { "STO1 DAC MIXR", NULL, "STO1 DAC Filter" }, + + { "HP Amp", NULL, "DAC L" }, + { "HP Amp", NULL, "DAC R" }, +}; + +static const struct snd_soc_dapm_route rt5663_v2_specific_dapm_routes[] = { + { "MICBIAS1", NULL, "LDO2" }, + { "MICBIAS2", NULL, "LDO2" }, + + { "BST1 CBJ", NULL, "IN1P" }, + { "BST1 CBJ", NULL, "IN1N" }, + { "BST1 CBJ", NULL, "CBJ Power" }, + + { "BST2", NULL, "IN2P" }, + { "BST2", NULL, "IN2N" }, + { "BST2", NULL, "BST2 Power" }, + + { "RECMIX1L", "BST2 Switch", "BST2" }, + { "RECMIX1L", "BST1 CBJ Switch", "BST1 CBJ" }, + { "RECMIX1L", NULL, "RECMIX1L Power" }, + { "RECMIX1R", "BST2 Switch", "BST2" }, + { "RECMIX1R", NULL, "RECMIX1R Power" }, + + { "ADC L", NULL, "RECMIX1L" }, + { "ADC R", NULL, "RECMIX1R" }, + { "ADC R", NULL, "ADC R Power" }, + { "ADC R", NULL, "ADC Clock" }, + + { "STO1 ADC L Mux", "ADC L", "ADC L" }, + { "STO1 ADC L Mux", "ADC R", "ADC R" }, + { "STO1 ADC L1", NULL, "STO1 ADC L Mux" }, + + { "STO1 ADC R Mux", "ADC L", "ADC L" }, + { "STO1 ADC R Mux", "ADC R", "ADC R" }, + { "STO1 ADC R1", NULL, "STO1 ADC R Mux" }, + { "STO1 ADC R2", NULL, "STO1 DAC MIXR" }, + + { "STO1 ADC MIXR", "ADC1 Switch", "STO1 ADC R1" }, + { "STO1 ADC MIXR", "ADC2 Switch", "STO1 ADC R2" }, + { "STO1 ADC MIXR", NULL, "STO1 ADC Filter" }, + + { "IF1 ADC1", NULL, "STO1 ADC MIXR" }, + + { "ADDA MIXR", "ADC R Switch", "STO1 ADC MIXR" }, + + { "DAC L", NULL, "STO1 DAC MIXL" }, + { "DAC L", NULL, "LDO DAC" }, + { "DAC L", NULL, "DAC Clock" }, + { "DAC R", NULL, "STO1 DAC MIXR" }, + { "DAC R", NULL, "LDO DAC" }, + { "DAC R", NULL, "DAC Clock" }, + + { "HPO Playback", "Switch", "HP Amp" }, + { "HPOL", NULL, "HPO Playback" }, + { "HPOR", NULL, "HPO Playback" }, +}; + +static const struct snd_soc_dapm_route rt5663_specific_dapm_routes[] = { + { "I2S", NULL, "Pre Div Power" }, + + { "BST1", NULL, "IN1P" }, + { "BST1", NULL, "IN1N" }, + { "BST1", NULL, "RECMIX1L Power" }, + + { "ADC L", NULL, "BST1" }, + + { "STO1 ADC L1", NULL, "ADC L" }, + + { "DAC L Mux", "DAC L", "DAC L1" }, + { "DAC L Mux", "STO DAC MIXL", "STO1 DAC MIXL" }, + { "DAC R Mux", "DAC R", "DAC R1"}, + { "DAC R Mux", "STO DAC MIXR", "STO1 DAC MIXR" }, + + { "DAC L", NULL, "DAC L Mux" }, + { "DAC R", NULL, "DAC R Mux" }, + + { "HPOL", NULL, "HP Amp" }, + { "HPOR", NULL, "HP Amp" }, +}; + +static int rt5663_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 rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec); + unsigned int val_len = 0; + int pre_div; + + rt5663->lrck = params_rate(params); + + dev_dbg(dai->dev, "bclk is %dHz and sysclk is %dHz\n", + rt5663->lrck, rt5663->sysclk); + + pre_div = rl6231_get_clk_info(rt5663->sysclk, rt5663->lrck); + if (pre_div < 0) { + dev_err(codec->dev, "Unsupported clock setting %d for DAI %d\n", + rt5663->lrck, dai->id); + return -EINVAL; + } + + dev_dbg(dai->dev, "pre_div is %d for iis %d\n", pre_div, dai->id); + + switch (params_width(params)) { + case 8: + val_len = RT5663_I2S_DL_8; + break; + case 16: + val_len = RT5663_I2S_DL_16; + break; + case 20: + val_len = RT5663_I2S_DL_20; + break; + case 24: + val_len = RT5663_I2S_DL_24; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, RT5663_I2S1_SDP, + RT5663_I2S_DL_MASK, val_len); + + snd_soc_update_bits(codec, RT5663_ADDA_CLK_1, + RT5663_I2S_PD1_MASK, pre_div << RT5663_I2S_PD1_SHIFT); + + return 0; +} + +static int rt5663_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + unsigned int reg_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + break; + case SND_SOC_DAIFMT_CBS_CFS: + reg_val |= RT5663_I2S_MS_S; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT5663_I2S_BP_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT5663_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5663_I2S_DF_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5663_I2S_DF_PCM_B; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, RT5663_I2S1_SDP, RT5663_I2S_MS_MASK | + RT5663_I2S_BP_MASK | RT5663_I2S_DF_MASK, reg_val); + + return 0; +} + +static int rt5663_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec); + unsigned int reg_val = 0; + + if (freq == rt5663->sysclk && clk_id == rt5663->sysclk_src) + return 0; + + switch (clk_id) { + case RT5663_SCLK_S_MCLK: + reg_val |= RT5663_SCLK_SRC_MCLK; + break; + case RT5663_SCLK_S_PLL1: + reg_val |= RT5663_SCLK_SRC_PLL1; + break; + case RT5663_SCLK_S_RCCLK: + reg_val |= RT5663_SCLK_SRC_RCCLK; + break; + default: + dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + snd_soc_update_bits(codec, RT5663_GLB_CLK, RT5663_SCLK_SRC_MASK, + reg_val); + rt5663->sysclk = freq; + rt5663->sysclk_src = clk_id; + + dev_dbg(codec->dev, "Sysclk is %dHz and clock id is %d\n", + freq, clk_id); + + return 0; +} + +static int rt5663_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec); + struct rl6231_pll_code pll_code; + int ret; + int mask, shift, val; + + if (source == rt5663->pll_src && freq_in == rt5663->pll_in && + freq_out == rt5663->pll_out) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(codec->dev, "PLL disabled\n"); + + rt5663->pll_in = 0; + rt5663->pll_out = 0; + snd_soc_update_bits(codec, RT5663_GLB_CLK, + RT5663_SCLK_SRC_MASK, RT5663_SCLK_SRC_MCLK); + return 0; + } + + switch (rt5663->codec_ver) { + case CODEC_VER_1: + mask = RT5663_V2_PLL1_SRC_MASK; + shift = RT5663_V2_PLL1_SRC_SHIFT; + break; + case CODEC_VER_0: + mask = RT5663_PLL1_SRC_MASK; + shift = RT5663_PLL1_SRC_SHIFT; + break; + default: + dev_err(codec->dev, "Unknown CODEC Version\n"); + return -EINVAL; + } + + switch (source) { + case RT5663_PLL1_S_MCLK: + val = 0x0; + break; + case RT5663_PLL1_S_BCLK1: + val = 0x1; + break; + default: + dev_err(codec->dev, "Unknown PLL source %d\n", source); + return -EINVAL; + } + snd_soc_update_bits(codec, RT5663_GLB_CLK, mask, (val << shift)); + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(codec->dev, "Unsupport input clock %d\n", freq_in); + return ret; + } + + dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n", pll_code.m_bp, + (pll_code.m_bp ? 0 : pll_code.m_code), pll_code.n_code, + pll_code.k_code); + + snd_soc_write(codec, RT5663_PLL_1, + pll_code.n_code << RT5663_PLL_N_SHIFT | pll_code.k_code); + snd_soc_write(codec, RT5663_PLL_2, + (pll_code.m_bp ? 0 : pll_code.m_code) << RT5663_PLL_M_SHIFT | + pll_code.m_bp << RT5663_PLL_M_BP_SHIFT); + + rt5663->pll_in = freq_in; + rt5663->pll_out = freq_out; + rt5663->pll_src = source; + + return 0; +} + +static int rt5663_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; + struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec); + unsigned int val = 0, reg; + + if (rx_mask || tx_mask) + val |= RT5663_TDM_MODE_TDM; + + switch (slots) { + case 4: + val |= RT5663_TDM_IN_CH_4; + val |= RT5663_TDM_OUT_CH_4; + break; + case 6: + val |= RT5663_TDM_IN_CH_6; + val |= RT5663_TDM_OUT_CH_6; + break; + case 8: + val |= RT5663_TDM_IN_CH_8; + val |= RT5663_TDM_OUT_CH_8; + break; + case 2: + break; + default: + return -EINVAL; + } + + switch (slot_width) { + case 20: + val |= RT5663_TDM_IN_LEN_20; + val |= RT5663_TDM_OUT_LEN_20; + break; + case 24: + val |= RT5663_TDM_IN_LEN_24; + val |= RT5663_TDM_OUT_LEN_24; + break; + case 32: + val |= RT5663_TDM_IN_LEN_32; + val |= RT5663_TDM_OUT_LEN_32; + break; + case 16: + break; + default: + return -EINVAL; + } + + switch (rt5663->codec_ver) { + case CODEC_VER_1: + reg = RT5663_TDM_2; + break; + case CODEC_VER_0: + reg = RT5663_TDM_1; + break; + default: + dev_err(codec->dev, "Unknown CODEC Version\n"); + return -EINVAL; + } + + snd_soc_update_bits(codec, reg, RT5663_TDM_MODE_MASK | + RT5663_TDM_IN_CH_MASK | RT5663_TDM_OUT_CH_MASK | + RT5663_TDM_IN_LEN_MASK | RT5663_TDM_OUT_LEN_MASK, val); + + return 0; +} + +static int rt5663_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec); + unsigned int reg; + + dev_dbg(codec->dev, "%s ratio = %d\n", __func__, ratio); + + if (rt5663->codec_ver == CODEC_VER_1) + reg = RT5663_TDM_9; + else + reg = RT5663_TDM_5; + + switch (ratio) { + case 32: + snd_soc_update_bits(codec, reg, + RT5663_TDM_LENGTN_MASK, + RT5663_TDM_LENGTN_16); + break; + case 40: + snd_soc_update_bits(codec, reg, + RT5663_TDM_LENGTN_MASK, + RT5663_TDM_LENGTN_20); + break; + case 48: + snd_soc_update_bits(codec, reg, + RT5663_TDM_LENGTN_MASK, + RT5663_TDM_LENGTN_24); + break; + case 64: + snd_soc_update_bits(codec, reg, + RT5663_TDM_LENGTN_MASK, + RT5663_TDM_LENGTN_32); + break; + default: + dev_err(codec->dev, "Invalid ratio!\n"); + return -EINVAL; + } + + return 0; +} + +static int rt5663_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec); + + switch (level) { + case SND_SOC_BIAS_ON: + snd_soc_update_bits(codec, RT5663_PWR_ANLG_1, + RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK, + RT5663_PWR_FV1 | RT5663_PWR_FV2); + break; + + case SND_SOC_BIAS_PREPARE: + if (rt5663->codec_ver == CODEC_VER_1) { + snd_soc_update_bits(codec, RT5663_DIG_MISC, + RT5663_DIG_GATE_CTRL_MASK, + RT5663_DIG_GATE_CTRL_EN); + snd_soc_update_bits(codec, RT5663_SIG_CLK_DET, + RT5663_EN_ANA_CLK_DET_MASK | + RT5663_PWR_CLK_DET_MASK, + RT5663_EN_ANA_CLK_DET_AUTO | + RT5663_PWR_CLK_DET_EN); + } + break; + + case SND_SOC_BIAS_STANDBY: + if (rt5663->codec_ver == CODEC_VER_1) + snd_soc_update_bits(codec, RT5663_DIG_MISC, + RT5663_DIG_GATE_CTRL_MASK, + RT5663_DIG_GATE_CTRL_DIS); + snd_soc_update_bits(codec, RT5663_PWR_ANLG_1, + RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK | + RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK | + RT5663_PWR_MB_MASK, RT5663_PWR_VREF1 | + RT5663_PWR_VREF2 | RT5663_PWR_MB); + usleep_range(10000, 10005); + if (rt5663->codec_ver == CODEC_VER_1) { + snd_soc_update_bits(codec, RT5663_SIG_CLK_DET, + RT5663_EN_ANA_CLK_DET_MASK | + RT5663_PWR_CLK_DET_MASK, + RT5663_EN_ANA_CLK_DET_DIS | + RT5663_PWR_CLK_DET_DIS); + } + break; + + case SND_SOC_BIAS_OFF: + snd_soc_update_bits(codec, RT5663_PWR_ANLG_1, + RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK | + RT5663_PWR_FV1 | RT5663_PWR_FV2, 0x0); + break; + + default: + break; + } + + return 0; +} + +static int rt5663_probe(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec); + + rt5663->codec = codec; + + switch (rt5663->codec_ver) { + case CODEC_VER_1: + snd_soc_dapm_new_controls(dapm, + rt5663_v2_specific_dapm_widgets, + ARRAY_SIZE(rt5663_v2_specific_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, + rt5663_v2_specific_dapm_routes, + ARRAY_SIZE(rt5663_v2_specific_dapm_routes)); + snd_soc_add_codec_controls(codec, rt5663_v2_specific_controls, + ARRAY_SIZE(rt5663_v2_specific_controls)); + break; + case CODEC_VER_0: + snd_soc_dapm_new_controls(dapm, + rt5663_specific_dapm_widgets, + ARRAY_SIZE(rt5663_specific_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, + rt5663_specific_dapm_routes, + ARRAY_SIZE(rt5663_specific_dapm_routes)); + snd_soc_add_codec_controls(codec, rt5663_specific_controls, + ARRAY_SIZE(rt5663_specific_controls)); + break; + } + + return 0; +} + +static int rt5663_remove(struct snd_soc_codec *codec) +{ + struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec); + + regmap_write(rt5663->regmap, RT5663_RESET, 0); + + return 0; +} + +#ifdef CONFIG_PM +static int rt5663_suspend(struct snd_soc_codec *codec) +{ + struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec); + + regcache_cache_only(rt5663->regmap, true); + regcache_mark_dirty(rt5663->regmap); + + return 0; +} + +static int rt5663_resume(struct snd_soc_codec *codec) +{ + struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec); + + regcache_cache_only(rt5663->regmap, false); + regcache_sync(rt5663->regmap); + + return 0; +} +#else +#define rt5663_suspend NULL +#define rt5663_resume NULL +#endif + +#define RT5663_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT5663_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static struct snd_soc_dai_ops rt5663_aif_dai_ops = { + .hw_params = rt5663_hw_params, + .set_fmt = rt5663_set_dai_fmt, + .set_sysclk = rt5663_set_dai_sysclk, + .set_pll = rt5663_set_dai_pll, + .set_tdm_slot = rt5663_set_tdm_slot, + .set_bclk_ratio = rt5663_set_bclk_ratio, +}; + +static struct snd_soc_dai_driver rt5663_dai[] = { + { + .name = "rt5663-aif", + .id = RT5663_AIF, + .playback = { + .stream_name = "AIF Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5663_STEREO_RATES, + .formats = RT5663_FORMATS, + }, + .capture = { + .stream_name = "AIF Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5663_STEREO_RATES, + .formats = RT5663_FORMATS, + }, + .ops = &rt5663_aif_dai_ops, + }, +}; + +static struct snd_soc_codec_driver soc_codec_dev_rt5663 = { + .probe = rt5663_probe, + .remove = rt5663_remove, + .suspend = rt5663_suspend, + .resume = rt5663_resume, + .set_bias_level = rt5663_set_bias_level, + .idle_bias_off = true, + .component_driver = { + .controls = rt5663_snd_controls, + .num_controls = ARRAY_SIZE(rt5663_snd_controls), + .dapm_widgets = rt5663_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5663_dapm_widgets), + .dapm_routes = rt5663_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5663_dapm_routes), + } +}; + +static const struct regmap_config rt5663_v2_regmap = { + .reg_bits = 16, + .val_bits = 16, + .use_single_rw = true, + .max_register = 0x07fa, + .volatile_reg = rt5663_v2_volatile_register, + .readable_reg = rt5663_v2_readable_register, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt5663_v2_reg, + .num_reg_defaults = ARRAY_SIZE(rt5663_v2_reg), +}; + +static const struct regmap_config rt5663_regmap = { + .reg_bits = 16, + .val_bits = 16, + .use_single_rw = true, + .max_register = 0x03f3, + .volatile_reg = rt5663_volatile_register, + .readable_reg = rt5663_readable_register, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt5663_reg, + .num_reg_defaults = ARRAY_SIZE(rt5663_reg), +}; + +static const struct regmap_config temp_regmap = { + .name = "nocache", + .reg_bits = 16, + .val_bits = 16, + .use_single_rw = true, + .max_register = 0x03f3, + .cache_type = REGCACHE_NONE, +}; + +static const struct i2c_device_id rt5663_i2c_id[] = { + { "rt5663", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, rt5663_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id rt5663_of_match[] = { + { .compatible = "realtek,rt5663", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rt5663_of_match); +#endif + +#ifdef CONFIG_ACPI +static struct acpi_device_id rt5663_acpi_match[] = { + { "10EC5663", 0}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, rt5663_acpi_match); +#endif + +static void rt5663_v2_calibrate(struct rt5663_priv *rt5663) +{ + regmap_write(rt5663->regmap, RT5663_BIAS_CUR_8, 0xa402); + regmap_write(rt5663->regmap, RT5663_PWR_DIG_1, 0x0100); + regmap_write(rt5663->regmap, RT5663_RECMIX, 0x4040); + regmap_write(rt5663->regmap, RT5663_DIG_MISC, 0x0001); + regmap_write(rt5663->regmap, RT5663_RC_CLK, 0x0380); + regmap_write(rt5663->regmap, RT5663_GLB_CLK, 0x8000); + regmap_write(rt5663->regmap, RT5663_ADDA_CLK_1, 0x1000); + regmap_write(rt5663->regmap, RT5663_CHOP_DAC_L, 0x3030); + regmap_write(rt5663->regmap, RT5663_CALIB_ADC, 0x3c05); + regmap_write(rt5663->regmap, RT5663_PWR_ANLG_1, 0xa23e); + msleep(40); + regmap_write(rt5663->regmap, RT5663_PWR_ANLG_1, 0xf23e); + regmap_write(rt5663->regmap, RT5663_HP_CALIB_2, 0x0321); + regmap_write(rt5663->regmap, RT5663_HP_CALIB_1, 0xfc00); + msleep(500); +} + +static void rt5663_calibrate(struct rt5663_priv *rt5663) +{ + int value, count; + + regmap_write(rt5663->regmap, RT5663_RC_CLK, 0x0280); + regmap_write(rt5663->regmap, RT5663_GLB_CLK, 0x8000); + regmap_write(rt5663->regmap, RT5663_DIG_MISC, 0x8001); + regmap_write(rt5663->regmap, RT5663_VREF_RECMIX, 0x0032); + regmap_write(rt5663->regmap, RT5663_PWR_ANLG_1, 0xa2be); + msleep(20); + regmap_write(rt5663->regmap, RT5663_PWR_ANLG_1, 0xf2be); + regmap_write(rt5663->regmap, RT5663_PWR_DIG_2, 0x8400); + regmap_write(rt5663->regmap, RT5663_CHOP_ADC, 0x3000); + regmap_write(rt5663->regmap, RT5663_DEPOP_1, 0x003b); + regmap_write(rt5663->regmap, RT5663_PWR_DIG_1, 0x8df8); + regmap_write(rt5663->regmap, RT5663_PWR_ANLG_2, 0x0003); + regmap_write(rt5663->regmap, RT5663_PWR_ANLG_3, 0x018c); + regmap_write(rt5663->regmap, RT5663_ADDA_CLK_1, 0x1111); + regmap_write(rt5663->regmap, RT5663_PRE_DIV_GATING_1, 0xffff); + regmap_write(rt5663->regmap, RT5663_PRE_DIV_GATING_2, 0xffff); + regmap_write(rt5663->regmap, RT5663_DEPOP_2, 0x3003); + regmap_write(rt5663->regmap, RT5663_DEPOP_1, 0x003b); + regmap_write(rt5663->regmap, RT5663_HP_CHARGE_PUMP_1, 0x1e32); + regmap_write(rt5663->regmap, RT5663_HP_CHARGE_PUMP_2, 0x1371); + regmap_write(rt5663->regmap, RT5663_DACREF_LDO, 0x3b0b); + regmap_write(rt5663->regmap, RT5663_STO_DAC_MIXER, 0x2080); + regmap_write(rt5663->regmap, RT5663_BYPASS_STO_DAC, 0x000c); + regmap_write(rt5663->regmap, RT5663_HP_BIAS, 0xabba); + regmap_write(rt5663->regmap, RT5663_CHARGE_PUMP_1, 0x2224); + regmap_write(rt5663->regmap, RT5663_HP_OUT_EN, 0x8088); + regmap_write(rt5663->regmap, RT5663_STO_DRE_9, 0x0017); + regmap_write(rt5663->regmap, RT5663_STO_DRE_10, 0x0017); + regmap_write(rt5663->regmap, RT5663_STO1_ADC_MIXER, 0x4040); + regmap_write(rt5663->regmap, RT5663_RECMIX, 0x0005); + regmap_write(rt5663->regmap, RT5663_ADDA_RST, 0xc000); + regmap_write(rt5663->regmap, RT5663_STO1_HPF_ADJ1, 0x3320); + regmap_write(rt5663->regmap, RT5663_HP_CALIB_2, 0x00c9); + regmap_write(rt5663->regmap, RT5663_DUMMY_1, 0x004c); + regmap_write(rt5663->regmap, RT5663_ANA_BIAS_CUR_1, 0x7766); + regmap_write(rt5663->regmap, RT5663_BIAS_CUR_8, 0x4702); + msleep(200); + regmap_write(rt5663->regmap, RT5663_HP_CALIB_1, 0x0069); + regmap_write(rt5663->regmap, RT5663_HP_CALIB_3, 0x06c2); + regmap_write(rt5663->regmap, RT5663_HP_CALIB_1_1, 0x7b00); + regmap_write(rt5663->regmap, RT5663_HP_CALIB_1_1, 0xfb00); + count = 0; + while (true) { + regmap_read(rt5663->regmap, RT5663_HP_CALIB_1_1, &value); + if (value & 0x8000) + usleep_range(10000, 10005); + else + break; + + if (count > 200) + return; + count++; + } +} + +static int rt5663_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct rt5663_priv *rt5663; + int ret; + unsigned int val; + struct regmap *regmap; + + rt5663 = devm_kzalloc(&i2c->dev, sizeof(struct rt5663_priv), + GFP_KERNEL); + + if (rt5663 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt5663); + + regmap = devm_regmap_init_i2c(i2c, &temp_regmap); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(&i2c->dev, "Failed to allocate temp register map: %d\n", + ret); + return ret; + } + regmap_read(regmap, RT5663_VENDOR_ID_2, &val); + switch (val) { + case RT5663_DEVICE_ID_2: + rt5663->regmap = devm_regmap_init_i2c(i2c, &rt5663_v2_regmap); + rt5663->codec_ver = CODEC_VER_1; + break; + case RT5663_DEVICE_ID_1: + rt5663->regmap = devm_regmap_init_i2c(i2c, &rt5663_regmap); + rt5663->codec_ver = CODEC_VER_0; + break; + default: + dev_err(&i2c->dev, + "Device with ID register %#x is not rt5663\n", + val); + return -ENODEV; + } + + if (IS_ERR(rt5663->regmap)) { + ret = PTR_ERR(rt5663->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + /* reset and calibrate */ + regmap_write(rt5663->regmap, RT5663_RESET, 0); + regcache_cache_bypass(rt5663->regmap, true); + switch (rt5663->codec_ver) { + case CODEC_VER_1: + rt5663_v2_calibrate(rt5663); + break; + case CODEC_VER_0: + rt5663_calibrate(rt5663); + break; + default: + dev_err(&i2c->dev, "%s:Unknown codec type\n", __func__); + } + regcache_cache_bypass(rt5663->regmap, false); + regmap_write(rt5663->regmap, RT5663_RESET, 0); + dev_dbg(&i2c->dev, "calibrate done\n"); + + /* GPIO1 as IRQ */ + regmap_update_bits(rt5663->regmap, RT5663_GPIO_1, RT5663_GP1_PIN_MASK, + RT5663_GP1_PIN_IRQ); + /* 4btn inline command debounce */ + regmap_update_bits(rt5663->regmap, RT5663_IL_CMD_5, + RT5663_4BTN_CLK_DEB_MASK, RT5663_4BTN_CLK_DEB_65MS); + + switch (rt5663->codec_ver) { + case CODEC_VER_1: + regmap_write(rt5663->regmap, RT5663_BIAS_CUR_8, 0xa402); + /* JD1 */ + regmap_update_bits(rt5663->regmap, RT5663_AUTO_1MRC_CLK, + RT5663_IRQ_POW_SAV_MASK | RT5663_IRQ_POW_SAV_JD1_MASK, + RT5663_IRQ_POW_SAV_EN | RT5663_IRQ_POW_SAV_JD1_EN); + regmap_update_bits(rt5663->regmap, RT5663_PWR_ANLG_2, + RT5663_PWR_JD1_MASK, RT5663_PWR_JD1); + regmap_update_bits(rt5663->regmap, RT5663_IRQ_1, + RT5663_EN_CB_JD_MASK, RT5663_EN_CB_JD_EN); + + regmap_update_bits(rt5663->regmap, RT5663_HP_LOGIC_2, + RT5663_HP_SIG_SRC1_MASK, RT5663_HP_SIG_SRC1_REG); + regmap_update_bits(rt5663->regmap, RT5663_RECMIX, + RT5663_VREF_BIAS_MASK | RT5663_CBJ_DET_MASK | + RT5663_DET_TYPE_MASK, RT5663_VREF_BIAS_REG | + RT5663_CBJ_DET_EN | RT5663_DET_TYPE_QFN); + /* Set GPIO4 and GPIO8 as input for combo jack */ + regmap_update_bits(rt5663->regmap, RT5663_GPIO_2, + RT5663_GP4_PIN_CONF_MASK, RT5663_GP4_PIN_CONF_INPUT); + regmap_update_bits(rt5663->regmap, RT5663_GPIO_3, + RT5663_GP8_PIN_CONF_MASK, RT5663_GP8_PIN_CONF_INPUT); + regmap_update_bits(rt5663->regmap, RT5663_PWR_ANLG_1, + RT5663_LDO1_DVO_MASK | RT5663_AMP_HP_MASK, + RT5663_LDO1_DVO_0_9V | RT5663_AMP_HP_3X); + break; + case CODEC_VER_0: + regmap_update_bits(rt5663->regmap, RT5663_DIG_MISC, + RT5663_DIG_GATE_CTRL_MASK, RT5663_DIG_GATE_CTRL_EN); + regmap_update_bits(rt5663->regmap, RT5663_AUTO_1MRC_CLK, + RT5663_IRQ_POW_SAV_MASK, RT5663_IRQ_POW_SAV_EN); + regmap_update_bits(rt5663->regmap, RT5663_IRQ_1, + RT5663_EN_IRQ_JD1_MASK, RT5663_EN_IRQ_JD1_EN); + regmap_update_bits(rt5663->regmap, RT5663_GPIO_1, + RT5663_GPIO1_TYPE_MASK, RT5663_GPIO1_TYPE_EN); + regmap_write(rt5663->regmap, RT5663_VREF_RECMIX, 0x0032); + regmap_write(rt5663->regmap, RT5663_PWR_ANLG_1, 0xa2be); + msleep(20); + regmap_write(rt5663->regmap, RT5663_PWR_ANLG_1, 0xf2be); + regmap_update_bits(rt5663->regmap, RT5663_GPIO_2, + RT5663_GP1_PIN_CONF_MASK | RT5663_SEL_GPIO1_MASK, + RT5663_GP1_PIN_CONF_OUTPUT | RT5663_SEL_GPIO1_EN); + /* DACREF LDO control */ + regmap_update_bits(rt5663->regmap, RT5663_DACREF_LDO, 0x3e0e, + 0x3a0a); + regmap_update_bits(rt5663->regmap, RT5663_RECMIX, + RT5663_RECMIX1_BST1_MASK, RT5663_RECMIX1_BST1_ON); + regmap_update_bits(rt5663->regmap, RT5663_TDM_2, + RT5663_DATA_SWAP_ADCDAT1_MASK, + RT5663_DATA_SWAP_ADCDAT1_LL); + break; + default: + dev_err(&i2c->dev, "%s:Unknown codec type\n", __func__); + } + + INIT_DELAYED_WORK(&rt5663->jack_detect_work, rt5663_jack_detect_work); + + if (i2c->irq) { + ret = request_irq(i2c->irq, rt5663_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING + | IRQF_ONESHOT, "rt5663", rt5663); + if (ret) + dev_err(&i2c->dev, "%s Failed to reguest IRQ: %d\n", + __func__, ret); + } + + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5663, + rt5663_dai, ARRAY_SIZE(rt5663_dai)); + + if (ret) { + if (i2c->irq) + free_irq(i2c->irq, rt5663); + } + + return ret; +} + +static int rt5663_i2c_remove(struct i2c_client *i2c) +{ + struct rt5663_priv *rt5663 = i2c_get_clientdata(i2c); + + if (i2c->irq) + free_irq(i2c->irq, rt5663); + + snd_soc_unregister_codec(&i2c->dev); + + return 0; +} + +static void rt5663_i2c_shutdown(struct i2c_client *client) +{ + struct rt5663_priv *rt5663 = i2c_get_clientdata(client); + + regmap_write(rt5663->regmap, RT5663_RESET, 0); +} + +static struct i2c_driver rt5663_i2c_driver = { + .driver = { + .name = "rt5663", + .acpi_match_table = ACPI_PTR(rt5663_acpi_match), + .of_match_table = of_match_ptr(rt5663_of_match), + }, + .probe = rt5663_i2c_probe, + .remove = rt5663_i2c_remove, + .shutdown = rt5663_i2c_shutdown, + .id_table = rt5663_i2c_id, +}; +module_i2c_driver(rt5663_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5663 driver"); +MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5663.h b/sound/soc/codecs/rt5663.h new file mode 100644 index 000000000000..d77fae619f2f --- /dev/null +++ b/sound/soc/codecs/rt5663.h @@ -0,0 +1,1123 @@ +/* + * rt5663.h -- RT5663 ALSA SoC audio driver + * + * Copyright 2016 Realtek Microelectronics + * Author: Jack Yu <jack.yu@realtek.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 __RT5663_H__ +#define __RT5663_H__ + +/* Info */ +#define RT5663_RESET 0x0000 +#define RT5663_VENDOR_ID 0x00fd +#define RT5663_VENDOR_ID_1 0x00fe +#define RT5663_VENDOR_ID_2 0x00ff + +#define RT5663_LOUT_CTRL 0x0001 +#define RT5663_HP_AMP_2 0x0003 +#define RT5663_MONO_OUT 0x0004 +#define RT5663_MONO_GAIN 0x0007 + +#define RT5663_AEC_BST 0x000b +#define RT5663_IN1_IN2 0x000c +#define RT5663_IN3_IN4 0x000d +#define RT5663_INL1_INR1 0x000f +#define RT5663_CBJ_TYPE_2 0x0011 +#define RT5663_CBJ_TYPE_3 0x0012 +#define RT5663_CBJ_TYPE_4 0x0013 +#define RT5663_CBJ_TYPE_5 0x0014 +#define RT5663_CBJ_TYPE_8 0x0017 + +/* I/O - ADC/DAC/DMIC */ +#define RT5663_DAC3_DIG_VOL 0x001a +#define RT5663_DAC3_CTRL 0x001b +#define RT5663_MONO_ADC_DIG_VOL 0x001d +#define RT5663_STO2_ADC_DIG_VOL 0x001e +#define RT5663_MONO_ADC_BST_GAIN 0x0020 +#define RT5663_STO2_ADC_BST_GAIN 0x0021 +#define RT5663_SIDETONE_CTRL 0x0024 +/* Mixer - D-D */ +#define RT5663_MONO1_ADC_MIXER 0x0027 +#define RT5663_STO2_ADC_MIXER 0x0028 +#define RT5663_MONO_DAC_MIXER 0x002b +#define RT5663_DAC2_SRC_CTRL 0x002e +#define RT5663_IF_3_4_DATA_CTL 0x002f +#define RT5663_IF_5_DATA_CTL 0x0030 +#define RT5663_PDM_OUT_CTL 0x0031 +#define RT5663_PDM_I2C_DATA_CTL1 0x0032 +#define RT5663_PDM_I2C_DATA_CTL2 0x0033 +#define RT5663_PDM_I2C_DATA_CTL3 0x0034 +#define RT5663_PDM_I2C_DATA_CTL4 0x0035 + +/*Mixer - Analog*/ +#define RT5663_RECMIX1_NEW 0x003a +#define RT5663_RECMIX1L_0 0x003b +#define RT5663_RECMIX1L 0x003c +#define RT5663_RECMIX1R_0 0x003d +#define RT5663_RECMIX1R 0x003e +#define RT5663_RECMIX2_NEW 0x003f +#define RT5663_RECMIX2_L_2 0x0041 +#define RT5663_RECMIX2_R 0x0042 +#define RT5663_RECMIX2_R_2 0x0043 +#define RT5663_CALIB_REC_LR 0x0044 +#define RT5663_ALC_BK_GAIN 0x0049 +#define RT5663_MONOMIX_GAIN 0x004a +#define RT5663_MONOMIX_IN_GAIN 0x004b +#define RT5663_OUT_MIXL_GAIN 0x004d +#define RT5663_OUT_LMIX_IN_GAIN 0x004e +#define RT5663_OUT_RMIX_IN_GAIN 0x004f +#define RT5663_OUT_RMIX_IN_GAIN1 0x0050 +#define RT5663_LOUT_MIXER_CTRL 0x0052 +/* Power */ +#define RT5663_PWR_VOL 0x0067 + +#define RT5663_ADCDAC_RST 0x006d +/* Format - ADC/DAC */ +#define RT5663_I2S34_SDP 0x0071 +#define RT5663_I2S5_SDP 0x0072 + +/* Function - Analog */ +#define RT5663_ASRC_3 0x0085 +#define RT5663_ASRC_6 0x0088 +#define RT5663_ASRC_7 0x0089 +#define RT5663_PLL_TRK_13 0x0099 +#define RT5663_I2S_M_CLK_CTL 0x00a0 +#define RT5663_FDIV_I2S34_M_CLK 0x00a1 +#define RT5663_FDIV_I2S34_M_CLK2 0x00a2 +#define RT5663_FDIV_I2S5_M_CLK 0x00a3 +#define RT5663_FDIV_I2S5_M_CLK2 0x00a4 + +/* Function - Digital */ +#define RT5663_V2_IRQ_4 0x00b9 +#define RT5663_GPIO_3 0x00c2 +#define RT5663_GPIO_4 0x00c3 +#define RT5663_GPIO_STA2 0x00c4 +#define RT5663_HP_AMP_DET1 0x00d0 +#define RT5663_HP_AMP_DET2 0x00d1 +#define RT5663_HP_AMP_DET3 0x00d2 +#define RT5663_MID_BD_HP_AMP 0x00d3 +#define RT5663_LOW_BD_HP_AMP 0x00d4 +#define RT5663_SOF_VOL_ZC2 0x00da +#define RT5663_ADC_STO2_ADJ1 0x00ee +#define RT5663_ADC_STO2_ADJ2 0x00ef +/* General Control */ +#define RT5663_A_JD_CTRL 0x00f0 +#define RT5663_JD1_TRES_CTRL 0x00f1 +#define RT5663_JD2_TRES_CTRL 0x00f2 +#define RT5663_V2_JD_CTRL2 0x00f7 +#define RT5663_DUM_REG_2 0x00fb +#define RT5663_DUM_REG_3 0x00fc + + +#define RT5663_DACADC_DIG_VOL2 0x0101 +#define RT5663_DIG_IN_PIN2 0x0133 +#define RT5663_PAD_DRV_CTL1 0x0136 +#define RT5663_SOF_RAM_DEPOP 0x0138 +#define RT5663_VOL_TEST 0x013f +#define RT5663_MONO_DYNA_1 0x0170 +#define RT5663_MONO_DYNA_2 0x0171 +#define RT5663_MONO_DYNA_3 0x0172 +#define RT5663_MONO_DYNA_4 0x0173 +#define RT5663_MONO_DYNA_5 0x0174 +#define RT5663_MONO_DYNA_6 0x0175 +#define RT5663_STO1_SIL_DET 0x0190 +#define RT5663_MONOL_SIL_DET 0x0191 +#define RT5663_MONOR_SIL_DET 0x0192 +#define RT5663_STO2_DAC_SIL 0x0193 +#define RT5663_PWR_SAV_CTL1 0x0194 +#define RT5663_PWR_SAV_CTL2 0x0195 +#define RT5663_PWR_SAV_CTL3 0x0196 +#define RT5663_PWR_SAV_CTL4 0x0197 +#define RT5663_PWR_SAV_CTL5 0x0198 +#define RT5663_PWR_SAV_CTL6 0x0199 +#define RT5663_MONO_AMP_CAL1 0x01a0 +#define RT5663_MONO_AMP_CAL2 0x01a1 +#define RT5663_MONO_AMP_CAL3 0x01a2 +#define RT5663_MONO_AMP_CAL4 0x01a3 +#define RT5663_MONO_AMP_CAL5 0x01a4 +#define RT5663_MONO_AMP_CAL6 0x01a5 +#define RT5663_MONO_AMP_CAL7 0x01a6 +#define RT5663_MONO_AMP_CAL_ST1 0x01a7 +#define RT5663_MONO_AMP_CAL_ST2 0x01a8 +#define RT5663_MONO_AMP_CAL_ST3 0x01a9 +#define RT5663_MONO_AMP_CAL_ST4 0x01aa +#define RT5663_MONO_AMP_CAL_ST5 0x01ab +#define RT5663_V2_HP_IMP_SEN_13 0x01b9 +#define RT5663_V2_HP_IMP_SEN_14 0x01ba +#define RT5663_V2_HP_IMP_SEN_6 0x01bb +#define RT5663_V2_HP_IMP_SEN_7 0x01bc +#define RT5663_V2_HP_IMP_SEN_8 0x01bd +#define RT5663_V2_HP_IMP_SEN_9 0x01be +#define RT5663_V2_HP_IMP_SEN_10 0x01bf +#define RT5663_HP_LOGIC_3 0x01dc +#define RT5663_HP_CALIB_ST10 0x01f3 +#define RT5663_HP_CALIB_ST11 0x01f4 +#define RT5663_PRO_REG_TBL_4 0x0203 +#define RT5663_PRO_REG_TBL_5 0x0204 +#define RT5663_PRO_REG_TBL_6 0x0205 +#define RT5663_PRO_REG_TBL_7 0x0206 +#define RT5663_PRO_REG_TBL_8 0x0207 +#define RT5663_PRO_REG_TBL_9 0x0208 +#define RT5663_SAR_ADC_INL_1 0x0210 +#define RT5663_SAR_ADC_INL_2 0x0211 +#define RT5663_SAR_ADC_INL_3 0x0212 +#define RT5663_SAR_ADC_INL_4 0x0213 +#define RT5663_SAR_ADC_INL_5 0x0214 +#define RT5663_SAR_ADC_INL_6 0x0215 +#define RT5663_SAR_ADC_INL_7 0x0216 +#define RT5663_SAR_ADC_INL_8 0x0217 +#define RT5663_SAR_ADC_INL_9 0x0218 +#define RT5663_SAR_ADC_INL_10 0x0219 +#define RT5663_SAR_ADC_INL_11 0x021a +#define RT5663_SAR_ADC_INL_12 0x021b +#define RT5663_DRC_CTRL_1 0x02ff +#define RT5663_DRC1_CTRL_2 0x0301 +#define RT5663_DRC1_CTRL_3 0x0302 +#define RT5663_DRC1_CTRL_4 0x0303 +#define RT5663_DRC1_CTRL_5 0x0304 +#define RT5663_DRC1_CTRL_6 0x0305 +#define RT5663_DRC1_HD_CTRL_1 0x0306 +#define RT5663_DRC1_HD_CTRL_2 0x0307 +#define RT5663_DRC1_PRI_REG_1 0x0310 +#define RT5663_DRC1_PRI_REG_2 0x0311 +#define RT5663_DRC1_PRI_REG_3 0x0312 +#define RT5663_DRC1_PRI_REG_4 0x0313 +#define RT5663_DRC1_PRI_REG_5 0x0314 +#define RT5663_DRC1_PRI_REG_6 0x0315 +#define RT5663_DRC1_PRI_REG_7 0x0316 +#define RT5663_DRC1_PRI_REG_8 0x0317 +#define RT5663_ALC_PGA_CTL_1 0x0330 +#define RT5663_ALC_PGA_CTL_2 0x0331 +#define RT5663_ALC_PGA_CTL_3 0x0332 +#define RT5663_ALC_PGA_CTL_4 0x0333 +#define RT5663_ALC_PGA_CTL_5 0x0334 +#define RT5663_ALC_PGA_CTL_6 0x0335 +#define RT5663_ALC_PGA_CTL_7 0x0336 +#define RT5663_ALC_PGA_CTL_8 0x0337 +#define RT5663_ALC_PGA_REG_1 0x0338 +#define RT5663_ALC_PGA_REG_2 0x0339 +#define RT5663_ALC_PGA_REG_3 0x033a +#define RT5663_ADC_EQ_RECOV_1 0x03c0 +#define RT5663_ADC_EQ_RECOV_2 0x03c1 +#define RT5663_ADC_EQ_RECOV_3 0x03c2 +#define RT5663_ADC_EQ_RECOV_4 0x03c3 +#define RT5663_ADC_EQ_RECOV_5 0x03c4 +#define RT5663_ADC_EQ_RECOV_6 0x03c5 +#define RT5663_ADC_EQ_RECOV_7 0x03c6 +#define RT5663_ADC_EQ_RECOV_8 0x03c7 +#define RT5663_ADC_EQ_RECOV_9 0x03c8 +#define RT5663_ADC_EQ_RECOV_10 0x03c9 +#define RT5663_ADC_EQ_RECOV_11 0x03ca +#define RT5663_ADC_EQ_RECOV_12 0x03cb +#define RT5663_ADC_EQ_RECOV_13 0x03cc +#define RT5663_VID_HIDDEN 0x03fe +#define RT5663_VID_CUSTOMER 0x03ff +#define RT5663_SCAN_MODE 0x07f0 +#define RT5663_I2C_BYPA 0x07fa + +/* Headphone Amp Control 2 (0x0003) */ +#define RT5663_EN_DAC_HPO_MASK (0x1 << 14) +#define RT5663_EN_DAC_HPO_SHIFT 14 +#define RT5663_EN_DAC_HPO_DIS (0x0 << 14) +#define RT5663_EN_DAC_HPO_EN (0x1 << 14) + +/*Headphone Amp L/R Analog Gain and Digital NG2 Gain Control (0x0005 0x0006)*/ +#define RT5663_GAIN_HP (0x1f << 8) +#define RT5663_GAIN_HP_SHIFT 8 + +/* AEC BST Control (0x000b) */ +#define RT5663_GAIN_CBJ_MASK (0xf << 8) +#define RT5663_GAIN_CBJ_SHIFT 8 + +/* IN1 Control / MIC GND REF (0x000c) */ +#define RT5663_IN1_DF_MASK (0x1 << 15) +#define RT5663_IN1_DF_SHIFT 15 + +/* Combo Jack and Type Detection Control 1 (0x0010) */ +#define RT5663_CBJ_DET_MASK (0x1 << 15) +#define RT5663_CBJ_DET_SHIFT 15 +#define RT5663_CBJ_DET_DIS (0x0 << 15) +#define RT5663_CBJ_DET_EN (0x1 << 15) +#define RT5663_DET_TYPE_MASK (0x1 << 12) +#define RT5663_DET_TYPE_SHIFT 12 +#define RT5663_DET_TYPE_WLCSP (0x0 << 12) +#define RT5663_DET_TYPE_QFN (0x1 << 12) +#define RT5663_VREF_BIAS_MASK (0x1 << 6) +#define RT5663_VREF_BIAS_SHIFT 6 +#define RT5663_VREF_BIAS_FSM (0x0 << 6) +#define RT5663_VREF_BIAS_REG (0x1 << 6) + +/* REC Left Mixer Control 2 (0x003c) */ +#define RT5663_RECMIX1L_BST1_CBJ (0x1 << 7) +#define RT5663_RECMIX1L_BST1_CBJ_SHIFT 7 +#define RT5663_RECMIX1L_BST2 (0x1 << 4) +#define RT5663_RECMIX1L_BST2_SHIFT 4 + +/* REC Right Mixer Control 2 (0x003e) */ +#define RT5663_RECMIX1R_BST2 (0x1 << 4) +#define RT5663_RECMIX1R_BST2_SHIFT 4 + +/* DAC1 Digital Volume (0x0019) */ +#define RT5663_DAC_L1_VOL_MASK (0xff << 8) +#define RT5663_DAC_L1_VOL_SHIFT 8 +#define RT5663_DAC_R1_VOL_MASK (0xff) +#define RT5663_DAC_R1_VOL_SHIFT 0 + +/* ADC Digital Volume Control (0x001c) */ +#define RT5663_ADC_L_MUTE_MASK (0x1 << 15) +#define RT5663_ADC_L_MUTE_SHIFT 15 +#define RT5663_ADC_L_VOL_MASK (0x7f << 8) +#define RT5663_ADC_L_VOL_SHIFT 8 +#define RT5663_ADC_R_MUTE_MASK (0x1 << 7) +#define RT5663_ADC_R_MUTE_SHIFT 7 +#define RT5663_ADC_R_VOL_MASK (0x7f) +#define RT5663_ADC_R_VOL_SHIFT 0 + +/* Stereo ADC Mixer Control (0x0026) */ +#define RT5663_M_STO1_ADC_L1 (0x1 << 15) +#define RT5663_M_STO1_ADC_L1_SHIFT 15 +#define RT5663_M_STO1_ADC_L2 (0x1 << 14) +#define RT5663_M_STO1_ADC_L2_SHIFT 14 +#define RT5663_STO1_ADC_L1_SRC (0x1 << 13) +#define RT5663_STO1_ADC_L1_SRC_SHIFT 13 +#define RT5663_STO1_ADC_L2_SRC (0x1 << 12) +#define RT5663_STO1_ADC_L2_SRC_SHIFT 12 +#define RT5663_STO1_ADC_L_SRC (0x3 << 10) +#define RT5663_STO1_ADC_L_SRC_SHIFT 10 +#define RT5663_M_STO1_ADC_R1 (0x1 << 7) +#define RT5663_M_STO1_ADC_R1_SHIFT 7 +#define RT5663_M_STO1_ADC_R2 (0x1 << 6) +#define RT5663_M_STO1_ADC_R2_SHIFT 6 +#define RT5663_STO1_ADC_R1_SRC (0x1 << 5) +#define RT5663_STO1_ADC_R1_SRC_SHIFT 5 +#define RT5663_STO1_ADC_R2_SRC (0x1 << 4) +#define RT5663_STO1_ADC_R2_SRC_SHIFT 4 +#define RT5663_STO1_ADC_R_SRC (0x3 << 2) +#define RT5663_STO1_ADC_R_SRC_SHIFT 2 + +/* ADC Mixer to DAC Mixer Control (0x0029) */ +#define RT5663_M_ADCMIX_L (0x1 << 15) +#define RT5663_M_ADCMIX_L_SHIFT 15 +#define RT5663_M_DAC1_L (0x1 << 14) +#define RT5663_M_DAC1_L_SHIFT 14 +#define RT5663_M_ADCMIX_R (0x1 << 7) +#define RT5663_M_ADCMIX_R_SHIFT 7 +#define RT5663_M_DAC1_R (0x1 << 6) +#define RT5663_M_DAC1_R_SHIFT 6 + +/* Stereo DAC Mixer Control (0x002a) */ +#define RT5663_M_DAC_L1_STO_L (0x1 << 15) +#define RT5663_M_DAC_L1_STO_L_SHIFT 15 +#define RT5663_M_DAC_R1_STO_L (0x1 << 13) +#define RT5663_M_DAC_R1_STO_L_SHIFT 13 +#define RT5663_M_DAC_L1_STO_R (0x1 << 7) +#define RT5663_M_DAC_L1_STO_R_SHIFT 7 +#define RT5663_M_DAC_R1_STO_R (0x1 << 5) +#define RT5663_M_DAC_R1_STO_R_SHIFT 5 + +/* Power Management for Digital 1 (0x0061) */ +#define RT5663_PWR_I2S1 (0x1 << 15) +#define RT5663_PWR_I2S1_SHIFT 15 +#define RT5663_PWR_DAC_L1 (0x1 << 11) +#define RT5663_PWR_DAC_L1_SHIFT 11 +#define RT5663_PWR_DAC_R1 (0x1 << 10) +#define RT5663_PWR_DAC_R1_SHIFT 10 +#define RT5663_PWR_LDO_DACREF_MASK (0x1 << 8) +#define RT5663_PWR_LDO_DACREF_SHIFT 8 +#define RT5663_PWR_LDO_DACREF_ON (0x1 << 8) +#define RT5663_PWR_LDO_DACREF_DOWN (0x0 << 8) +#define RT5663_PWR_LDO_SHIFT 8 +#define RT5663_PWR_ADC_L1 (0x1 << 4) +#define RT5663_PWR_ADC_L1_SHIFT 4 +#define RT5663_PWR_ADC_R1 (0x1 << 3) +#define RT5663_PWR_ADC_R1_SHIFT 3 + +/* Power Management for Digital 2 (0x0062) */ +#define RT5663_PWR_ADC_S1F (0x1 << 15) +#define RT5663_PWR_ADC_S1F_SHIFT 15 +#define RT5663_PWR_DAC_S1F (0x1 << 10) +#define RT5663_PWR_DAC_S1F_SHIFT 10 + +/* Power Management for Analog 1 (0x0063) */ +#define RT5663_PWR_VREF1 (0x1 << 15) +#define RT5663_PWR_VREF1_MASK (0x1 << 15) +#define RT5663_PWR_VREF1_SHIFT 15 +#define RT5663_PWR_FV1 (0x1 << 14) +#define RT5663_PWR_FV1_MASK (0x1 << 14) +#define RT5663_PWR_FV1_SHIFT 14 +#define RT5663_PWR_VREF2 (0x1 << 13) +#define RT5663_PWR_VREF2_MASK (0x1 << 13) +#define RT5663_PWR_VREF2_SHIFT 13 +#define RT5663_PWR_FV2 (0x1 << 12) +#define RT5663_PWR_FV2_MASK (0x1 << 12) +#define RT5663_PWR_FV2_SHIFT 12 +#define RT5663_PWR_MB (0x1 << 9) +#define RT5663_PWR_MB_MASK (0x1 << 9) +#define RT5663_PWR_MB_SHIFT 9 +#define RT5663_AMP_HP_MASK (0x3 << 2) +#define RT5663_AMP_HP_SHIFT 2 +#define RT5663_AMP_HP_1X (0x0 << 2) +#define RT5663_AMP_HP_3X (0x1 << 2) +#define RT5663_AMP_HP_5X (0x3 << 2) +#define RT5663_LDO1_DVO_MASK (0x3) +#define RT5663_LDO1_DVO_SHIFT 0 +#define RT5663_LDO1_DVO_0_9V (0x0) +#define RT5663_LDO1_DVO_1_0V (0x1) +#define RT5663_LDO1_DVO_1_2V (0x2) +#define RT5663_LDO1_DVO_1_4V (0x3) + +/* Power Management for Analog 2 (0x0064) */ +#define RT5663_PWR_BST1 (0x1 << 15) +#define RT5663_PWR_BST1_MASK (0x1 << 15) +#define RT5663_PWR_BST1_SHIFT 15 +#define RT5663_PWR_BST1_OFF (0x0 << 15) +#define RT5663_PWR_BST1_ON (0x1 << 15) +#define RT5663_PWR_BST2 (0x1 << 14) +#define RT5663_PWR_BST2_MASK (0x1 << 14) +#define RT5663_PWR_BST2_SHIFT 14 +#define RT5663_PWR_MB1 (0x1 << 11) +#define RT5663_PWR_MB1_SHIFT 11 +#define RT5663_PWR_MB2 (0x1 << 10) +#define RT5663_PWR_MB2_SHIFT 10 +#define RT5663_PWR_BST2_OP (0x1 << 6) +#define RT5663_PWR_BST2_OP_MASK (0x1 << 6) +#define RT5663_PWR_BST2_OP_SHIFT 6 +#define RT5663_PWR_JD1 (0x1 << 3) +#define RT5663_PWR_JD1_MASK (0x1 << 3) +#define RT5663_PWR_JD1_SHIFT 3 +#define RT5663_PWR_JD2 (0x1 << 2) +#define RT5663_PWR_JD2_MASK (0x1 << 2) +#define RT5663_PWR_JD2_SHIFT 2 +#define RT5663_PWR_RECMIX1 (0x1 << 1) +#define RT5663_PWR_RECMIX1_SHIFT 1 +#define RT5663_PWR_RECMIX2 (0x1) +#define RT5663_PWR_RECMIX2_SHIFT 0 + +/* Power Management for Analog 3 (0x0065) */ +#define RT5663_PWR_CBJ_MASK (0x1 << 9) +#define RT5663_PWR_CBJ_SHIFT 9 +#define RT5663_PWR_CBJ_OFF (0x0 << 9) +#define RT5663_PWR_CBJ_ON (0x1 << 9) +#define RT5663_PWR_PLL (0x1 << 6) +#define RT5663_PWR_PLL_SHIFT 6 +#define RT5663_PWR_LDO2 (0x1 << 2) +#define RT5663_PWR_LDO2_SHIFT 2 + +/* Power Management for Volume (0x0067) */ +#define RT5663_V2_PWR_MIC_DET (0x1 << 5) +#define RT5663_V2_PWR_MIC_DET_SHIFT 5 + +/* MCLK and System Clock Detection Control (0x006b) */ +#define RT5663_EN_ANA_CLK_DET_MASK (0x1 << 15) +#define RT5663_EN_ANA_CLK_DET_SHIFT 15 +#define RT5663_EN_ANA_CLK_DET_DIS (0x0 << 15) +#define RT5663_EN_ANA_CLK_DET_AUTO (0x1 << 15) +#define RT5663_PWR_CLK_DET_MASK (0x1) +#define RT5663_PWR_CLK_DET_SHIFT 0 +#define RT5663_PWR_CLK_DET_DIS (0x0) +#define RT5663_PWR_CLK_DET_EN (0x1) + +/* I2S1 Audio Serial Data Port Control (0x0070) */ +#define RT5663_I2S_MS_MASK (0x1 << 15) +#define RT5663_I2S_MS_SHIFT 15 +#define RT5663_I2S_MS_M (0x0 << 15) +#define RT5663_I2S_MS_S (0x1 << 15) +#define RT5663_I2S_BP_MASK (0x1 << 8) +#define RT5663_I2S_BP_SHIFT 8 +#define RT5663_I2S_BP_NOR (0x0 << 8) +#define RT5663_I2S_BP_INV (0x1 << 8) +#define RT5663_I2S_DL_MASK (0x3 << 4) +#define RT5663_I2S_DL_SHIFT 4 +#define RT5663_I2S_DL_16 (0x0 << 4) +#define RT5663_I2S_DL_20 (0x1 << 4) +#define RT5663_I2S_DL_24 (0x2 << 4) +#define RT5663_I2S_DL_8 (0x3 << 4) +#define RT5663_I2S_DF_MASK (0x7) +#define RT5663_I2S_DF_SHIFT 0 +#define RT5663_I2S_DF_I2S (0x0) +#define RT5663_I2S_DF_LEFT (0x1) +#define RT5663_I2S_DF_PCM_A (0x2) +#define RT5663_I2S_DF_PCM_B (0x3) +#define RT5663_I2S_DF_PCM_A_N (0x6) +#define RT5663_I2S_DF_PCM_B_N (0x7) + +/* ADC/DAC Clock Control 1 (0x0073) */ +#define RT5663_I2S_PD1_MASK (0x7 << 12) +#define RT5663_I2S_PD1_SHIFT 12 +#define RT5663_M_I2S_DIV_MASK (0x7 << 8) +#define RT5663_M_I2S_DIV_SHIFT 8 +#define RT5663_CLK_SRC_MASK (0x3 << 4) +#define RT5663_CLK_SRC_MCLK (0x0 << 4) +#define RT5663_CLK_SRC_PLL_OUT (0x1 << 4) +#define RT5663_CLK_SRC_DIV (0x2 << 4) +#define RT5663_CLK_SRC_RC (0x3 << 4) +#define RT5663_DAC_OSR_MASK (0x3 << 2) +#define RT5663_DAC_OSR_SHIFT 2 +#define RT5663_DAC_OSR_128 (0x0 << 2) +#define RT5663_DAC_OSR_64 (0x1 << 2) +#define RT5663_DAC_OSR_32 (0x2 << 2) +#define RT5663_ADC_OSR_MASK (0x3) +#define RT5663_ADC_OSR_SHIFT 0 +#define RT5663_ADC_OSR_128 (0x0) +#define RT5663_ADC_OSR_64 (0x1) +#define RT5663_ADC_OSR_32 (0x2) + +/* TDM1 control 1 (0x0078) */ +#define RT5663_TDM_MODE_MASK (0x1 << 15) +#define RT5663_TDM_MODE_SHIFT 15 +#define RT5663_TDM_MODE_I2S (0x0 << 15) +#define RT5663_TDM_MODE_TDM (0x1 << 15) +#define RT5663_TDM_IN_CH_MASK (0x3 << 10) +#define RT5663_TDM_IN_CH_SHIFT 10 +#define RT5663_TDM_IN_CH_2 (0x0 << 10) +#define RT5663_TDM_IN_CH_4 (0x1 << 10) +#define RT5663_TDM_IN_CH_6 (0x2 << 10) +#define RT5663_TDM_IN_CH_8 (0x3 << 10) +#define RT5663_TDM_OUT_CH_MASK (0x3 << 8) +#define RT5663_TDM_OUT_CH_SHIFT 8 +#define RT5663_TDM_OUT_CH_2 (0x0 << 8) +#define RT5663_TDM_OUT_CH_4 (0x1 << 8) +#define RT5663_TDM_OUT_CH_6 (0x2 << 8) +#define RT5663_TDM_OUT_CH_8 (0x3 << 8) +#define RT5663_TDM_IN_LEN_MASK (0x3 << 6) +#define RT5663_TDM_IN_LEN_SHIFT 6 +#define RT5663_TDM_IN_LEN_16 (0x0 << 6) +#define RT5663_TDM_IN_LEN_20 (0x1 << 6) +#define RT5663_TDM_IN_LEN_24 (0x2 << 6) +#define RT5663_TDM_IN_LEN_32 (0x3 << 6) +#define RT5663_TDM_OUT_LEN_MASK (0x3 << 4) +#define RT5663_TDM_OUT_LEN_SHIFT 4 +#define RT5663_TDM_OUT_LEN_16 (0x0 << 4) +#define RT5663_TDM_OUT_LEN_20 (0x1 << 4) +#define RT5663_TDM_OUT_LEN_24 (0x2 << 4) +#define RT5663_TDM_OUT_LEN_32 (0x3 << 4) + +/* Global Clock Control (0x0080) */ +#define RT5663_SCLK_SRC_MASK (0x3 << 14) +#define RT5663_SCLK_SRC_SHIFT 14 +#define RT5663_SCLK_SRC_MCLK (0x0 << 14) +#define RT5663_SCLK_SRC_PLL1 (0x1 << 14) +#define RT5663_SCLK_SRC_RCCLK (0x2 << 14) +#define RT5663_PLL1_SRC_MASK (0x7 << 11) +#define RT5663_PLL1_SRC_SHIFT 11 +#define RT5663_PLL1_SRC_MCLK (0x0 << 11) +#define RT5663_PLL1_SRC_BCLK1 (0x1 << 11) +#define RT5663_V2_PLL1_SRC_MASK (0x7 << 8) +#define RT5663_V2_PLL1_SRC_SHIFT 8 +#define RT5663_V2_PLL1_SRC_MCLK (0x0 << 8) +#define RT5663_V2_PLL1_SRC_BCLK1 (0x1 << 8) +#define RT5663_PLL1_PD_MASK (0x1 << 4) +#define RT5663_PLL1_PD_SHIFT 4 + +#define RT5663_PLL_INP_MAX 40000000 +#define RT5663_PLL_INP_MIN 256000 +/* PLL M/N/K Code Control 1 (0x0081) */ +#define RT5663_PLL_N_MAX 0x001ff +#define RT5663_PLL_N_MASK (RT5663_PLL_N_MAX << 7) +#define RT5663_PLL_N_SHIFT 7 +#define RT5663_PLL_K_MAX 0x001f +#define RT5663_PLL_K_MASK (RT5663_PLL_K_MAX) +#define RT5663_PLL_K_SHIFT 0 + +/* PLL M/N/K Code Control 2 (0x0082) */ +#define RT5663_PLL_M_MAX 0x00f +#define RT5663_PLL_M_MASK (RT5663_PLL_M_MAX << 12) +#define RT5663_PLL_M_SHIFT 12 +#define RT5663_PLL_M_BP (0x1 << 11) +#define RT5663_PLL_M_BP_SHIFT 11 + +/* PLL tracking mode 1 (0x0083) */ +#define RT5663_V2_I2S1_ASRC_MASK (0x1 << 13) +#define RT5663_V2_I2S1_ASRC_SHIFT 13 +#define RT5663_V2_DAC_STO1_ASRC_MASK (0x1 << 12) +#define RT5663_V2_DAC_STO1_ASRC_SHIFT 12 +#define RT5663_V2_ADC_STO1_ASRC_MASK (0x1 << 4) +#define RT5663_V2_ADC_STO1_ASRC_SHIFT 4 + +/* PLL tracking mode 2 (0x0084)*/ +#define RT5663_DA_STO1_TRACK_MASK (0x7 << 12) +#define RT5663_DA_STO1_TRACK_SHIFT 12 +#define RT5663_DA_STO1_TRACK_SYSCLK (0x0 << 12) +#define RT5663_DA_STO1_TRACK_I2S1 (0x1 << 12) + +/* PLL tracking mode 3 (0x0085)*/ +#define RT5663_V2_AD_STO1_TRACK_MASK (0x7 << 12) +#define RT5663_V2_AD_STO1_TRACK_SHIFT 12 +#define RT5663_V2_AD_STO1_TRACK_SYSCLK (0x0 << 12) +#define RT5663_V2_AD_STO1_TRACK_I2S1 (0x1 << 12) + +/* HPOUT Charge pump control 1 (0x0091) */ +#define RT5663_OSW_HP_L_MASK (0x1 << 11) +#define RT5663_OSW_HP_L_SHIFT 11 +#define RT5663_OSW_HP_L_EN (0x1 << 11) +#define RT5663_OSW_HP_L_DIS (0x0 << 11) +#define RT5663_OSW_HP_R_MASK (0x1 << 10) +#define RT5663_OSW_HP_R_SHIFT 10 +#define RT5663_OSW_HP_R_EN (0x1 << 10) +#define RT5663_OSW_HP_R_DIS (0x0 << 10) +#define RT5663_SEL_PM_HP_MASK (0x3 << 8) +#define RT5663_SEL_PM_HP_SHIFT 8 +#define RT5663_SEL_PM_HP_0_6 (0x0 << 8) +#define RT5663_SEL_PM_HP_0_9 (0x1 << 8) +#define RT5663_SEL_PM_HP_1_8 (0x2 << 8) +#define RT5663_SEL_PM_HP_HIGH (0x3 << 8) +#define RT5663_OVCD_HP_MASK (0x1 << 2) +#define RT5663_OVCD_HP_SHIFT 2 +#define RT5663_OVCD_HP_EN (0x1 << 2) +#define RT5663_OVCD_HP_DIS (0x0 << 2) + +/* RC Clock Control (0x0094) */ +#define RT5663_DIG_25M_CLK_MASK (0x1 << 9) +#define RT5663_DIG_25M_CLK_SHIFT 9 +#define RT5663_DIG_25M_CLK_DIS (0x0 << 9) +#define RT5663_DIG_25M_CLK_EN (0x1 << 9) +#define RT5663_DIG_1M_CLK_MASK (0x1 << 8) +#define RT5663_DIG_1M_CLK_SHIFT 8 +#define RT5663_DIG_1M_CLK_DIS (0x0 << 8) +#define RT5663_DIG_1M_CLK_EN (0x1 << 8) + +/* Auto Turn On 1M RC CLK (0x009f) */ +#define RT5663_IRQ_POW_SAV_MASK (0x1 << 15) +#define RT5663_IRQ_POW_SAV_SHIFT 15 +#define RT5663_IRQ_POW_SAV_DIS (0x0 << 15) +#define RT5663_IRQ_POW_SAV_EN (0x1 << 15) +#define RT5663_IRQ_POW_SAV_JD1_MASK (0x1 << 14) +#define RT5663_IRQ_POW_SAV_JD1_SHIFT 14 +#define RT5663_IRQ_POW_SAV_JD1_DIS (0x0 << 14) +#define RT5663_IRQ_POW_SAV_JD1_EN (0x1 << 14) + +/* IRQ Control 1 (0x00b6) */ +#define RT5663_EN_CB_JD_MASK (0x1 << 3) +#define RT5663_EN_CB_JD_SHIFT 3 +#define RT5663_EN_CB_JD_EN (0x1 << 3) +#define RT5663_EN_CB_JD_DIS (0x0 << 3) + +/* IRQ Control 3 (0x00b8) */ +#define RT5663_V2_EN_IRQ_INLINE_MASK (0x1 << 6) +#define RT5663_V2_EN_IRQ_INLINE_SHIFT 6 +#define RT5663_V2_EN_IRQ_INLINE_BYP (0x0 << 6) +#define RT5663_V2_EN_IRQ_INLINE_NOR (0x1 << 6) + +/* GPIO Control 1 (0x00c0) */ +#define RT5663_GP1_PIN_MASK (0x1 << 15) +#define RT5663_GP1_PIN_SHIFT 15 +#define RT5663_GP1_PIN_GPIO1 (0x0 << 15) +#define RT5663_GP1_PIN_IRQ (0x1 << 15) + +/* GPIO Control 2 (0x00c1) */ +#define RT5663_GP4_PIN_CONF_MASK (0x1 << 5) +#define RT5663_GP4_PIN_CONF_SHIFT 5 +#define RT5663_GP4_PIN_CONF_INPUT (0x0 << 5) +#define RT5663_GP4_PIN_CONF_OUTPUT (0x1 << 5) + +/* GPIO Control 2 (0x00c2) */ +#define RT5663_GP8_PIN_CONF_MASK (0x1 << 13) +#define RT5663_GP8_PIN_CONF_SHIFT 13 +#define RT5663_GP8_PIN_CONF_INPUT (0x0 << 13) +#define RT5663_GP8_PIN_CONF_OUTPUT (0x1 << 13) + +/* 4 Buttons Inline Command Function 1 (0x00df) */ +#define RT5663_4BTN_CLK_DEB_MASK (0x3 << 2) +#define RT5663_4BTN_CLK_DEB_SHIFT 2 +#define RT5663_4BTN_CLK_DEB_8MS (0x0 << 2) +#define RT5663_4BTN_CLK_DEB_16MS (0x1 << 2) +#define RT5663_4BTN_CLK_DEB_32MS (0x2 << 2) +#define RT5663_4BTN_CLK_DEB_65MS (0x3 << 2) + +/* Inline Command Function 6 (0x00e0) */ +#define RT5663_EN_4BTN_INL_MASK (0x1 << 15) +#define RT5663_EN_4BTN_INL_SHIFT 15 +#define RT5663_EN_4BTN_INL_DIS (0x0 << 15) +#define RT5663_EN_4BTN_INL_EN (0x1 << 15) +#define RT5663_RESET_4BTN_INL_MASK (0x1 << 14) +#define RT5663_RESET_4BTN_INL_SHIFT 14 +#define RT5663_RESET_4BTN_INL_RESET (0x0 << 14) +#define RT5663_RESET_4BTN_INL_NOR (0x1 << 14) + +/* Digital Misc Control (0x00fa) */ +#define RT5663_DIG_GATE_CTRL_MASK 0x1 +#define RT5663_DIG_GATE_CTRL_SHIFT (0) +#define RT5663_DIG_GATE_CTRL_DIS 0x0 +#define RT5663_DIG_GATE_CTRL_EN 0x1 + +/* Chopper and Clock control for DAC L (0x013a)*/ +#define RT5663_CKXEN_DAC1_MASK (0x1 << 13) +#define RT5663_CKXEN_DAC1_SHIFT 13 +#define RT5663_CKGEN_DAC1_MASK (0x1 << 12) +#define RT5663_CKGEN_DAC1_SHIFT 12 + +/* Chopper and Clock control for ADC (0x013b)*/ +#define RT5663_CKXEN_ADCC_MASK (0x1 << 13) +#define RT5663_CKXEN_ADCC_SHIFT 13 +#define RT5663_CKGEN_ADCC_MASK (0x1 << 12) +#define RT5663_CKGEN_ADCC_SHIFT 12 + +/* HP Behavior Logic Control 2 (0x01db) */ +#define RT5663_HP_SIG_SRC1_MASK (0x3) +#define RT5663_HP_SIG_SRC1_SHIFT 0 +#define RT5663_HP_SIG_SRC1_HP_DC (0x0) +#define RT5663_HP_SIG_SRC1_HP_CALIB (0x1) +#define RT5663_HP_SIG_SRC1_REG (0x2) +#define RT5663_HP_SIG_SRC1_SILENCE (0x3) + +/* RT5663 specific register */ +#define RT5663_HP_OUT_EN 0x0002 +#define RT5663_HP_LCH_DRE 0x0005 +#define RT5663_HP_RCH_DRE 0x0006 +#define RT5663_CALIB_BST 0x000a +#define RT5663_RECMIX 0x0010 +#define RT5663_SIL_DET_CTL 0x0015 +#define RT5663_PWR_SAV_SILDET 0x0016 +#define RT5663_SIDETONE_CTL 0x0018 +#define RT5663_STO1_DAC_DIG_VOL 0x0019 +#define RT5663_STO1_ADC_DIG_VOL 0x001c +#define RT5663_STO1_BOOST 0x001f +#define RT5663_HP_IMP_GAIN_1 0x0022 +#define RT5663_HP_IMP_GAIN_2 0x0023 +#define RT5663_STO1_ADC_MIXER 0x0026 +#define RT5663_AD_DA_MIXER 0x0029 +#define RT5663_STO_DAC_MIXER 0x002a +#define RT5663_DIG_SIDE_MIXER 0x002c +#define RT5663_BYPASS_STO_DAC 0x002d +#define RT5663_CALIB_REC_MIX 0x0040 +#define RT5663_PWR_DIG_1 0x0061 +#define RT5663_PWR_DIG_2 0x0062 +#define RT5663_PWR_ANLG_1 0x0063 +#define RT5663_PWR_ANLG_2 0x0064 +#define RT5663_PWR_ANLG_3 0x0065 +#define RT5663_PWR_MIXER 0x0066 +#define RT5663_SIG_CLK_DET 0x006b +#define RT5663_PRE_DIV_GATING_1 0x006e +#define RT5663_PRE_DIV_GATING_2 0x006f +#define RT5663_I2S1_SDP 0x0070 +#define RT5663_ADDA_CLK_1 0x0073 +#define RT5663_ADDA_RST 0x0074 +#define RT5663_FRAC_DIV_1 0x0075 +#define RT5663_FRAC_DIV_2 0x0076 +#define RT5663_TDM_1 0x0077 +#define RT5663_TDM_2 0x0078 +#define RT5663_TDM_3 0x0079 +#define RT5663_TDM_4 0x007a +#define RT5663_TDM_5 0x007b +#define RT5663_TDM_6 0x007c +#define RT5663_TDM_7 0x007d +#define RT5663_TDM_8 0x007e +#define RT5663_TDM_9 0x007f +#define RT5663_GLB_CLK 0x0080 +#define RT5663_PLL_1 0x0081 +#define RT5663_PLL_2 0x0082 +#define RT5663_ASRC_1 0x0083 +#define RT5663_ASRC_2 0x0084 +#define RT5663_ASRC_4 0x0086 +#define RT5663_DUMMY_REG 0x0087 +#define RT5663_ASRC_8 0x008a +#define RT5663_ASRC_9 0x008b +#define RT5663_ASRC_11 0x008c +#define RT5663_DEPOP_1 0x008e +#define RT5663_DEPOP_2 0x008f +#define RT5663_DEPOP_3 0x0090 +#define RT5663_HP_CHARGE_PUMP_1 0x0091 +#define RT5663_HP_CHARGE_PUMP_2 0x0092 +#define RT5663_MICBIAS_1 0x0093 +#define RT5663_RC_CLK 0x0094 +#define RT5663_ASRC_11_2 0x0097 +#define RT5663_DUMMY_REG_2 0x0098 +#define RT5663_REC_PATH_GAIN 0x009a +#define RT5663_AUTO_1MRC_CLK 0x009f +#define RT5663_ADC_EQ_1 0x00ae +#define RT5663_ADC_EQ_2 0x00af +#define RT5663_IRQ_1 0x00b6 +#define RT5663_IRQ_2 0x00b7 +#define RT5663_IRQ_3 0x00b8 +#define RT5663_IRQ_4 0x00ba +#define RT5663_IRQ_5 0x00bb +#define RT5663_INT_ST_1 0x00be +#define RT5663_INT_ST_2 0x00bf +#define RT5663_GPIO_1 0x00c0 +#define RT5663_GPIO_2 0x00c1 +#define RT5663_GPIO_STA1 0x00c5 +#define RT5663_SIN_GEN_1 0x00cb +#define RT5663_SIN_GEN_2 0x00cc +#define RT5663_SIN_GEN_3 0x00cd +#define RT5663_SOF_VOL_ZC1 0x00d9 +#define RT5663_IL_CMD_1 0x00db +#define RT5663_IL_CMD_2 0x00dc +#define RT5663_IL_CMD_3 0x00dd +#define RT5663_IL_CMD_4 0x00de +#define RT5663_IL_CMD_5 0x00df +#define RT5663_IL_CMD_6 0x00e0 +#define RT5663_IL_CMD_7 0x00e1 +#define RT5663_IL_CMD_8 0x00e2 +#define RT5663_IL_CMD_PWRSAV1 0x00e4 +#define RT5663_IL_CMD_PWRSAV2 0x00e5 +#define RT5663_EM_JACK_TYPE_1 0x00e6 +#define RT5663_EM_JACK_TYPE_2 0x00e7 +#define RT5663_EM_JACK_TYPE_3 0x00e8 +#define RT5663_EM_JACK_TYPE_4 0x00e9 +#define RT5663_EM_JACK_TYPE_5 0x00ea +#define RT5663_EM_JACK_TYPE_6 0x00eb +#define RT5663_STO1_HPF_ADJ1 0x00ec +#define RT5663_STO1_HPF_ADJ2 0x00ed +#define RT5663_FAST_OFF_MICBIAS 0x00f4 +#define RT5663_JD_CTRL1 0x00f6 +#define RT5663_JD_CTRL2 0x00f8 +#define RT5663_DIG_MISC 0x00fa +#define RT5663_DIG_VOL_ZCD 0x0100 +#define RT5663_ANA_BIAS_CUR_1 0x0108 +#define RT5663_ANA_BIAS_CUR_2 0x0109 +#define RT5663_ANA_BIAS_CUR_3 0x010a +#define RT5663_ANA_BIAS_CUR_4 0x010b +#define RT5663_ANA_BIAS_CUR_5 0x010c +#define RT5663_ANA_BIAS_CUR_6 0x010d +#define RT5663_BIAS_CUR_5 0x010e +#define RT5663_BIAS_CUR_6 0x010f +#define RT5663_BIAS_CUR_7 0x0110 +#define RT5663_BIAS_CUR_8 0x0111 +#define RT5663_DACREF_LDO 0x0112 +#define RT5663_DUMMY_REG_3 0x0113 +#define RT5663_BIAS_CUR_9 0x0114 +#define RT5663_DUMMY_REG_4 0x0116 +#define RT5663_VREFADJ_OP 0x0117 +#define RT5663_VREF_RECMIX 0x0118 +#define RT5663_CHARGE_PUMP_1 0x0125 +#define RT5663_CHARGE_PUMP_1_2 0x0126 +#define RT5663_CHARGE_PUMP_1_3 0x0127 +#define RT5663_CHARGE_PUMP_2 0x0128 +#define RT5663_DIG_IN_PIN1 0x0132 +#define RT5663_PAD_DRV_CTL 0x0137 +#define RT5663_PLL_INT_REG 0x0139 +#define RT5663_CHOP_DAC_L 0x013a +#define RT5663_CHOP_ADC 0x013b +#define RT5663_CALIB_ADC 0x013c +#define RT5663_CHOP_DAC_R 0x013d +#define RT5663_DUMMY_CTL_DACLR 0x013e +#define RT5663_DUMMY_REG_5 0x0140 +#define RT5663_SOFT_RAMP 0x0141 +#define RT5663_TEST_MODE_1 0x0144 +#define RT5663_TEST_MODE_2 0x0145 +#define RT5663_TEST_MODE_3 0x0146 +#define RT5663_TEST_MODE_4 0x0147 +#define RT5663_TEST_MODE_5 0x0148 +#define RT5663_STO_DRE_1 0x0160 +#define RT5663_STO_DRE_2 0x0161 +#define RT5663_STO_DRE_3 0x0162 +#define RT5663_STO_DRE_4 0x0163 +#define RT5663_STO_DRE_5 0x0164 +#define RT5663_STO_DRE_6 0x0165 +#define RT5663_STO_DRE_7 0x0166 +#define RT5663_STO_DRE_8 0x0167 +#define RT5663_STO_DRE_9 0x0168 +#define RT5663_STO_DRE_10 0x0169 +#define RT5663_MIC_DECRO_1 0x0180 +#define RT5663_MIC_DECRO_2 0x0181 +#define RT5663_MIC_DECRO_3 0x0182 +#define RT5663_MIC_DECRO_4 0x0183 +#define RT5663_MIC_DECRO_5 0x0184 +#define RT5663_MIC_DECRO_6 0x0185 +#define RT5663_HP_DECRO_1 0x01b0 +#define RT5663_HP_DECRO_2 0x01b1 +#define RT5663_HP_DECRO_3 0x01b2 +#define RT5663_HP_DECRO_4 0x01b3 +#define RT5663_HP_DECOUP 0x01b4 +#define RT5663_HP_IMP_SEN_MAP8 0x01b5 +#define RT5663_HP_IMP_SEN_MAP9 0x01b6 +#define RT5663_HP_IMP_SEN_MAP10 0x01b7 +#define RT5663_HP_IMP_SEN_MAP11 0x01b8 +#define RT5663_HP_IMP_SEN_1 0x01c0 +#define RT5663_HP_IMP_SEN_2 0x01c1 +#define RT5663_HP_IMP_SEN_3 0x01c2 +#define RT5663_HP_IMP_SEN_4 0x01c3 +#define RT5663_HP_IMP_SEN_5 0x01c4 +#define RT5663_HP_IMP_SEN_6 0x01c5 +#define RT5663_HP_IMP_SEN_7 0x01c6 +#define RT5663_HP_IMP_SEN_8 0x01c7 +#define RT5663_HP_IMP_SEN_9 0x01c8 +#define RT5663_HP_IMP_SEN_10 0x01c9 +#define RT5663_HP_IMP_SEN_11 0x01ca +#define RT5663_HP_IMP_SEN_12 0x01cb +#define RT5663_HP_IMP_SEN_13 0x01cc +#define RT5663_HP_IMP_SEN_14 0x01cd +#define RT5663_HP_IMP_SEN_15 0x01ce +#define RT5663_HP_IMP_SEN_16 0x01cf +#define RT5663_HP_IMP_SEN_17 0x01d0 +#define RT5663_HP_IMP_SEN_18 0x01d1 +#define RT5663_HP_IMP_SEN_19 0x01d2 +#define RT5663_HP_IMPSEN_DIG5 0x01d3 +#define RT5663_HP_IMPSEN_MAP1 0x01d4 +#define RT5663_HP_IMPSEN_MAP2 0x01d5 +#define RT5663_HP_IMPSEN_MAP3 0x01d6 +#define RT5663_HP_IMPSEN_MAP4 0x01d7 +#define RT5663_HP_IMPSEN_MAP5 0x01d8 +#define RT5663_HP_IMPSEN_MAP7 0x01d9 +#define RT5663_HP_LOGIC_1 0x01da +#define RT5663_HP_LOGIC_2 0x01db +#define RT5663_HP_CALIB_1 0x01dd +#define RT5663_HP_CALIB_1_1 0x01de +#define RT5663_HP_CALIB_2 0x01df +#define RT5663_HP_CALIB_3 0x01e0 +#define RT5663_HP_CALIB_4 0x01e1 +#define RT5663_HP_CALIB_5 0x01e2 +#define RT5663_HP_CALIB_5_1 0x01e3 +#define RT5663_HP_CALIB_6 0x01e4 +#define RT5663_HP_CALIB_7 0x01e5 +#define RT5663_HP_CALIB_9 0x01e6 +#define RT5663_HP_CALIB_10 0x01e7 +#define RT5663_HP_CALIB_11 0x01e8 +#define RT5663_HP_CALIB_ST1 0x01ea +#define RT5663_HP_CALIB_ST2 0x01eb +#define RT5663_HP_CALIB_ST3 0x01ec +#define RT5663_HP_CALIB_ST4 0x01ed +#define RT5663_HP_CALIB_ST5 0x01ee +#define RT5663_HP_CALIB_ST6 0x01ef +#define RT5663_HP_CALIB_ST7 0x01f0 +#define RT5663_HP_CALIB_ST8 0x01f1 +#define RT5663_HP_CALIB_ST9 0x01f2 +#define RT5663_HP_AMP_DET 0x0200 +#define RT5663_DUMMY_REG_6 0x0201 +#define RT5663_HP_BIAS 0x0202 +#define RT5663_CBJ_1 0x0250 +#define RT5663_CBJ_2 0x0251 +#define RT5663_CBJ_3 0x0252 +#define RT5663_DUMMY_1 0x02fa +#define RT5663_DUMMY_2 0x02fb +#define RT5663_DUMMY_3 0x02fc +#define RT5663_ANA_JD 0x0300 +#define RT5663_ADC_LCH_LPF1_A1 0x03d0 +#define RT5663_ADC_RCH_LPF1_A1 0x03d1 +#define RT5663_ADC_LCH_LPF1_H0 0x03d2 +#define RT5663_ADC_RCH_LPF1_H0 0x03d3 +#define RT5663_ADC_LCH_BPF1_A1 0x03d4 +#define RT5663_ADC_RCH_BPF1_A1 0x03d5 +#define RT5663_ADC_LCH_BPF1_A2 0x03d6 +#define RT5663_ADC_RCH_BPF1_A2 0x03d7 +#define RT5663_ADC_LCH_BPF1_H0 0x03d8 +#define RT5663_ADC_RCH_BPF1_H0 0x03d9 +#define RT5663_ADC_LCH_BPF2_A1 0x03da +#define RT5663_ADC_RCH_BPF2_A1 0x03db +#define RT5663_ADC_LCH_BPF2_A2 0x03dc +#define RT5663_ADC_RCH_BPF2_A2 0x03dd +#define RT5663_ADC_LCH_BPF2_H0 0x03de +#define RT5663_ADC_RCH_BPF2_H0 0x03df +#define RT5663_ADC_LCH_BPF3_A1 0x03e0 +#define RT5663_ADC_RCH_BPF3_A1 0x03e1 +#define RT5663_ADC_LCH_BPF3_A2 0x03e2 +#define RT5663_ADC_RCH_BPF3_A2 0x03e3 +#define RT5663_ADC_LCH_BPF3_H0 0x03e4 +#define RT5663_ADC_RCH_BPF3_H0 0x03e5 +#define RT5663_ADC_LCH_BPF4_A1 0x03e6 +#define RT5663_ADC_RCH_BPF4_A1 0x03e7 +#define RT5663_ADC_LCH_BPF4_A2 0x03e8 +#define RT5663_ADC_RCH_BPF4_A2 0x03e9 +#define RT5663_ADC_LCH_BPF4_H0 0x03ea +#define RT5663_ADC_RCH_BPF4_H0 0x03eb +#define RT5663_ADC_LCH_HPF1_A1 0x03ec +#define RT5663_ADC_RCH_HPF1_A1 0x03ed +#define RT5663_ADC_LCH_HPF1_H0 0x03ee +#define RT5663_ADC_RCH_HPF1_H0 0x03ef +#define RT5663_ADC_EQ_PRE_VOL_L 0x03f0 +#define RT5663_ADC_EQ_PRE_VOL_R 0x03f1 +#define RT5663_ADC_EQ_POST_VOL_L 0x03f2 +#define RT5663_ADC_EQ_POST_VOL_R 0x03f3 + +/* RECMIX Control (0x0010) */ +#define RT5663_RECMIX1_BST1_MASK (0x1) +#define RT5663_RECMIX1_BST1_SHIFT 0 +#define RT5663_RECMIX1_BST1_ON (0x0) +#define RT5663_RECMIX1_BST1_OFF (0x1) + +/* Bypass Stereo1 DAC Mixer Control (0x002d) */ +#define RT5663_DACL1_SRC_MASK (0x1 << 3) +#define RT5663_DACL1_SRC_SHIFT 3 +#define RT5663_DACR1_SRC_MASK (0x1 << 2) +#define RT5663_DACR1_SRC_SHIFT 2 + +/* TDM control 2 (0x0078) */ +#define RT5663_DATA_SWAP_ADCDAT1_MASK (0x3 << 14) +#define RT5663_DATA_SWAP_ADCDAT1_SHIFT 14 +#define RT5663_DATA_SWAP_ADCDAT1_LR (0x0 << 14) +#define RT5663_DATA_SWAP_ADCDAT1_RL (0x1 << 14) +#define RT5663_DATA_SWAP_ADCDAT1_LL (0x2 << 14) +#define RT5663_DATA_SWAP_ADCDAT1_RR (0x3 << 14) + +/* TDM control 5 (0x007b) */ +#define RT5663_TDM_LENGTN_MASK (0x3) +#define RT5663_TDM_LENGTN_SHIFT 0 +#define RT5663_TDM_LENGTN_16 (0x0) +#define RT5663_TDM_LENGTN_20 (0x1) +#define RT5663_TDM_LENGTN_24 (0x2) +#define RT5663_TDM_LENGTN_32 (0x3) + +/* PLL tracking mode 1 (0x0083) */ +#define RT5663_I2S1_ASRC_MASK (0x1 << 11) +#define RT5663_I2S1_ASRC_SHIFT 11 +#define RT5663_DAC_STO1_ASRC_MASK (0x1 << 10) +#define RT5663_DAC_STO1_ASRC_SHIFT 10 +#define RT5663_ADC_STO1_ASRC_MASK (0x1 << 3) +#define RT5663_ADC_STO1_ASRC_SHIFT 3 + +/* PLL tracking mode 2 (0x0084)*/ +#define RT5663_DA_STO1_TRACK_MASK (0x7 << 12) +#define RT5663_DA_STO1_TRACK_SHIFT 12 +#define RT5663_DA_STO1_TRACK_SYSCLK (0x0 << 12) +#define RT5663_DA_STO1_TRACK_I2S1 (0x1 << 12) +#define RT5663_AD_STO1_TRACK_MASK (0x7) +#define RT5663_AD_STO1_TRACK_SHIFT 0 +#define RT5663_AD_STO1_TRACK_SYSCLK (0x0) +#define RT5663_AD_STO1_TRACK_I2S1 (0x1) + +/* HPOUT Charge pump control 1 (0x0091) */ +#define RT5663_SI_HP_MASK (0x1 << 12) +#define RT5663_SI_HP_SHIFT 12 +#define RT5663_SI_HP_EN (0x1 << 12) +#define RT5663_SI_HP_DIS (0x0 << 12) + +/* GPIO Control 2 (0x00b6) */ +#define RT5663_GP1_PIN_CONF_MASK (0x1 << 2) +#define RT5663_GP1_PIN_CONF_SHIFT 2 +#define RT5663_GP1_PIN_CONF_OUTPUT (0x1 << 2) +#define RT5663_GP1_PIN_CONF_INPUT (0x0 << 2) + +/* GPIO Control 2 (0x00b7) */ +#define RT5663_EN_IRQ_INLINE_MASK (0x1 << 3) +#define RT5663_EN_IRQ_INLINE_SHIFT 3 +#define RT5663_EN_IRQ_INLINE_NOR (0x1 << 3) +#define RT5663_EN_IRQ_INLINE_BYP (0x0 << 3) + +/* GPIO Control 1 (0x00c0) */ +#define RT5663_GPIO1_TYPE_MASK (0x1 << 15) +#define RT5663_GPIO1_TYPE_SHIFT 15 +#define RT5663_GPIO1_TYPE_EN (0x1 << 15) +#define RT5663_GPIO1_TYPE_DIS (0x0 << 15) + +/* IRQ Control 1 (0x00c1) */ +#define RT5663_EN_IRQ_JD1_MASK (0x1 << 6) +#define RT5663_EN_IRQ_JD1_SHIFT 6 +#define RT5663_EN_IRQ_JD1_EN (0x1 << 6) +#define RT5663_EN_IRQ_JD1_DIS (0x0 << 6) +#define RT5663_SEL_GPIO1_MASK (0x1 << 2) +#define RT5663_SEL_GPIO1_SHIFT 6 +#define RT5663_SEL_GPIO1_EN (0x1 << 2) +#define RT5663_SEL_GPIO1_DIS (0x0 << 2) + +/* Inline Command Function 2 (0x00dc) */ +#define RT5663_PWR_MIC_DET_MASK (0x1) +#define RT5663_PWR_MIC_DET_SHIFT 0 +#define RT5663_PWR_MIC_DET_ON (0x1) +#define RT5663_PWR_MIC_DET_OFF (0x0) + +/* Embeeded Jack and Type Detection Control 1 (0x00e6)*/ +#define RT5663_CBJ_DET_MASK (0x1 << 15) +#define RT5663_CBJ_DET_SHIFT 15 +#define RT5663_CBJ_DET_DIS (0x0 << 15) +#define RT5663_CBJ_DET_EN (0x1 << 15) +#define RT5663_EXT_JD_MASK (0x1 << 11) +#define RT5663_EXT_JD_SHIFT 11 +#define RT5663_EXT_JD_EN (0x1 << 11) +#define RT5663_EXT_JD_DIS (0x0 << 11) +#define RT5663_POL_EXT_JD_MASK (0x1 << 10) +#define RT5663_POL_EXT_JD_SHIFT 10 +#define RT5663_POL_EXT_JD_EN (0x1 << 10) +#define RT5663_POL_EXT_JD_DIS (0x0 << 10) + +/* DACREF LDO Control (0x0112)*/ +#define RT5663_PWR_LDO_DACREFL_MASK (0x1 << 9) +#define RT5663_PWR_LDO_DACREFL_SHIFT 9 +#define RT5663_PWR_LDO_DACREFR_MASK (0x1 << 1) +#define RT5663_PWR_LDO_DACREFR_SHIFT 1 + +/* Stereo Dynamic Range Enhancement Control 9 (0x0168, 0x0169)*/ +#define RT5663_DRE_GAIN_HP_MASK (0x1f) +#define RT5663_DRE_GAIN_HP_SHIFT 0 + +/* Combo Jack Control (0x0250) */ +#define RT5663_INBUF_CBJ_BST1_MASK (0x1 << 11) +#define RT5663_INBUF_CBJ_BST1_SHIFT 11 +#define RT5663_INBUF_CBJ_BST1_ON (0x1 << 11) +#define RT5663_INBUF_CBJ_BST1_OFF (0x0 << 11) +#define RT5663_CBJ_SENSE_BST1_MASK (0x1 << 10) +#define RT5663_CBJ_SENSE_BST1_SHIFT 10 +#define RT5663_CBJ_SENSE_BST1_L (0x1 << 10) +#define RT5663_CBJ_SENSE_BST1_R (0x0 << 10) + +/* Combo Jack Control (0x0251) */ +#define RT5663_GAIN_BST1_MASK (0xf) +#define RT5663_GAIN_BST1_SHIFT 0 + +/* Dummy register 1 (0x02fa) */ +#define RT5663_EMB_CLK_MASK (0x1 << 9) +#define RT5663_EMB_CLK_SHIFT 9 +#define RT5663_EMB_CLK_EN (0x1 << 9) +#define RT5663_EMB_CLK_DIS (0x0 << 9) +#define RT5663_HPA_CPL_BIAS_MASK (0x7 << 6) +#define RT5663_HPA_CPL_BIAS_SHIFT 6 +#define RT5663_HPA_CPL_BIAS_0_5 (0x0 << 6) +#define RT5663_HPA_CPL_BIAS_1 (0x1 << 6) +#define RT5663_HPA_CPL_BIAS_2 (0x2 << 6) +#define RT5663_HPA_CPL_BIAS_3 (0x3 << 6) +#define RT5663_HPA_CPL_BIAS_4_1 (0x4 << 6) +#define RT5663_HPA_CPL_BIAS_4_2 (0x5 << 6) +#define RT5663_HPA_CPL_BIAS_6 (0x6 << 6) +#define RT5663_HPA_CPL_BIAS_8 (0x7 << 6) +#define RT5663_HPA_CPR_BIAS_MASK (0x7 << 3) +#define RT5663_HPA_CPR_BIAS_SHIFT 3 +#define RT5663_HPA_CPR_BIAS_0_5 (0x0 << 3) +#define RT5663_HPA_CPR_BIAS_1 (0x1 << 3) +#define RT5663_HPA_CPR_BIAS_2 (0x2 << 3) +#define RT5663_HPA_CPR_BIAS_3 (0x3 << 3) +#define RT5663_HPA_CPR_BIAS_4_1 (0x4 << 3) +#define RT5663_HPA_CPR_BIAS_4_2 (0x5 << 3) +#define RT5663_HPA_CPR_BIAS_6 (0x6 << 3) +#define RT5663_HPA_CPR_BIAS_8 (0x7 << 3) +#define RT5663_DUMMY_BIAS_MASK (0x7) +#define RT5663_DUMMY_BIAS_SHIFT 0 +#define RT5663_DUMMY_BIAS_0_5 (0x0) +#define RT5663_DUMMY_BIAS_1 (0x1) +#define RT5663_DUMMY_BIAS_2 (0x2) +#define RT5663_DUMMY_BIAS_3 (0x3) +#define RT5663_DUMMY_BIAS_4_1 (0x4) +#define RT5663_DUMMY_BIAS_4_2 (0x5) +#define RT5663_DUMMY_BIAS_6 (0x6) +#define RT5663_DUMMY_BIAS_8 (0x7) + + +/* System Clock Source */ +enum { + RT5663_SCLK_S_MCLK, + RT5663_SCLK_S_PLL1, + RT5663_SCLK_S_RCCLK, +}; + +/* PLL1 Source */ +enum { + RT5663_PLL1_S_MCLK, + RT5663_PLL1_S_BCLK1, +}; + +enum { + RT5663_AIF, + RT5663_AIFS, +}; + +/* asrc clock source */ +enum { + RT5663_CLK_SEL_SYS = 0x0, + RT5663_CLK_SEL_I2S1_ASRC = 0x1, +}; + +/* filter mask */ +enum { + RT5663_DA_STEREO_FILTER = 0x1, + RT5663_AD_STEREO_FILTER = 0x2, +}; + +int rt5663_set_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *hs_jack); +int rt5663_sel_asrc_clk_src(struct snd_soc_codec *codec, + unsigned int filter_mask, unsigned int clk_src); + +#endif /* __RT5663_H__ */ diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c new file mode 100644 index 000000000000..324461e985b3 --- /dev/null +++ b/sound/soc/codecs/rt5665.c @@ -0,0 +1,4874 @@ +/* + * rt5665.c -- RT5665/RT5658 ALSA SoC audio codec driver + * + * Copyright 2016 Realtek Semiconductor Corp. + * Author: Bard Liao <bardliao@realtek.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/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/acpi.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/regulator/consumer.h> +#include <linux/mutex.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/jack.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <sound/rt5665.h> + +#include "rl6231.h" +#include "rt5665.h" + +#define RT5665_NUM_SUPPLIES 3 + +static const char *rt5665_supply_names[RT5665_NUM_SUPPLIES] = { + "AVDD", + "MICVDD", + "VBAT", +}; + +struct rt5665_priv { + struct snd_soc_codec *codec; + struct rt5665_platform_data pdata; + struct regmap *regmap; + struct gpio_desc *gpiod_ldo1_en; + struct gpio_desc *gpiod_reset; + struct snd_soc_jack *hs_jack; + struct regulator_bulk_data supplies[RT5665_NUM_SUPPLIES]; + struct delayed_work jack_detect_work; + struct delayed_work calibrate_work; + struct delayed_work jd_check_work; + struct mutex calibrate_mutex; + + int sysclk; + int sysclk_src; + int lrck[RT5665_AIFS]; + int bclk[RT5665_AIFS]; + int master[RT5665_AIFS]; + int id; + + int pll_src; + int pll_in; + int pll_out; + + int jack_type; + int irq_work_delay_time; + unsigned int sar_adc_value; +}; + +static const struct reg_default rt5665_reg[] = { + {0x0000, 0x0000}, + {0x0001, 0xc8c8}, + {0x0002, 0x8080}, + {0x0003, 0x8000}, + {0x0004, 0xc80a}, + {0x0005, 0x0000}, + {0x0006, 0x0000}, + {0x0007, 0x0000}, + {0x000a, 0x0000}, + {0x000b, 0x0000}, + {0x000c, 0x0000}, + {0x000d, 0x0000}, + {0x000f, 0x0808}, + {0x0010, 0x4040}, + {0x0011, 0x0000}, + {0x0012, 0x1404}, + {0x0013, 0x1000}, + {0x0014, 0xa00a}, + {0x0015, 0x0404}, + {0x0016, 0x0404}, + {0x0017, 0x0011}, + {0x0018, 0xafaf}, + {0x0019, 0xafaf}, + {0x001a, 0xafaf}, + {0x001b, 0x0011}, + {0x001c, 0x2f2f}, + {0x001d, 0x2f2f}, + {0x001e, 0x2f2f}, + {0x001f, 0x0000}, + {0x0020, 0x0000}, + {0x0021, 0x0000}, + {0x0022, 0x5757}, + {0x0023, 0x0039}, + {0x0026, 0xc0c0}, + {0x0027, 0xc0c0}, + {0x0028, 0xc0c0}, + {0x0029, 0x8080}, + {0x002a, 0xaaaa}, + {0x002b, 0xaaaa}, + {0x002c, 0xaba8}, + {0x002d, 0x0000}, + {0x002e, 0x0000}, + {0x002f, 0x0000}, + {0x0030, 0x0000}, + {0x0031, 0x5000}, + {0x0032, 0x0000}, + {0x0033, 0x0000}, + {0x0034, 0x0000}, + {0x0035, 0x0000}, + {0x003a, 0x0000}, + {0x003b, 0x0000}, + {0x003c, 0x00ff}, + {0x003d, 0x0000}, + {0x003e, 0x00ff}, + {0x003f, 0x0000}, + {0x0040, 0x0000}, + {0x0041, 0x00ff}, + {0x0042, 0x0000}, + {0x0043, 0x00ff}, + {0x0044, 0x0c0c}, + {0x0049, 0xc00b}, + {0x004a, 0x0000}, + {0x004b, 0x031f}, + {0x004d, 0x0000}, + {0x004e, 0x001f}, + {0x004f, 0x0000}, + {0x0050, 0x001f}, + {0x0052, 0xf000}, + {0x0061, 0x0000}, + {0x0062, 0x0000}, + {0x0063, 0x003e}, + {0x0064, 0x0000}, + {0x0065, 0x0000}, + {0x0066, 0x003f}, + {0x0067, 0x0000}, + {0x006b, 0x0000}, + {0x006d, 0xff00}, + {0x006e, 0x2808}, + {0x006f, 0x000a}, + {0x0070, 0x8000}, + {0x0071, 0x8000}, + {0x0072, 0x8000}, + {0x0073, 0x7000}, + {0x0074, 0x7770}, + {0x0075, 0x0002}, + {0x0076, 0x0001}, + {0x0078, 0x00f0}, + {0x0079, 0x0000}, + {0x007a, 0x0000}, + {0x007b, 0x0000}, + {0x007c, 0x0000}, + {0x007d, 0x0123}, + {0x007e, 0x4500}, + {0x007f, 0x8003}, + {0x0080, 0x0000}, + {0x0081, 0x0000}, + {0x0082, 0x0000}, + {0x0083, 0x0000}, + {0x0084, 0x0000}, + {0x0085, 0x0000}, + {0x0086, 0x0008}, + {0x0087, 0x0000}, + {0x0088, 0x0000}, + {0x0089, 0x0000}, + {0x008a, 0x0000}, + {0x008b, 0x0000}, + {0x008c, 0x0003}, + {0x008e, 0x0060}, + {0x008f, 0x1000}, + {0x0091, 0x0c26}, + {0x0092, 0x0073}, + {0x0093, 0x0000}, + {0x0094, 0x0080}, + {0x0098, 0x0000}, + {0x0099, 0x0000}, + {0x009a, 0x0007}, + {0x009f, 0x0000}, + {0x00a0, 0x0000}, + {0x00a1, 0x0002}, + {0x00a2, 0x0001}, + {0x00a3, 0x0002}, + {0x00a4, 0x0001}, + {0x00ae, 0x2040}, + {0x00af, 0x0000}, + {0x00b6, 0x0000}, + {0x00b7, 0x0000}, + {0x00b8, 0x0000}, + {0x00b9, 0x0000}, + {0x00ba, 0x0002}, + {0x00bb, 0x0000}, + {0x00be, 0x0000}, + {0x00c0, 0x0000}, + {0x00c1, 0x0aaa}, + {0x00c2, 0xaa80}, + {0x00c3, 0x0003}, + {0x00c4, 0x0000}, + {0x00d0, 0x0000}, + {0x00d1, 0x2244}, + {0x00d3, 0x3300}, + {0x00d4, 0x2200}, + {0x00d9, 0x0809}, + {0x00da, 0x0000}, + {0x00db, 0x0008}, + {0x00dc, 0x00c0}, + {0x00dd, 0x6724}, + {0x00de, 0x3131}, + {0x00df, 0x0008}, + {0x00e0, 0x4000}, + {0x00e1, 0x3131}, + {0x00e2, 0x600c}, + {0x00ea, 0xb320}, + {0x00eb, 0x0000}, + {0x00ec, 0xb300}, + {0x00ed, 0x0000}, + {0x00ee, 0xb320}, + {0x00ef, 0x0000}, + {0x00f0, 0x0201}, + {0x00f1, 0x0ddd}, + {0x00f2, 0x0ddd}, + {0x00f6, 0x0000}, + {0x00f7, 0x0000}, + {0x00f8, 0x0000}, + {0x00fa, 0x0000}, + {0x00fb, 0x0000}, + {0x00fc, 0x0000}, + {0x00fd, 0x0000}, + {0x00fe, 0x10ec}, + {0x00ff, 0x6451}, + {0x0100, 0xaaaa}, + {0x0101, 0x000a}, + {0x010a, 0xaaaa}, + {0x010b, 0xa0a0}, + {0x010c, 0xaeae}, + {0x010d, 0xaaaa}, + {0x010e, 0xaaaa}, + {0x010f, 0xaaaa}, + {0x0110, 0xe002}, + {0x0111, 0xa402}, + {0x0112, 0xaaaa}, + {0x0113, 0x2000}, + {0x0117, 0x0f00}, + {0x0125, 0x0410}, + {0x0132, 0x0000}, + {0x0133, 0x0000}, + {0x0137, 0x5540}, + {0x0138, 0x3700}, + {0x0139, 0x79a1}, + {0x013a, 0x2020}, + {0x013b, 0x2020}, + {0x013c, 0x2005}, + {0x013f, 0x0000}, + {0x0145, 0x0002}, + {0x0146, 0x0000}, + {0x0147, 0x0000}, + {0x0148, 0x0000}, + {0x0150, 0x0000}, + {0x0160, 0x4eff}, + {0x0161, 0x0080}, + {0x0162, 0x0200}, + {0x0163, 0x0800}, + {0x0164, 0x0000}, + {0x0165, 0x0000}, + {0x0166, 0x0000}, + {0x0167, 0x000f}, + {0x0170, 0x4e87}, + {0x0171, 0x0080}, + {0x0172, 0x0200}, + {0x0173, 0x0800}, + {0x0174, 0x00ff}, + {0x0175, 0x0000}, + {0x0190, 0x413d}, + {0x0191, 0x4139}, + {0x0192, 0x4135}, + {0x0193, 0x413d}, + {0x0194, 0x0000}, + {0x0195, 0x0000}, + {0x0196, 0x0000}, + {0x0197, 0x0000}, + {0x0198, 0x0000}, + {0x0199, 0x0000}, + {0x01a0, 0x1e64}, + {0x01a1, 0x06a3}, + {0x01a2, 0x0000}, + {0x01a3, 0x0000}, + {0x01a4, 0x0000}, + {0x01a5, 0x0000}, + {0x01a6, 0x0000}, + {0x01a7, 0x8000}, + {0x01a8, 0x0000}, + {0x01a9, 0x0000}, + {0x01aa, 0x0000}, + {0x01ab, 0x0000}, + {0x01b5, 0x0000}, + {0x01b6, 0x01c3}, + {0x01b7, 0x02a0}, + {0x01b8, 0x03e9}, + {0x01b9, 0x1389}, + {0x01ba, 0xc351}, + {0x01bb, 0x0009}, + {0x01bc, 0x0018}, + {0x01bd, 0x002a}, + {0x01be, 0x004c}, + {0x01bf, 0x0097}, + {0x01c0, 0x433d}, + {0x01c1, 0x0000}, + {0x01c2, 0x0000}, + {0x01c3, 0x0000}, + {0x01c4, 0x0000}, + {0x01c5, 0x0000}, + {0x01c6, 0x0000}, + {0x01c7, 0x0000}, + {0x01c8, 0x40af}, + {0x01c9, 0x0702}, + {0x01ca, 0x0000}, + {0x01cb, 0x0000}, + {0x01cc, 0x5757}, + {0x01cd, 0x5757}, + {0x01ce, 0x5757}, + {0x01cf, 0x5757}, + {0x01d0, 0x5757}, + {0x01d1, 0x5757}, + {0x01d2, 0x5757}, + {0x01d3, 0x5757}, + {0x01d4, 0x5757}, + {0x01d5, 0x5757}, + {0x01d6, 0x003c}, + {0x01da, 0x0000}, + {0x01db, 0x0000}, + {0x01dc, 0x0000}, + {0x01de, 0x7c00}, + {0x01df, 0x0320}, + {0x01e0, 0x06a1}, + {0x01e1, 0x0000}, + {0x01e2, 0x0000}, + {0x01e3, 0x0000}, + {0x01e4, 0x0000}, + {0x01e6, 0x0001}, + {0x01e7, 0x0000}, + {0x01e8, 0x0000}, + {0x01ea, 0xbf3f}, + {0x01eb, 0x0000}, + {0x01ec, 0x0000}, + {0x01ed, 0x0000}, + {0x01ee, 0x0000}, + {0x01ef, 0x0000}, + {0x01f0, 0x0000}, + {0x01f1, 0x0000}, + {0x01f2, 0x0000}, + {0x01f3, 0x0000}, + {0x01f4, 0x0000}, + {0x0200, 0x0000}, + {0x0201, 0x0000}, + {0x0202, 0x0000}, + {0x0203, 0x0000}, + {0x0204, 0x0000}, + {0x0205, 0x0000}, + {0x0206, 0x0000}, + {0x0207, 0x0000}, + {0x0208, 0x0000}, + {0x0210, 0x60b1}, + {0x0211, 0xa005}, + {0x0212, 0x024c}, + {0x0213, 0xf7ff}, + {0x0214, 0x024c}, + {0x0215, 0x0102}, + {0x0216, 0x00a3}, + {0x0217, 0x0048}, + {0x0218, 0xa2c0}, + {0x0219, 0x0400}, + {0x021a, 0x00c8}, + {0x021b, 0x00c0}, + {0x02ff, 0x0110}, + {0x0300, 0x001f}, + {0x0301, 0x032c}, + {0x0302, 0x5f21}, + {0x0303, 0x4000}, + {0x0304, 0x4000}, + {0x0305, 0x06d5}, + {0x0306, 0x8000}, + {0x0307, 0x0700}, + {0x0310, 0x4560}, + {0x0311, 0xa4a8}, + {0x0312, 0x7418}, + {0x0313, 0x0000}, + {0x0314, 0x0006}, + {0x0315, 0xffff}, + {0x0316, 0xc400}, + {0x0317, 0x0000}, + {0x0330, 0x00a6}, + {0x0331, 0x04c3}, + {0x0332, 0x27c8}, + {0x0333, 0xbf50}, + {0x0334, 0x0045}, + {0x0335, 0x0007}, + {0x0336, 0x7418}, + {0x0337, 0x0501}, + {0x0338, 0x0000}, + {0x0339, 0x0010}, + {0x033a, 0x1010}, + {0x03c0, 0x7e00}, + {0x03c1, 0x8000}, + {0x03c2, 0x8000}, + {0x03c3, 0x8000}, + {0x03c4, 0x8000}, + {0x03c5, 0x8000}, + {0x03c6, 0x8000}, + {0x03c7, 0x8000}, + {0x03c8, 0x8000}, + {0x03c9, 0x8000}, + {0x03ca, 0x8000}, + {0x03cb, 0x8000}, + {0x03cc, 0x8000}, + {0x03d0, 0x0000}, + {0x03d1, 0x0000}, + {0x03d2, 0x0000}, + {0x03d3, 0x0000}, + {0x03d4, 0x2000}, + {0x03d5, 0x2000}, + {0x03d6, 0x0000}, + {0x03d7, 0x0000}, + {0x03d8, 0x2000}, + {0x03d9, 0x2000}, + {0x03da, 0x2000}, + {0x03db, 0x2000}, + {0x03dc, 0x0000}, + {0x03dd, 0x0000}, + {0x03de, 0x0000}, + {0x03df, 0x2000}, + {0x03e0, 0x0000}, + {0x03e1, 0x0000}, + {0x03e2, 0x0000}, + {0x03e3, 0x0000}, + {0x03e4, 0x0000}, + {0x03e5, 0x0000}, + {0x03e6, 0x0000}, + {0x03e7, 0x0000}, + {0x03e8, 0x0000}, + {0x03e9, 0x0000}, + {0x03ea, 0x0000}, + {0x03eb, 0x0000}, + {0x03ec, 0x0000}, + {0x03ed, 0x0000}, + {0x03ee, 0x0000}, + {0x03ef, 0x0000}, + {0x03f0, 0x0800}, + {0x03f1, 0x0800}, + {0x03f2, 0x0800}, + {0x03f3, 0x0800}, +}; + +static bool rt5665_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5665_RESET: + case RT5665_EJD_CTRL_2: + case RT5665_GPIO_STA: + case RT5665_INT_ST_1: + case RT5665_IL_CMD_1: + case RT5665_4BTN_IL_CMD_1: + case RT5665_PSV_IL_CMD_1: + case RT5665_AJD1_CTRL: + case RT5665_JD_CTRL_3: + case RT5665_STO_NG2_CTRL_1: + case RT5665_SAR_IL_CMD_4: + case RT5665_DEVICE_ID: + case RT5665_STO1_DAC_SIL_DET ... RT5665_STO2_DAC_SIL_DET: + case RT5665_MONO_AMP_CALIB_STA1 ... RT5665_MONO_AMP_CALIB_STA6: + case RT5665_HP_IMP_SENS_CTRL_12 ... RT5665_HP_IMP_SENS_CTRL_15: + case RT5665_HP_CALIB_STA_1 ... RT5665_HP_CALIB_STA_11: + return true; + default: + return false; + } +} + +static bool rt5665_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5665_RESET: + case RT5665_VENDOR_ID: + case RT5665_VENDOR_ID_1: + case RT5665_DEVICE_ID: + case RT5665_LOUT: + case RT5665_HP_CTRL_1: + case RT5665_HP_CTRL_2: + case RT5665_MONO_OUT: + case RT5665_HPL_GAIN: + case RT5665_HPR_GAIN: + case RT5665_MONO_GAIN: + case RT5665_CAL_BST_CTRL: + case RT5665_CBJ_BST_CTRL: + case RT5665_IN1_IN2: + case RT5665_IN3_IN4: + case RT5665_INL1_INR1_VOL: + case RT5665_EJD_CTRL_1: + case RT5665_EJD_CTRL_2: + case RT5665_EJD_CTRL_3: + case RT5665_EJD_CTRL_4: + case RT5665_EJD_CTRL_5: + case RT5665_EJD_CTRL_6: + case RT5665_EJD_CTRL_7: + case RT5665_DAC2_CTRL: + case RT5665_DAC2_DIG_VOL: + case RT5665_DAC1_DIG_VOL: + case RT5665_DAC3_DIG_VOL: + case RT5665_DAC3_CTRL: + case RT5665_STO1_ADC_DIG_VOL: + case RT5665_MONO_ADC_DIG_VOL: + case RT5665_STO2_ADC_DIG_VOL: + case RT5665_STO1_ADC_BOOST: + case RT5665_MONO_ADC_BOOST: + case RT5665_STO2_ADC_BOOST: + case RT5665_HP_IMP_GAIN_1: + case RT5665_HP_IMP_GAIN_2: + case RT5665_STO1_ADC_MIXER: + case RT5665_MONO_ADC_MIXER: + case RT5665_STO2_ADC_MIXER: + case RT5665_AD_DA_MIXER: + case RT5665_STO1_DAC_MIXER: + case RT5665_MONO_DAC_MIXER: + case RT5665_STO2_DAC_MIXER: + case RT5665_A_DAC1_MUX: + case RT5665_A_DAC2_MUX: + case RT5665_DIG_INF2_DATA: + case RT5665_DIG_INF3_DATA: + case RT5665_PDM_OUT_CTRL: + case RT5665_PDM_DATA_CTRL_1: + case RT5665_PDM_DATA_CTRL_2: + case RT5665_PDM_DATA_CTRL_3: + case RT5665_PDM_DATA_CTRL_4: + case RT5665_REC1_GAIN: + case RT5665_REC1_L1_MIXER: + case RT5665_REC1_L2_MIXER: + case RT5665_REC1_R1_MIXER: + case RT5665_REC1_R2_MIXER: + case RT5665_REC2_GAIN: + case RT5665_REC2_L1_MIXER: + case RT5665_REC2_L2_MIXER: + case RT5665_REC2_R1_MIXER: + case RT5665_REC2_R2_MIXER: + case RT5665_CAL_REC: + case RT5665_ALC_BACK_GAIN: + case RT5665_MONOMIX_GAIN: + case RT5665_MONOMIX_IN_GAIN: + case RT5665_OUT_L_GAIN: + case RT5665_OUT_L_MIXER: + case RT5665_OUT_R_GAIN: + case RT5665_OUT_R_MIXER: + case RT5665_LOUT_MIXER: + case RT5665_PWR_DIG_1: + case RT5665_PWR_DIG_2: + case RT5665_PWR_ANLG_1: + case RT5665_PWR_ANLG_2: + case RT5665_PWR_ANLG_3: + case RT5665_PWR_MIXER: + case RT5665_PWR_VOL: + case RT5665_CLK_DET: + case RT5665_HPF_CTRL1: + case RT5665_DMIC_CTRL_1: + case RT5665_DMIC_CTRL_2: + case RT5665_I2S1_SDP: + case RT5665_I2S2_SDP: + case RT5665_I2S3_SDP: + case RT5665_ADDA_CLK_1: + case RT5665_ADDA_CLK_2: + case RT5665_I2S1_F_DIV_CTRL_1: + case RT5665_I2S1_F_DIV_CTRL_2: + case RT5665_TDM_CTRL_1: + case RT5665_TDM_CTRL_2: + case RT5665_TDM_CTRL_3: + case RT5665_TDM_CTRL_4: + case RT5665_TDM_CTRL_5: + case RT5665_TDM_CTRL_6: + case RT5665_TDM_CTRL_7: + case RT5665_TDM_CTRL_8: + case RT5665_GLB_CLK: + case RT5665_PLL_CTRL_1: + case RT5665_PLL_CTRL_2: + case RT5665_ASRC_1: + case RT5665_ASRC_2: + case RT5665_ASRC_3: + case RT5665_ASRC_4: + case RT5665_ASRC_5: + case RT5665_ASRC_6: + case RT5665_ASRC_7: + case RT5665_ASRC_8: + case RT5665_ASRC_9: + case RT5665_ASRC_10: + case RT5665_DEPOP_1: + case RT5665_DEPOP_2: + case RT5665_HP_CHARGE_PUMP_1: + case RT5665_HP_CHARGE_PUMP_2: + case RT5665_MICBIAS_1: + case RT5665_MICBIAS_2: + case RT5665_ASRC_12: + case RT5665_ASRC_13: + case RT5665_ASRC_14: + case RT5665_RC_CLK_CTRL: + case RT5665_I2S_M_CLK_CTRL_1: + case RT5665_I2S2_F_DIV_CTRL_1: + case RT5665_I2S2_F_DIV_CTRL_2: + case RT5665_I2S3_F_DIV_CTRL_1: + case RT5665_I2S3_F_DIV_CTRL_2: + case RT5665_EQ_CTRL_1: + case RT5665_EQ_CTRL_2: + case RT5665_IRQ_CTRL_1: + case RT5665_IRQ_CTRL_2: + case RT5665_IRQ_CTRL_3: + case RT5665_IRQ_CTRL_4: + case RT5665_IRQ_CTRL_5: + case RT5665_IRQ_CTRL_6: + case RT5665_INT_ST_1: + case RT5665_GPIO_CTRL_1: + case RT5665_GPIO_CTRL_2: + case RT5665_GPIO_CTRL_3: + case RT5665_GPIO_CTRL_4: + case RT5665_GPIO_STA: + case RT5665_HP_AMP_DET_CTRL_1: + case RT5665_HP_AMP_DET_CTRL_2: + case RT5665_MID_HP_AMP_DET: + case RT5665_LOW_HP_AMP_DET: + case RT5665_SV_ZCD_1: + case RT5665_SV_ZCD_2: + case RT5665_IL_CMD_1: + case RT5665_IL_CMD_2: + case RT5665_IL_CMD_3: + case RT5665_IL_CMD_4: + case RT5665_4BTN_IL_CMD_1: + case RT5665_4BTN_IL_CMD_2: + case RT5665_4BTN_IL_CMD_3: + case RT5665_PSV_IL_CMD_1: + case RT5665_ADC_STO1_HP_CTRL_1: + case RT5665_ADC_STO1_HP_CTRL_2: + case RT5665_ADC_MONO_HP_CTRL_1: + case RT5665_ADC_MONO_HP_CTRL_2: + case RT5665_ADC_STO2_HP_CTRL_1: + case RT5665_ADC_STO2_HP_CTRL_2: + case RT5665_AJD1_CTRL: + case RT5665_JD1_THD: + case RT5665_JD2_THD: + case RT5665_JD_CTRL_1: + case RT5665_JD_CTRL_2: + case RT5665_JD_CTRL_3: + case RT5665_DIG_MISC: + case RT5665_DUMMY_2: + case RT5665_DUMMY_3: + case RT5665_DAC_ADC_DIG_VOL1: + case RT5665_DAC_ADC_DIG_VOL2: + case RT5665_BIAS_CUR_CTRL_1: + case RT5665_BIAS_CUR_CTRL_2: + case RT5665_BIAS_CUR_CTRL_3: + case RT5665_BIAS_CUR_CTRL_4: + case RT5665_BIAS_CUR_CTRL_5: + case RT5665_BIAS_CUR_CTRL_6: + case RT5665_BIAS_CUR_CTRL_7: + case RT5665_BIAS_CUR_CTRL_8: + case RT5665_BIAS_CUR_CTRL_9: + case RT5665_BIAS_CUR_CTRL_10: + case RT5665_VREF_REC_OP_FB_CAP_CTRL: + case RT5665_CHARGE_PUMP_1: + case RT5665_DIG_IN_CTRL_1: + case RT5665_DIG_IN_CTRL_2: + case RT5665_PAD_DRIVING_CTRL: + case RT5665_SOFT_RAMP_DEPOP: + case RT5665_PLL: + case RT5665_CHOP_DAC: + case RT5665_CHOP_ADC: + case RT5665_CALIB_ADC_CTRL: + case RT5665_VOL_TEST: + case RT5665_TEST_MODE_CTRL_1: + case RT5665_TEST_MODE_CTRL_2: + case RT5665_TEST_MODE_CTRL_3: + case RT5665_TEST_MODE_CTRL_4: + case RT5665_BASSBACK_CTRL: + case RT5665_STO_NG2_CTRL_1: + case RT5665_STO_NG2_CTRL_2: + case RT5665_STO_NG2_CTRL_3: + case RT5665_STO_NG2_CTRL_4: + case RT5665_STO_NG2_CTRL_5: + case RT5665_STO_NG2_CTRL_6: + case RT5665_STO_NG2_CTRL_7: + case RT5665_STO_NG2_CTRL_8: + case RT5665_MONO_NG2_CTRL_1: + case RT5665_MONO_NG2_CTRL_2: + case RT5665_MONO_NG2_CTRL_3: + case RT5665_MONO_NG2_CTRL_4: + case RT5665_MONO_NG2_CTRL_5: + case RT5665_MONO_NG2_CTRL_6: + case RT5665_STO1_DAC_SIL_DET: + case RT5665_MONOL_DAC_SIL_DET: + case RT5665_MONOR_DAC_SIL_DET: + case RT5665_STO2_DAC_SIL_DET: + case RT5665_SIL_PSV_CTRL1: + case RT5665_SIL_PSV_CTRL2: + case RT5665_SIL_PSV_CTRL3: + case RT5665_SIL_PSV_CTRL4: + case RT5665_SIL_PSV_CTRL5: + case RT5665_SIL_PSV_CTRL6: + case RT5665_MONO_AMP_CALIB_CTRL_1: + case RT5665_MONO_AMP_CALIB_CTRL_2: + case RT5665_MONO_AMP_CALIB_CTRL_3: + case RT5665_MONO_AMP_CALIB_CTRL_4: + case RT5665_MONO_AMP_CALIB_CTRL_5: + case RT5665_MONO_AMP_CALIB_CTRL_6: + case RT5665_MONO_AMP_CALIB_CTRL_7: + case RT5665_MONO_AMP_CALIB_STA1: + case RT5665_MONO_AMP_CALIB_STA2: + case RT5665_MONO_AMP_CALIB_STA3: + case RT5665_MONO_AMP_CALIB_STA4: + case RT5665_MONO_AMP_CALIB_STA6: + case RT5665_HP_IMP_SENS_CTRL_01: + case RT5665_HP_IMP_SENS_CTRL_02: + case RT5665_HP_IMP_SENS_CTRL_03: + case RT5665_HP_IMP_SENS_CTRL_04: + case RT5665_HP_IMP_SENS_CTRL_05: + case RT5665_HP_IMP_SENS_CTRL_06: + case RT5665_HP_IMP_SENS_CTRL_07: + case RT5665_HP_IMP_SENS_CTRL_08: + case RT5665_HP_IMP_SENS_CTRL_09: + case RT5665_HP_IMP_SENS_CTRL_10: + case RT5665_HP_IMP_SENS_CTRL_11: + case RT5665_HP_IMP_SENS_CTRL_12: + case RT5665_HP_IMP_SENS_CTRL_13: + case RT5665_HP_IMP_SENS_CTRL_14: + case RT5665_HP_IMP_SENS_CTRL_15: + case RT5665_HP_IMP_SENS_CTRL_16: + case RT5665_HP_IMP_SENS_CTRL_17: + case RT5665_HP_IMP_SENS_CTRL_18: + case RT5665_HP_IMP_SENS_CTRL_19: + case RT5665_HP_IMP_SENS_CTRL_20: + case RT5665_HP_IMP_SENS_CTRL_21: + case RT5665_HP_IMP_SENS_CTRL_22: + case RT5665_HP_IMP_SENS_CTRL_23: + case RT5665_HP_IMP_SENS_CTRL_24: + case RT5665_HP_IMP_SENS_CTRL_25: + case RT5665_HP_IMP_SENS_CTRL_26: + case RT5665_HP_IMP_SENS_CTRL_27: + case RT5665_HP_IMP_SENS_CTRL_28: + case RT5665_HP_IMP_SENS_CTRL_29: + case RT5665_HP_IMP_SENS_CTRL_30: + case RT5665_HP_IMP_SENS_CTRL_31: + case RT5665_HP_IMP_SENS_CTRL_32: + case RT5665_HP_IMP_SENS_CTRL_33: + case RT5665_HP_IMP_SENS_CTRL_34: + case RT5665_HP_LOGIC_CTRL_1: + case RT5665_HP_LOGIC_CTRL_2: + case RT5665_HP_LOGIC_CTRL_3: + case RT5665_HP_CALIB_CTRL_1: + case RT5665_HP_CALIB_CTRL_2: + case RT5665_HP_CALIB_CTRL_3: + case RT5665_HP_CALIB_CTRL_4: + case RT5665_HP_CALIB_CTRL_5: + case RT5665_HP_CALIB_CTRL_6: + case RT5665_HP_CALIB_CTRL_7: + case RT5665_HP_CALIB_CTRL_9: + case RT5665_HP_CALIB_CTRL_10: + case RT5665_HP_CALIB_CTRL_11: + case RT5665_HP_CALIB_STA_1: + case RT5665_HP_CALIB_STA_2: + case RT5665_HP_CALIB_STA_3: + case RT5665_HP_CALIB_STA_4: + case RT5665_HP_CALIB_STA_5: + case RT5665_HP_CALIB_STA_6: + case RT5665_HP_CALIB_STA_7: + case RT5665_HP_CALIB_STA_8: + case RT5665_HP_CALIB_STA_9: + case RT5665_HP_CALIB_STA_10: + case RT5665_HP_CALIB_STA_11: + case RT5665_PGM_TAB_CTRL1: + case RT5665_PGM_TAB_CTRL2: + case RT5665_PGM_TAB_CTRL3: + case RT5665_PGM_TAB_CTRL4: + case RT5665_PGM_TAB_CTRL5: + case RT5665_PGM_TAB_CTRL6: + case RT5665_PGM_TAB_CTRL7: + case RT5665_PGM_TAB_CTRL8: + case RT5665_PGM_TAB_CTRL9: + case RT5665_SAR_IL_CMD_1: + case RT5665_SAR_IL_CMD_2: + case RT5665_SAR_IL_CMD_3: + case RT5665_SAR_IL_CMD_4: + case RT5665_SAR_IL_CMD_5: + case RT5665_SAR_IL_CMD_6: + case RT5665_SAR_IL_CMD_7: + case RT5665_SAR_IL_CMD_8: + case RT5665_SAR_IL_CMD_9: + case RT5665_SAR_IL_CMD_10: + case RT5665_SAR_IL_CMD_11: + case RT5665_SAR_IL_CMD_12: + case RT5665_DRC1_CTRL_0: + case RT5665_DRC1_CTRL_1: + case RT5665_DRC1_CTRL_2: + case RT5665_DRC1_CTRL_3: + case RT5665_DRC1_CTRL_4: + case RT5665_DRC1_CTRL_5: + case RT5665_DRC1_CTRL_6: + case RT5665_DRC1_HARD_LMT_CTRL_1: + case RT5665_DRC1_HARD_LMT_CTRL_2: + case RT5665_DRC1_PRIV_1: + case RT5665_DRC1_PRIV_2: + case RT5665_DRC1_PRIV_3: + case RT5665_DRC1_PRIV_4: + case RT5665_DRC1_PRIV_5: + case RT5665_DRC1_PRIV_6: + case RT5665_DRC1_PRIV_7: + case RT5665_DRC1_PRIV_8: + case RT5665_ALC_PGA_CTRL_1: + case RT5665_ALC_PGA_CTRL_2: + case RT5665_ALC_PGA_CTRL_3: + case RT5665_ALC_PGA_CTRL_4: + case RT5665_ALC_PGA_CTRL_5: + case RT5665_ALC_PGA_CTRL_6: + case RT5665_ALC_PGA_CTRL_7: + case RT5665_ALC_PGA_CTRL_8: + case RT5665_ALC_PGA_STA_1: + case RT5665_ALC_PGA_STA_2: + case RT5665_ALC_PGA_STA_3: + case RT5665_EQ_AUTO_RCV_CTRL1: + case RT5665_EQ_AUTO_RCV_CTRL2: + case RT5665_EQ_AUTO_RCV_CTRL3: + case RT5665_EQ_AUTO_RCV_CTRL4: + case RT5665_EQ_AUTO_RCV_CTRL5: + case RT5665_EQ_AUTO_RCV_CTRL6: + case RT5665_EQ_AUTO_RCV_CTRL7: + case RT5665_EQ_AUTO_RCV_CTRL8: + case RT5665_EQ_AUTO_RCV_CTRL9: + case RT5665_EQ_AUTO_RCV_CTRL10: + case RT5665_EQ_AUTO_RCV_CTRL11: + case RT5665_EQ_AUTO_RCV_CTRL12: + case RT5665_EQ_AUTO_RCV_CTRL13: + case RT5665_ADC_L_EQ_LPF1_A1: + case RT5665_R_EQ_LPF1_A1: + case RT5665_L_EQ_LPF1_H0: + case RT5665_R_EQ_LPF1_H0: + case RT5665_L_EQ_BPF1_A1: + case RT5665_R_EQ_BPF1_A1: + case RT5665_L_EQ_BPF1_A2: + case RT5665_R_EQ_BPF1_A2: + case RT5665_L_EQ_BPF1_H0: + case RT5665_R_EQ_BPF1_H0: + case RT5665_L_EQ_BPF2_A1: + case RT5665_R_EQ_BPF2_A1: + case RT5665_L_EQ_BPF2_A2: + case RT5665_R_EQ_BPF2_A2: + case RT5665_L_EQ_BPF2_H0: + case RT5665_R_EQ_BPF2_H0: + case RT5665_L_EQ_BPF3_A1: + case RT5665_R_EQ_BPF3_A1: + case RT5665_L_EQ_BPF3_A2: + case RT5665_R_EQ_BPF3_A2: + case RT5665_L_EQ_BPF3_H0: + case RT5665_R_EQ_BPF3_H0: + case RT5665_L_EQ_BPF4_A1: + case RT5665_R_EQ_BPF4_A1: + case RT5665_L_EQ_BPF4_A2: + case RT5665_R_EQ_BPF4_A2: + case RT5665_L_EQ_BPF4_H0: + case RT5665_R_EQ_BPF4_H0: + case RT5665_L_EQ_HPF1_A1: + case RT5665_R_EQ_HPF1_A1: + case RT5665_L_EQ_HPF1_H0: + case RT5665_R_EQ_HPF1_H0: + case RT5665_L_EQ_PRE_VOL: + case RT5665_R_EQ_PRE_VOL: + case RT5665_L_EQ_POST_VOL: + case RT5665_R_EQ_POST_VOL: + case RT5665_SCAN_MODE_CTRL: + case RT5665_I2C_MODE: + return true; + default: + return false; + } +} + +static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -2250, 150, 0); +static const DECLARE_TLV_DB_SCALE(mono_vol_tlv, -1400, 150, 0); +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0); +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(in_bst_tlv, -1200, 75, 0); + +/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ +static const DECLARE_TLV_DB_RANGE(bst_tlv, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), + 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0), + 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0) +); + +/* Interface data select */ +static const char * const rt5665_data_select[] = { + "L/R", "R/L", "L/L", "R/R" +}; + +static const SOC_ENUM_SINGLE_DECL(rt5665_if1_1_01_adc_enum, + RT5665_TDM_CTRL_2, RT5665_I2S1_1_DS_ADC_SLOT01_SFT, rt5665_data_select); + +static const SOC_ENUM_SINGLE_DECL(rt5665_if1_1_23_adc_enum, + RT5665_TDM_CTRL_2, RT5665_I2S1_1_DS_ADC_SLOT23_SFT, rt5665_data_select); + +static const SOC_ENUM_SINGLE_DECL(rt5665_if1_1_45_adc_enum, + RT5665_TDM_CTRL_2, RT5665_I2S1_1_DS_ADC_SLOT45_SFT, rt5665_data_select); + +static const SOC_ENUM_SINGLE_DECL(rt5665_if1_1_67_adc_enum, + RT5665_TDM_CTRL_2, RT5665_I2S1_1_DS_ADC_SLOT67_SFT, rt5665_data_select); + +static const SOC_ENUM_SINGLE_DECL(rt5665_if1_2_01_adc_enum, + RT5665_TDM_CTRL_2, RT5665_I2S1_2_DS_ADC_SLOT01_SFT, rt5665_data_select); + +static const SOC_ENUM_SINGLE_DECL(rt5665_if1_2_23_adc_enum, + RT5665_TDM_CTRL_2, RT5665_I2S1_2_DS_ADC_SLOT23_SFT, rt5665_data_select); + +static const SOC_ENUM_SINGLE_DECL(rt5665_if1_2_45_adc_enum, + RT5665_TDM_CTRL_2, RT5665_I2S1_2_DS_ADC_SLOT45_SFT, rt5665_data_select); + +static const SOC_ENUM_SINGLE_DECL(rt5665_if1_2_67_adc_enum, + RT5665_TDM_CTRL_2, RT5665_I2S1_2_DS_ADC_SLOT67_SFT, rt5665_data_select); + +static const SOC_ENUM_SINGLE_DECL(rt5665_if2_1_dac_enum, + RT5665_DIG_INF2_DATA, RT5665_IF2_1_DAC_SEL_SFT, rt5665_data_select); + +static const SOC_ENUM_SINGLE_DECL(rt5665_if2_1_adc_enum, + RT5665_DIG_INF2_DATA, RT5665_IF2_1_ADC_SEL_SFT, rt5665_data_select); + +static const SOC_ENUM_SINGLE_DECL(rt5665_if2_2_dac_enum, + RT5665_DIG_INF2_DATA, RT5665_IF2_2_DAC_SEL_SFT, rt5665_data_select); + +static const SOC_ENUM_SINGLE_DECL(rt5665_if2_2_adc_enum, + RT5665_DIG_INF2_DATA, RT5665_IF2_2_ADC_SEL_SFT, rt5665_data_select); + +static const SOC_ENUM_SINGLE_DECL(rt5665_if3_dac_enum, + RT5665_DIG_INF3_DATA, RT5665_IF3_DAC_SEL_SFT, rt5665_data_select); + +static const SOC_ENUM_SINGLE_DECL(rt5665_if3_adc_enum, + RT5665_DIG_INF3_DATA, RT5665_IF3_ADC_SEL_SFT, rt5665_data_select); + +static const struct snd_kcontrol_new rt5665_if1_1_01_adc_swap_mux = + SOC_DAPM_ENUM("IF1_1 01 ADC Swap Mux", rt5665_if1_1_01_adc_enum); + +static const struct snd_kcontrol_new rt5665_if1_1_23_adc_swap_mux = + SOC_DAPM_ENUM("IF1_1 23 ADC Swap Mux", rt5665_if1_1_23_adc_enum); + +static const struct snd_kcontrol_new rt5665_if1_1_45_adc_swap_mux = + SOC_DAPM_ENUM("IF1_1 45 ADC Swap Mux", rt5665_if1_1_45_adc_enum); + +static const struct snd_kcontrol_new rt5665_if1_1_67_adc_swap_mux = + SOC_DAPM_ENUM("IF1_1 67 ADC Swap Mux", rt5665_if1_1_67_adc_enum); + +static const struct snd_kcontrol_new rt5665_if1_2_01_adc_swap_mux = + SOC_DAPM_ENUM("IF1_2 01 ADC Swap Mux", rt5665_if1_2_01_adc_enum); + +static const struct snd_kcontrol_new rt5665_if1_2_23_adc_swap_mux = + SOC_DAPM_ENUM("IF1_2 23 ADC1 Swap Mux", rt5665_if1_2_23_adc_enum); + +static const struct snd_kcontrol_new rt5665_if1_2_45_adc_swap_mux = + SOC_DAPM_ENUM("IF1_2 45 ADC1 Swap Mux", rt5665_if1_2_45_adc_enum); + +static const struct snd_kcontrol_new rt5665_if1_2_67_adc_swap_mux = + SOC_DAPM_ENUM("IF1_2 67 ADC1 Swap Mux", rt5665_if1_2_67_adc_enum); + +static const struct snd_kcontrol_new rt5665_if2_1_dac_swap_mux = + SOC_DAPM_ENUM("IF2_1 DAC Swap Source", rt5665_if2_1_dac_enum); + +static const struct snd_kcontrol_new rt5665_if2_1_adc_swap_mux = + SOC_DAPM_ENUM("IF2_1 ADC Swap Source", rt5665_if2_1_adc_enum); + +static const struct snd_kcontrol_new rt5665_if2_2_dac_swap_mux = + SOC_DAPM_ENUM("IF2_2 DAC Swap Source", rt5665_if2_2_dac_enum); + +static const struct snd_kcontrol_new rt5665_if2_2_adc_swap_mux = + SOC_DAPM_ENUM("IF2_2 ADC Swap Source", rt5665_if2_2_adc_enum); + +static const struct snd_kcontrol_new rt5665_if3_dac_swap_mux = + SOC_DAPM_ENUM("IF3 DAC Swap Source", rt5665_if3_dac_enum); + +static const struct snd_kcontrol_new rt5665_if3_adc_swap_mux = + SOC_DAPM_ENUM("IF3 ADC Swap Source", rt5665_if3_adc_enum); + +static int rt5665_hp_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int ret = snd_soc_put_volsw(kcontrol, ucontrol); + + if (snd_soc_read(codec, RT5665_STO_NG2_CTRL_1) & RT5665_NG2_EN) { + snd_soc_update_bits(codec, RT5665_STO_NG2_CTRL_1, + RT5665_NG2_EN_MASK, RT5665_NG2_DIS); + snd_soc_update_bits(codec, RT5665_STO_NG2_CTRL_1, + RT5665_NG2_EN_MASK, RT5665_NG2_EN); + } + + return ret; +} + +static int rt5665_mono_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int ret = snd_soc_put_volsw(kcontrol, ucontrol); + + if (snd_soc_read(codec, RT5665_MONO_NG2_CTRL_1) & RT5665_NG2_EN) { + snd_soc_update_bits(codec, RT5665_MONO_NG2_CTRL_1, + RT5665_NG2_EN_MASK, RT5665_NG2_DIS); + snd_soc_update_bits(codec, RT5665_MONO_NG2_CTRL_1, + RT5665_NG2_EN_MASK, RT5665_NG2_EN); + } + + return ret; +} + +/** + * rt5665_sel_asrc_clk_src - select ASRC clock source for a set of filters + * @codec: SoC audio codec device. + * @filter_mask: mask of filters. + * @clk_src: clock source + * + * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5665 can + * only support standard 32fs or 64fs i2s format, ASRC should be enabled to + * support special i2s clock format such as Intel's 100fs(100 * sampling rate). + * ASRC function will track i2s clock and generate a corresponding system clock + * for codec. This function provides an API to select the clock source for a + * set of filters specified by the mask. And the codec driver will turn on ASRC + * for these filters if ASRC is selected as their clock source. + */ +int rt5665_sel_asrc_clk_src(struct snd_soc_codec *codec, + unsigned int filter_mask, unsigned int clk_src) +{ + unsigned int asrc2_mask = 0; + unsigned int asrc2_value = 0; + unsigned int asrc3_mask = 0; + unsigned int asrc3_value = 0; + + switch (clk_src) { + case RT5665_CLK_SEL_SYS: + case RT5665_CLK_SEL_I2S1_ASRC: + case RT5665_CLK_SEL_I2S2_ASRC: + case RT5665_CLK_SEL_I2S3_ASRC: + case RT5665_CLK_SEL_SYS2: + case RT5665_CLK_SEL_SYS3: + case RT5665_CLK_SEL_SYS4: + break; + + default: + return -EINVAL; + } + + if (filter_mask & RT5665_DA_STEREO1_FILTER) { + asrc2_mask |= RT5665_DA_STO1_CLK_SEL_MASK; + asrc2_value = (asrc2_value & ~RT5665_DA_STO1_CLK_SEL_MASK) + | (clk_src << RT5665_DA_STO1_CLK_SEL_SFT); + } + + if (filter_mask & RT5665_DA_STEREO2_FILTER) { + asrc2_mask |= RT5665_DA_STO2_CLK_SEL_MASK; + asrc2_value = (asrc2_value & ~RT5665_DA_STO2_CLK_SEL_MASK) + | (clk_src << RT5665_DA_STO2_CLK_SEL_SFT); + } + + if (filter_mask & RT5665_DA_MONO_L_FILTER) { + asrc2_mask |= RT5665_DA_MONOL_CLK_SEL_MASK; + asrc2_value = (asrc2_value & ~RT5665_DA_MONOL_CLK_SEL_MASK) + | (clk_src << RT5665_DA_MONOL_CLK_SEL_SFT); + } + + if (filter_mask & RT5665_DA_MONO_R_FILTER) { + asrc2_mask |= RT5665_DA_MONOR_CLK_SEL_MASK; + asrc2_value = (asrc2_value & ~RT5665_DA_MONOR_CLK_SEL_MASK) + | (clk_src << RT5665_DA_MONOR_CLK_SEL_SFT); + } + + if (filter_mask & RT5665_AD_STEREO1_FILTER) { + asrc3_mask |= RT5665_AD_STO1_CLK_SEL_MASK; + asrc3_value = (asrc2_value & ~RT5665_AD_STO1_CLK_SEL_MASK) + | (clk_src << RT5665_AD_STO1_CLK_SEL_SFT); + } + + if (filter_mask & RT5665_AD_STEREO2_FILTER) { + asrc3_mask |= RT5665_AD_STO2_CLK_SEL_MASK; + asrc3_value = (asrc2_value & ~RT5665_AD_STO2_CLK_SEL_MASK) + | (clk_src << RT5665_AD_STO2_CLK_SEL_SFT); + } + + if (filter_mask & RT5665_AD_MONO_L_FILTER) { + asrc3_mask |= RT5665_AD_MONOL_CLK_SEL_MASK; + asrc3_value = (asrc3_value & ~RT5665_AD_MONOL_CLK_SEL_MASK) + | (clk_src << RT5665_AD_MONOL_CLK_SEL_SFT); + } + + if (filter_mask & RT5665_AD_MONO_R_FILTER) { + asrc3_mask |= RT5665_AD_MONOR_CLK_SEL_MASK; + asrc3_value = (asrc3_value & ~RT5665_AD_MONOR_CLK_SEL_MASK) + | (clk_src << RT5665_AD_MONOR_CLK_SEL_SFT); + } + + if (asrc2_mask) + snd_soc_update_bits(codec, RT5665_ASRC_2, + asrc2_mask, asrc2_value); + + if (asrc3_mask) + snd_soc_update_bits(codec, RT5665_ASRC_3, + asrc3_mask, asrc3_value); + + return 0; +} +EXPORT_SYMBOL_GPL(rt5665_sel_asrc_clk_src); + +static int rt5665_button_detect(struct snd_soc_codec *codec) +{ + int btn_type, val; + + val = snd_soc_read(codec, RT5665_4BTN_IL_CMD_1); + btn_type = val & 0xfff0; + snd_soc_write(codec, RT5665_4BTN_IL_CMD_1, val); + + return btn_type; +} + +static void rt5665_enable_push_button_irq(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) { + snd_soc_write(codec, RT5665_4BTN_IL_CMD_1, 0x000b); + snd_soc_write(codec, RT5665_IL_CMD_1, 0x0048); + snd_soc_update_bits(codec, RT5665_4BTN_IL_CMD_2, + RT5665_4BTN_IL_MASK | RT5665_4BTN_IL_RST_MASK, + RT5665_4BTN_IL_EN | RT5665_4BTN_IL_NOR); + snd_soc_update_bits(codec, RT5665_IRQ_CTRL_3, + RT5665_IL_IRQ_MASK, RT5665_IL_IRQ_EN); + } else { + snd_soc_update_bits(codec, RT5665_IRQ_CTRL_3, + RT5665_IL_IRQ_MASK, RT5665_IL_IRQ_DIS); + snd_soc_update_bits(codec, RT5665_4BTN_IL_CMD_2, + RT5665_4BTN_IL_MASK, RT5665_4BTN_IL_DIS); + snd_soc_update_bits(codec, RT5665_4BTN_IL_CMD_2, + RT5665_4BTN_IL_RST_MASK, RT5665_4BTN_IL_RST); + } +} + +/** + * rt5665_headset_detect - Detect headset. + * @codec: SoC audio codec device. + * @jack_insert: Jack insert or not. + * + * Detect whether is headset or not when jack inserted. + * + * Returns detect status. + */ +static int rt5665_headset_detect(struct snd_soc_codec *codec, int jack_insert) +{ + struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + unsigned int sar_hs_type, val; + + if (jack_insert) { + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1"); + snd_soc_dapm_sync(dapm); + + regmap_update_bits(rt5665->regmap, RT5665_MICBIAS_2, 0x100, + 0x100); + + regmap_read(rt5665->regmap, RT5665_GPIO_STA, &val); + if (val & 0x4) { + regmap_update_bits(rt5665->regmap, RT5665_EJD_CTRL_1, + 0x100, 0); + + regmap_read(rt5665->regmap, RT5665_GPIO_STA, &val); + while (val & 0x4) { + usleep_range(10000, 15000); + regmap_read(rt5665->regmap, RT5665_GPIO_STA, + &val); + } + } + + regmap_update_bits(rt5665->regmap, RT5665_EJD_CTRL_1, + 0x180, 0x180); + regmap_write(rt5665->regmap, RT5665_EJD_CTRL_3, 0x3424); + regmap_write(rt5665->regmap, RT5665_SAR_IL_CMD_1, 0xa291); + + rt5665->sar_adc_value = snd_soc_read(rt5665->codec, + RT5665_SAR_IL_CMD_4) & 0x7ff; + + sar_hs_type = rt5665->pdata.sar_hs_type ? + rt5665->pdata.sar_hs_type : 729; + + if (rt5665->sar_adc_value > sar_hs_type) { + rt5665->jack_type = SND_JACK_HEADSET; + rt5665_enable_push_button_irq(codec, true); + } else { + rt5665->jack_type = SND_JACK_HEADPHONE; + regmap_write(rt5665->regmap, RT5665_SAR_IL_CMD_1, + 0x2291); + regmap_update_bits(rt5665->regmap, RT5665_MICBIAS_2, + 0x100, 0); + snd_soc_dapm_disable_pin(dapm, "MICBIAS1"); + snd_soc_dapm_sync(dapm); + } + } else { + regmap_write(rt5665->regmap, RT5665_SAR_IL_CMD_1, 0x2291); + regmap_update_bits(rt5665->regmap, RT5665_MICBIAS_2, 0x100, 0); + snd_soc_dapm_disable_pin(dapm, "MICBIAS1"); + snd_soc_dapm_sync(dapm); + if (rt5665->jack_type == SND_JACK_HEADSET) + rt5665_enable_push_button_irq(codec, false); + rt5665->jack_type = 0; + } + + dev_dbg(codec->dev, "jack_type = %d\n", rt5665->jack_type); + return rt5665->jack_type; +} + +static irqreturn_t rt5665_irq(int irq, void *data) +{ + struct rt5665_priv *rt5665 = data; + + mod_delayed_work(system_power_efficient_wq, + &rt5665->jack_detect_work, msecs_to_jiffies(250)); + + return IRQ_HANDLED; +} + +static void rt5665_jd_check_handler(struct work_struct *work) +{ + struct rt5665_priv *rt5665 = container_of(work, struct rt5665_priv, + calibrate_work.work); + + if (snd_soc_read(rt5665->codec, RT5665_AJD1_CTRL) & 0x0010) { + /* jack out */ + rt5665->jack_type = rt5665_headset_detect(rt5665->codec, 0); + + snd_soc_jack_report(rt5665->hs_jack, rt5665->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + } else { + schedule_delayed_work(&rt5665->jd_check_work, 500); + } +} + +int rt5665_set_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *hs_jack) +{ + struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec); + + switch (rt5665->pdata.jd_src) { + case RT5665_JD1: + regmap_update_bits(rt5665->regmap, RT5665_GPIO_CTRL_1, + RT5665_GP1_PIN_MASK, RT5665_GP1_PIN_IRQ); + regmap_update_bits(rt5665->regmap, RT5665_RC_CLK_CTRL, + 0xc000, 0xc000); + regmap_update_bits(rt5665->regmap, RT5665_PWR_ANLG_2, + RT5665_PWR_JD1, RT5665_PWR_JD1); + regmap_update_bits(rt5665->regmap, RT5665_IRQ_CTRL_1, 0x8, 0x8); + break; + + case RT5665_JD_NULL: + break; + + default: + dev_warn(codec->dev, "Wrong JD source\n"); + break; + } + + rt5665->hs_jack = hs_jack; + + return 0; +} +EXPORT_SYMBOL_GPL(rt5665_set_jack_detect); + +static void rt5665_jack_detect_handler(struct work_struct *work) +{ + struct rt5665_priv *rt5665 = + container_of(work, struct rt5665_priv, jack_detect_work.work); + int val, btn_type; + + while (!rt5665->codec) { + pr_debug("%s codec = null\n", __func__); + usleep_range(10000, 15000); + } + + while (!rt5665->codec->component.card->instantiated) { + pr_debug("%s\n", __func__); + usleep_range(10000, 15000); + } + + mutex_lock(&rt5665->calibrate_mutex); + + val = snd_soc_read(rt5665->codec, RT5665_AJD1_CTRL) & 0x0010; + if (!val) { + /* jack in */ + if (rt5665->jack_type == 0) { + /* jack was out, report jack type */ + rt5665->jack_type = + rt5665_headset_detect(rt5665->codec, 1); + } else { + /* jack is already in, report button event */ + rt5665->jack_type = SND_JACK_HEADSET; + btn_type = rt5665_button_detect(rt5665->codec); + /** + * rt5665 can report three kinds of button behavior, + * one click, double click and hold. However, + * currently we will report button pressed/released + * event. So all the three button behaviors are + * treated as button pressed. + */ + switch (btn_type) { + case 0x8000: + case 0x4000: + case 0x2000: + rt5665->jack_type |= SND_JACK_BTN_0; + break; + case 0x1000: + case 0x0800: + case 0x0400: + rt5665->jack_type |= SND_JACK_BTN_1; + break; + case 0x0200: + case 0x0100: + case 0x0080: + rt5665->jack_type |= SND_JACK_BTN_2; + break; + case 0x0040: + case 0x0020: + case 0x0010: + rt5665->jack_type |= SND_JACK_BTN_3; + break; + case 0x0000: /* unpressed */ + break; + default: + btn_type = 0; + dev_err(rt5665->codec->dev, + "Unexpected button code 0x%04x\n", + btn_type); + break; + } + } + } else { + /* jack out */ + rt5665->jack_type = rt5665_headset_detect(rt5665->codec, 0); + } + + snd_soc_jack_report(rt5665->hs_jack, rt5665->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (rt5665->jack_type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3)) + schedule_delayed_work(&rt5665->jd_check_work, 0); + else + cancel_delayed_work_sync(&rt5665->jd_check_work); + + mutex_unlock(&rt5665->calibrate_mutex); +} + +static const struct snd_kcontrol_new rt5665_snd_controls[] = { + /* Headphone Output Volume */ + SOC_DOUBLE_R_EXT_TLV("Headphone Playback Volume", RT5665_HPL_GAIN, + RT5665_HPR_GAIN, RT5665_G_HP_SFT, 15, 1, snd_soc_get_volsw, + rt5665_hp_vol_put, hp_vol_tlv), + + /* Mono Output Volume */ + SOC_SINGLE_EXT_TLV("Mono Playback Volume", RT5665_MONO_GAIN, + RT5665_L_VOL_SFT, 15, 1, snd_soc_get_volsw, + rt5665_mono_vol_put, mono_vol_tlv), + + /* Output Volume */ + SOC_DOUBLE_TLV("OUT Playback Volume", RT5665_LOUT, RT5665_L_VOL_SFT, + RT5665_R_VOL_SFT, 39, 1, out_vol_tlv), + + /* DAC Digital Volume */ + SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5665_DAC1_DIG_VOL, + RT5665_L_VOL_SFT, RT5665_R_VOL_SFT, 175, 0, dac_vol_tlv), + SOC_DOUBLE_TLV("DAC2 Playback Volume", RT5665_DAC2_DIG_VOL, + RT5665_L_VOL_SFT, RT5665_R_VOL_SFT, 175, 0, dac_vol_tlv), + SOC_DOUBLE("DAC2 Playback Switch", RT5665_DAC2_CTRL, + RT5665_M_DAC2_L_VOL_SFT, RT5665_M_DAC2_R_VOL_SFT, 1, 1), + + /* IN1/IN2/IN3/IN4 Volume */ + SOC_SINGLE_TLV("IN1 Boost Volume", RT5665_IN1_IN2, + RT5665_BST1_SFT, 69, 0, in_bst_tlv), + SOC_SINGLE_TLV("IN2 Boost Volume", RT5665_IN1_IN2, + RT5665_BST2_SFT, 69, 0, in_bst_tlv), + SOC_SINGLE_TLV("IN3 Boost Volume", RT5665_IN3_IN4, + RT5665_BST3_SFT, 69, 0, in_bst_tlv), + SOC_SINGLE_TLV("IN4 Boost Volume", RT5665_IN3_IN4, + RT5665_BST4_SFT, 69, 0, in_bst_tlv), + SOC_SINGLE_TLV("CBJ Boost Volume", RT5665_CBJ_BST_CTRL, + RT5665_BST_CBJ_SFT, 8, 0, bst_tlv), + + /* INL/INR Volume Control */ + SOC_DOUBLE_TLV("IN Capture Volume", RT5665_INL1_INR1_VOL, + RT5665_INL_VOL_SFT, RT5665_INR_VOL_SFT, 31, 1, in_vol_tlv), + + /* ADC Digital Volume Control */ + SOC_DOUBLE("STO1 ADC Capture Switch", RT5665_STO1_ADC_DIG_VOL, + RT5665_L_MUTE_SFT, RT5665_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("STO1 ADC Capture Volume", RT5665_STO1_ADC_DIG_VOL, + RT5665_L_VOL_SFT, RT5665_R_VOL_SFT, 127, 0, adc_vol_tlv), + SOC_DOUBLE("Mono ADC Capture Switch", RT5665_MONO_ADC_DIG_VOL, + RT5665_L_MUTE_SFT, RT5665_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5665_MONO_ADC_DIG_VOL, + RT5665_L_VOL_SFT, RT5665_R_VOL_SFT, 127, 0, adc_vol_tlv), + SOC_DOUBLE("STO2 ADC Capture Switch", RT5665_STO2_ADC_DIG_VOL, + RT5665_L_MUTE_SFT, RT5665_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("STO2 ADC Capture Volume", RT5665_STO2_ADC_DIG_VOL, + RT5665_L_VOL_SFT, RT5665_R_VOL_SFT, 127, 0, adc_vol_tlv), + + /* ADC Boost Volume Control */ + SOC_DOUBLE_TLV("STO1 ADC Boost Gain Volume", RT5665_STO1_ADC_BOOST, + RT5665_STO1_ADC_L_BST_SFT, RT5665_STO1_ADC_R_BST_SFT, + 3, 0, adc_bst_tlv), + + SOC_DOUBLE_TLV("Mono ADC Boost Gain Volume", RT5665_MONO_ADC_BOOST, + RT5665_MONO_ADC_L_BST_SFT, RT5665_MONO_ADC_R_BST_SFT, + 3, 0, adc_bst_tlv), + + SOC_DOUBLE_TLV("STO2 ADC Boost Gain Volume", RT5665_STO2_ADC_BOOST, + RT5665_STO2_ADC_L_BST_SFT, RT5665_STO2_ADC_R_BST_SFT, + 3, 0, adc_bst_tlv), +}; + +/** + * set_dmic_clk - Set parameter of dmic. + * + * @w: DAPM widget. + * @kcontrol: The kcontrol of this widget. + * @event: Event id. + * + * Choose dmic clock between 1MHz and 3MHz. + * It is better for clock to approximate 3MHz. + */ +static int set_dmic_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec); + int pd, idx = -EINVAL; + + pd = rl6231_get_pre_div(rt5665->regmap, + RT5665_ADDA_CLK_1, RT5665_I2S_PD1_SFT); + idx = rl6231_calc_dmic_clk(rt5665->sysclk / pd); + + if (idx < 0) + dev_err(codec->dev, "Failed to set DMIC clock\n"); + else { + snd_soc_update_bits(codec, RT5665_DMIC_CTRL_1, + RT5665_DMIC_CLK_MASK, idx << RT5665_DMIC_CLK_SFT); + } + return idx; +} + +static int rt5665_charge_pump_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, RT5665_HP_CHARGE_PUMP_1, + RT5665_PM_HP_MASK | RT5665_OSW_L_MASK, + RT5665_PM_HP_HV | RT5665_OSW_L_EN); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, RT5665_HP_CHARGE_PUMP_1, + RT5665_PM_HP_MASK | RT5665_OSW_L_MASK, + RT5665_PM_HP_LV | RT5665_OSW_L_DIS); + break; + default: + return 0; + } + + return 0; +} + +static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_widget *sink) +{ + unsigned int val; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + val = snd_soc_read(codec, RT5665_GLB_CLK); + val &= RT5665_SCLK_SRC_MASK; + if (val == RT5665_SCLK_SRC_PLL1) + return 1; + else + return 0; +} + +static int is_using_asrc(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_widget *sink) +{ + unsigned int reg, shift, val; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (w->shift) { + case RT5665_ADC_MONO_R_ASRC_SFT: + reg = RT5665_ASRC_3; + shift = RT5665_AD_MONOR_CLK_SEL_SFT; + break; + case RT5665_ADC_MONO_L_ASRC_SFT: + reg = RT5665_ASRC_3; + shift = RT5665_AD_MONOL_CLK_SEL_SFT; + break; + case RT5665_ADC_STO1_ASRC_SFT: + reg = RT5665_ASRC_3; + shift = RT5665_AD_STO1_CLK_SEL_SFT; + break; + case RT5665_ADC_STO2_ASRC_SFT: + reg = RT5665_ASRC_3; + shift = RT5665_AD_STO2_CLK_SEL_SFT; + break; + case RT5665_DAC_MONO_R_ASRC_SFT: + reg = RT5665_ASRC_2; + shift = RT5665_DA_MONOR_CLK_SEL_SFT; + break; + case RT5665_DAC_MONO_L_ASRC_SFT: + reg = RT5665_ASRC_2; + shift = RT5665_DA_MONOL_CLK_SEL_SFT; + break; + case RT5665_DAC_STO1_ASRC_SFT: + reg = RT5665_ASRC_2; + shift = RT5665_DA_STO1_CLK_SEL_SFT; + break; + case RT5665_DAC_STO2_ASRC_SFT: + reg = RT5665_ASRC_2; + shift = RT5665_DA_STO2_CLK_SEL_SFT; + break; + default: + return 0; + } + + val = (snd_soc_read(codec, reg) >> shift) & 0xf; + switch (val) { + case RT5665_CLK_SEL_I2S1_ASRC: + case RT5665_CLK_SEL_I2S2_ASRC: + case RT5665_CLK_SEL_I2S3_ASRC: + /* I2S_Pre_Div1 should be 1 in asrc mode */ + snd_soc_update_bits(codec, RT5665_ADDA_CLK_1, + RT5665_I2S_PD1_MASK, RT5665_I2S_PD1_2); + return 1; + default: + return 0; + } + +} + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt5665_sto1_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5665_STO1_ADC_MIXER, + RT5665_M_STO1_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5665_STO1_ADC_MIXER, + RT5665_M_STO1_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_sto1_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5665_STO1_ADC_MIXER, + RT5665_M_STO1_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5665_STO1_ADC_MIXER, + RT5665_M_STO1_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_sto2_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5665_STO2_ADC_MIXER, + RT5665_M_STO2_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5665_STO2_ADC_MIXER, + RT5665_M_STO2_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_sto2_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5665_STO2_ADC_MIXER, + RT5665_M_STO2_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5665_STO2_ADC_MIXER, + RT5665_M_STO2_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_mono_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5665_MONO_ADC_MIXER, + RT5665_M_MONO_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5665_MONO_ADC_MIXER, + RT5665_M_MONO_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_mono_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5665_MONO_ADC_MIXER, + RT5665_M_MONO_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5665_MONO_ADC_MIXER, + RT5665_M_MONO_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_dac_l_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5665_AD_DA_MIXER, + RT5665_M_ADCMIX_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5665_AD_DA_MIXER, + RT5665_M_DAC1_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_dac_r_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5665_AD_DA_MIXER, + RT5665_M_ADCMIX_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5665_AD_DA_MIXER, + RT5665_M_DAC1_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_sto1_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5665_STO1_DAC_MIXER, + RT5665_M_DAC_L1_STO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5665_STO1_DAC_MIXER, + RT5665_M_DAC_R1_STO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5665_STO1_DAC_MIXER, + RT5665_M_DAC_L2_STO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5665_STO1_DAC_MIXER, + RT5665_M_DAC_R2_STO_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_sto1_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5665_STO1_DAC_MIXER, + RT5665_M_DAC_L1_STO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5665_STO1_DAC_MIXER, + RT5665_M_DAC_R1_STO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5665_STO1_DAC_MIXER, + RT5665_M_DAC_L2_STO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5665_STO1_DAC_MIXER, + RT5665_M_DAC_R2_STO_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_sto2_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5665_STO2_DAC_MIXER, + RT5665_M_DAC_L1_STO2_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5665_STO2_DAC_MIXER, + RT5665_M_DAC_L2_STO2_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L3 Switch", RT5665_STO2_DAC_MIXER, + RT5665_M_DAC_L3_STO2_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_sto2_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5665_STO2_DAC_MIXER, + RT5665_M_DAC_R1_STO2_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5665_STO2_DAC_MIXER, + RT5665_M_DAC_R2_STO2_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R3 Switch", RT5665_STO2_DAC_MIXER, + RT5665_M_DAC_R3_STO2_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_mono_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5665_MONO_DAC_MIXER, + RT5665_M_DAC_L1_MONO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5665_MONO_DAC_MIXER, + RT5665_M_DAC_R1_MONO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5665_MONO_DAC_MIXER, + RT5665_M_DAC_L2_MONO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5665_MONO_DAC_MIXER, + RT5665_M_DAC_R2_MONO_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_mono_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5665_MONO_DAC_MIXER, + RT5665_M_DAC_L1_MONO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5665_MONO_DAC_MIXER, + RT5665_M_DAC_R1_MONO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5665_MONO_DAC_MIXER, + RT5665_M_DAC_L2_MONO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5665_MONO_DAC_MIXER, + RT5665_M_DAC_R2_MONO_R_SFT, 1, 1), +}; + +/* Analog Input Mixer */ +static const struct snd_kcontrol_new rt5665_rec1_l_mix[] = { + SOC_DAPM_SINGLE("CBJ Switch", RT5665_REC1_L2_MIXER, + RT5665_M_CBJ_RM1_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5665_REC1_L2_MIXER, + RT5665_M_INL_RM1_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5665_REC1_L2_MIXER, + RT5665_M_INR_RM1_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST4 Switch", RT5665_REC1_L2_MIXER, + RT5665_M_BST4_RM1_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5665_REC1_L2_MIXER, + RT5665_M_BST3_RM1_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5665_REC1_L2_MIXER, + RT5665_M_BST2_RM1_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5665_REC1_L2_MIXER, + RT5665_M_BST1_RM1_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_rec1_r_mix[] = { + SOC_DAPM_SINGLE("MONOVOL Switch", RT5665_REC1_R2_MIXER, + RT5665_M_AEC_REF_RM1_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5665_REC1_R2_MIXER, + RT5665_M_INR_RM1_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST4 Switch", RT5665_REC1_R2_MIXER, + RT5665_M_BST4_RM1_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5665_REC1_R2_MIXER, + RT5665_M_BST3_RM1_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5665_REC1_R2_MIXER, + RT5665_M_BST2_RM1_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5665_REC1_R2_MIXER, + RT5665_M_BST1_RM1_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_rec2_l_mix[] = { + SOC_DAPM_SINGLE("INL Switch", RT5665_REC2_L2_MIXER, + RT5665_M_INL_RM2_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5665_REC2_L2_MIXER, + RT5665_M_INR_RM2_L_SFT, 1, 1), + SOC_DAPM_SINGLE("CBJ Switch", RT5665_REC2_L2_MIXER, + RT5665_M_CBJ_RM2_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST4 Switch", RT5665_REC2_L2_MIXER, + RT5665_M_BST4_RM2_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5665_REC2_L2_MIXER, + RT5665_M_BST3_RM2_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5665_REC2_L2_MIXER, + RT5665_M_BST2_RM2_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5665_REC2_L2_MIXER, + RT5665_M_BST1_RM2_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_rec2_r_mix[] = { + SOC_DAPM_SINGLE("MONOVOL Switch", RT5665_REC2_R2_MIXER, + RT5665_M_MONOVOL_RM2_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5665_REC2_R2_MIXER, + RT5665_M_INL_RM2_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5665_REC2_R2_MIXER, + RT5665_M_INR_RM2_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST4 Switch", RT5665_REC2_R2_MIXER, + RT5665_M_BST4_RM2_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5665_REC2_R2_MIXER, + RT5665_M_BST3_RM2_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5665_REC2_R2_MIXER, + RT5665_M_BST2_RM2_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5665_REC2_R2_MIXER, + RT5665_M_BST1_RM2_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_monovol_mix[] = { + SOC_DAPM_SINGLE("DAC L2 Switch", RT5665_MONOMIX_IN_GAIN, + RT5665_M_DAC_L2_MM_SFT, 1, 1), + SOC_DAPM_SINGLE("RECMIX2L Switch", RT5665_MONOMIX_IN_GAIN, + RT5665_M_RECMIC2L_MM_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5665_MONOMIX_IN_GAIN, + RT5665_M_BST1_MM_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5665_MONOMIX_IN_GAIN, + RT5665_M_BST2_MM_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5665_MONOMIX_IN_GAIN, + RT5665_M_BST3_MM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_out_l_mix[] = { + SOC_DAPM_SINGLE("DAC L2 Switch", RT5665_OUT_L_MIXER, + RT5665_M_DAC_L2_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5665_OUT_L_MIXER, + RT5665_M_IN_L_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5665_OUT_L_MIXER, + RT5665_M_BST1_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5665_OUT_L_MIXER, + RT5665_M_BST2_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5665_OUT_L_MIXER, + RT5665_M_BST3_OM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_out_r_mix[] = { + SOC_DAPM_SINGLE("DAC R2 Switch", RT5665_OUT_R_MIXER, + RT5665_M_DAC_R2_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5665_OUT_R_MIXER, + RT5665_M_IN_R_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5665_OUT_R_MIXER, + RT5665_M_BST2_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5665_OUT_R_MIXER, + RT5665_M_BST3_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST4 Switch", RT5665_OUT_R_MIXER, + RT5665_M_BST4_OM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_mono_mix[] = { + SOC_DAPM_SINGLE("DAC L2 Switch", RT5665_MONOMIX_IN_GAIN, + RT5665_M_DAC_L2_MA_SFT, 1, 1), + SOC_DAPM_SINGLE("MONOVOL Switch", RT5665_MONOMIX_IN_GAIN, + RT5665_M_MONOVOL_MA_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_lout_l_mix[] = { + SOC_DAPM_SINGLE("DAC L2 Switch", RT5665_LOUT_MIXER, + RT5665_M_DAC_L2_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTVOL L Switch", RT5665_LOUT_MIXER, + RT5665_M_OV_L_LM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_lout_r_mix[] = { + SOC_DAPM_SINGLE("DAC R2 Switch", RT5665_LOUT_MIXER, + RT5665_M_DAC_R2_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTVOL R Switch", RT5665_LOUT_MIXER, + RT5665_M_OV_R_LM_SFT, 1, 1), +}; + +/*DAC L2, DAC R2*/ +/*MX-17 [6:4], MX-17 [2:0]*/ +static const char * const rt5665_dac2_src[] = { + "IF1 DAC2", "IF2_1 DAC", "IF2_2 DAC", "IF3 DAC", "Mono ADC MIX" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_dac_l2_enum, RT5665_DAC2_CTRL, + RT5665_DAC_L2_SEL_SFT, rt5665_dac2_src); + +static const struct snd_kcontrol_new rt5665_dac_l2_mux = + SOC_DAPM_ENUM("Digital DAC L2 Source", rt5665_dac_l2_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5665_dac_r2_enum, RT5665_DAC2_CTRL, + RT5665_DAC_R2_SEL_SFT, rt5665_dac2_src); + +static const struct snd_kcontrol_new rt5665_dac_r2_mux = + SOC_DAPM_ENUM("Digital DAC R2 Source", rt5665_dac_r2_enum); + +/*DAC L3, DAC R3*/ +/*MX-1B [6:4], MX-1B [2:0]*/ +static const char * const rt5665_dac3_src[] = { + "IF1 DAC2", "IF2_1 DAC", "IF2_2 DAC", "IF3 DAC", "STO2 ADC MIX" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_dac_l3_enum, RT5665_DAC3_CTRL, + RT5665_DAC_L3_SEL_SFT, rt5665_dac3_src); + +static const struct snd_kcontrol_new rt5665_dac_l3_mux = + SOC_DAPM_ENUM("Digital DAC L3 Source", rt5665_dac_l3_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5665_dac_r3_enum, RT5665_DAC3_CTRL, + RT5665_DAC_R3_SEL_SFT, rt5665_dac3_src); + +static const struct snd_kcontrol_new rt5665_dac_r3_mux = + SOC_DAPM_ENUM("Digital DAC R3 Source", rt5665_dac_r3_enum); + +/* STO1 ADC1 Source */ +/* MX-26 [13] [5] */ +static const char * const rt5665_sto1_adc1_src[] = { + "DD Mux", "ADC" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_sto1_adc1l_enum, RT5665_STO1_ADC_MIXER, + RT5665_STO1_ADC1L_SRC_SFT, rt5665_sto1_adc1_src); + +static const struct snd_kcontrol_new rt5665_sto1_adc1l_mux = + SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5665_sto1_adc1l_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5665_sto1_adc1r_enum, RT5665_STO1_ADC_MIXER, + RT5665_STO1_ADC1R_SRC_SFT, rt5665_sto1_adc1_src); + +static const struct snd_kcontrol_new rt5665_sto1_adc1r_mux = + SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5665_sto1_adc1r_enum); + +/* STO1 ADC Source */ +/* MX-26 [11:10] [3:2] */ +static const char * const rt5665_sto1_adc_src[] = { + "ADC1 L", "ADC1 R", "ADC2 L", "ADC2 R" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_sto1_adcl_enum, RT5665_STO1_ADC_MIXER, + RT5665_STO1_ADCL_SRC_SFT, rt5665_sto1_adc_src); + +static const struct snd_kcontrol_new rt5665_sto1_adcl_mux = + SOC_DAPM_ENUM("Stereo1 ADCL Source", rt5665_sto1_adcl_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5665_sto1_adcr_enum, RT5665_STO1_ADC_MIXER, + RT5665_STO1_ADCR_SRC_SFT, rt5665_sto1_adc_src); + +static const struct snd_kcontrol_new rt5665_sto1_adcr_mux = + SOC_DAPM_ENUM("Stereo1 ADCR Source", rt5665_sto1_adcr_enum); + +/* STO1 ADC2 Source */ +/* MX-26 [12] [4] */ +static const char * const rt5665_sto1_adc2_src[] = { + "DAC MIX", "DMIC" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_sto1_adc2l_enum, RT5665_STO1_ADC_MIXER, + RT5665_STO1_ADC2L_SRC_SFT, rt5665_sto1_adc2_src); + +static const struct snd_kcontrol_new rt5665_sto1_adc2l_mux = + SOC_DAPM_ENUM("Stereo1 ADC2L Source", rt5665_sto1_adc2l_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5665_sto1_adc2r_enum, RT5665_STO1_ADC_MIXER, + RT5665_STO1_ADC2R_SRC_SFT, rt5665_sto1_adc2_src); + +static const struct snd_kcontrol_new rt5665_sto1_adc2r_mux = + SOC_DAPM_ENUM("Stereo1 ADC2R Source", rt5665_sto1_adc2r_enum); + +/* STO1 DMIC Source */ +/* MX-26 [8] */ +static const char * const rt5665_sto1_dmic_src[] = { + "DMIC1", "DMIC2" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_sto1_dmic_enum, RT5665_STO1_ADC_MIXER, + RT5665_STO1_DMIC_SRC_SFT, rt5665_sto1_dmic_src); + +static const struct snd_kcontrol_new rt5665_sto1_dmic_mux = + SOC_DAPM_ENUM("Stereo1 DMIC Mux", rt5665_sto1_dmic_enum); + +/* MX-26 [9] */ +static const char * const rt5665_sto1_dd_l_src[] = { + "STO2 DAC", "MONO DAC" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_sto1_dd_l_enum, RT5665_STO1_ADC_MIXER, + RT5665_STO1_DD_L_SRC_SFT, rt5665_sto1_dd_l_src); + +static const struct snd_kcontrol_new rt5665_sto1_dd_l_mux = + SOC_DAPM_ENUM("Stereo1 DD L Source", rt5665_sto1_dd_l_enum); + +/* MX-26 [1:0] */ +static const char * const rt5665_sto1_dd_r_src[] = { + "STO2 DAC", "MONO DAC", "AEC REF" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_sto1_dd_r_enum, RT5665_STO1_ADC_MIXER, + RT5665_STO1_DD_R_SRC_SFT, rt5665_sto1_dd_r_src); + +static const struct snd_kcontrol_new rt5665_sto1_dd_r_mux = + SOC_DAPM_ENUM("Stereo1 DD R Source", rt5665_sto1_dd_r_enum); + +/* MONO ADC L2 Source */ +/* MX-27 [12] */ +static const char * const rt5665_mono_adc_l2_src[] = { + "DAC MIXL", "DMIC" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_mono_adc_l2_enum, RT5665_MONO_ADC_MIXER, + RT5665_MONO_ADC_L2_SRC_SFT, rt5665_mono_adc_l2_src); + +static const struct snd_kcontrol_new rt5665_mono_adc_l2_mux = + SOC_DAPM_ENUM("Mono ADC L2 Source", rt5665_mono_adc_l2_enum); + + +/* MONO ADC L1 Source */ +/* MX-27 [13] */ +static const char * const rt5665_mono_adc_l1_src[] = { + "DD Mux", "ADC" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_mono_adc_l1_enum, RT5665_MONO_ADC_MIXER, + RT5665_MONO_ADC_L1_SRC_SFT, rt5665_mono_adc_l1_src); + +static const struct snd_kcontrol_new rt5665_mono_adc_l1_mux = + SOC_DAPM_ENUM("Mono ADC L1 Source", rt5665_mono_adc_l1_enum); + +/* MX-27 [9][1]*/ +static const char * const rt5665_mono_dd_src[] = { + "STO2 DAC", "MONO DAC" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_mono_dd_l_enum, RT5665_MONO_ADC_MIXER, + RT5665_MONO_DD_L_SRC_SFT, rt5665_mono_dd_src); + +static const struct snd_kcontrol_new rt5665_mono_dd_l_mux = + SOC_DAPM_ENUM("Mono DD L Source", rt5665_mono_dd_l_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5665_mono_dd_r_enum, RT5665_MONO_ADC_MIXER, + RT5665_MONO_DD_R_SRC_SFT, rt5665_mono_dd_src); + +static const struct snd_kcontrol_new rt5665_mono_dd_r_mux = + SOC_DAPM_ENUM("Mono DD R Source", rt5665_mono_dd_r_enum); + +/* MONO ADC L Source, MONO ADC R Source*/ +/* MX-27 [11:10], MX-27 [3:2] */ +static const char * const rt5665_mono_adc_src[] = { + "ADC1 L", "ADC1 R", "ADC2 L", "ADC2 R" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_mono_adc_l_enum, RT5665_MONO_ADC_MIXER, + RT5665_MONO_ADC_L_SRC_SFT, rt5665_mono_adc_src); + +static const struct snd_kcontrol_new rt5665_mono_adc_l_mux = + SOC_DAPM_ENUM("Mono ADC L Source", rt5665_mono_adc_l_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5665_mono_adcr_enum, RT5665_MONO_ADC_MIXER, + RT5665_MONO_ADC_R_SRC_SFT, rt5665_mono_adc_src); + +static const struct snd_kcontrol_new rt5665_mono_adc_r_mux = + SOC_DAPM_ENUM("Mono ADC R Source", rt5665_mono_adcr_enum); + +/* MONO DMIC L Source */ +/* MX-27 [8] */ +static const char * const rt5665_mono_dmic_l_src[] = { + "DMIC1 L", "DMIC2 L" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_mono_dmic_l_enum, RT5665_MONO_ADC_MIXER, + RT5665_MONO_DMIC_L_SRC_SFT, rt5665_mono_dmic_l_src); + +static const struct snd_kcontrol_new rt5665_mono_dmic_l_mux = + SOC_DAPM_ENUM("Mono DMIC L Source", rt5665_mono_dmic_l_enum); + +/* MONO ADC R2 Source */ +/* MX-27 [4] */ +static const char * const rt5665_mono_adc_r2_src[] = { + "DAC MIXR", "DMIC" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_mono_adc_r2_enum, RT5665_MONO_ADC_MIXER, + RT5665_MONO_ADC_R2_SRC_SFT, rt5665_mono_adc_r2_src); + +static const struct snd_kcontrol_new rt5665_mono_adc_r2_mux = + SOC_DAPM_ENUM("Mono ADC R2 Source", rt5665_mono_adc_r2_enum); + +/* MONO ADC R1 Source */ +/* MX-27 [5] */ +static const char * const rt5665_mono_adc_r1_src[] = { + "DD Mux", "ADC" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_mono_adc_r1_enum, RT5665_MONO_ADC_MIXER, + RT5665_MONO_ADC_R1_SRC_SFT, rt5665_mono_adc_r1_src); + +static const struct snd_kcontrol_new rt5665_mono_adc_r1_mux = + SOC_DAPM_ENUM("Mono ADC R1 Source", rt5665_mono_adc_r1_enum); + +/* MONO DMIC R Source */ +/* MX-27 [0] */ +static const char * const rt5665_mono_dmic_r_src[] = { + "DMIC1 R", "DMIC2 R" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_mono_dmic_r_enum, RT5665_MONO_ADC_MIXER, + RT5665_MONO_DMIC_R_SRC_SFT, rt5665_mono_dmic_r_src); + +static const struct snd_kcontrol_new rt5665_mono_dmic_r_mux = + SOC_DAPM_ENUM("Mono DMIC R Source", rt5665_mono_dmic_r_enum); + + +/* STO2 ADC1 Source */ +/* MX-28 [13] [5] */ +static const char * const rt5665_sto2_adc1_src[] = { + "DD Mux", "ADC" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_sto2_adc1l_enum, RT5665_STO2_ADC_MIXER, + RT5665_STO2_ADC1L_SRC_SFT, rt5665_sto2_adc1_src); + +static const struct snd_kcontrol_new rt5665_sto2_adc1l_mux = + SOC_DAPM_ENUM("Stereo2 ADC1L Source", rt5665_sto2_adc1l_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5665_sto2_adc1r_enum, RT5665_STO2_ADC_MIXER, + RT5665_STO2_ADC1R_SRC_SFT, rt5665_sto2_adc1_src); + +static const struct snd_kcontrol_new rt5665_sto2_adc1r_mux = + SOC_DAPM_ENUM("Stereo2 ADC1L Source", rt5665_sto2_adc1r_enum); + +/* STO2 ADC Source */ +/* MX-28 [11:10] [3:2] */ +static const char * const rt5665_sto2_adc_src[] = { + "ADC1 L", "ADC1 R", "ADC2 L" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_sto2_adcl_enum, RT5665_STO2_ADC_MIXER, + RT5665_STO2_ADCL_SRC_SFT, rt5665_sto2_adc_src); + +static const struct snd_kcontrol_new rt5665_sto2_adcl_mux = + SOC_DAPM_ENUM("Stereo2 ADCL Source", rt5665_sto2_adcl_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5665_sto2_adcr_enum, RT5665_STO2_ADC_MIXER, + RT5665_STO2_ADCR_SRC_SFT, rt5665_sto2_adc_src); + +static const struct snd_kcontrol_new rt5665_sto2_adcr_mux = + SOC_DAPM_ENUM("Stereo2 ADCR Source", rt5665_sto2_adcr_enum); + +/* STO2 ADC2 Source */ +/* MX-28 [12] [4] */ +static const char * const rt5665_sto2_adc2_src[] = { + "DAC MIX", "DMIC" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_sto2_adc2l_enum, RT5665_STO2_ADC_MIXER, + RT5665_STO2_ADC2L_SRC_SFT, rt5665_sto2_adc2_src); + +static const struct snd_kcontrol_new rt5665_sto2_adc2l_mux = + SOC_DAPM_ENUM("Stereo2 ADC2L Source", rt5665_sto2_adc2l_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5665_sto2_adc2r_enum, RT5665_STO2_ADC_MIXER, + RT5665_STO2_ADC2R_SRC_SFT, rt5665_sto2_adc2_src); + +static const struct snd_kcontrol_new rt5665_sto2_adc2r_mux = + SOC_DAPM_ENUM("Stereo2 ADC2R Source", rt5665_sto2_adc2r_enum); + +/* STO2 DMIC Source */ +/* MX-28 [8] */ +static const char * const rt5665_sto2_dmic_src[] = { + "DMIC1", "DMIC2" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_sto2_dmic_enum, RT5665_STO2_ADC_MIXER, + RT5665_STO2_DMIC_SRC_SFT, rt5665_sto2_dmic_src); + +static const struct snd_kcontrol_new rt5665_sto2_dmic_mux = + SOC_DAPM_ENUM("Stereo2 DMIC Source", rt5665_sto2_dmic_enum); + +/* MX-28 [9] */ +static const char * const rt5665_sto2_dd_l_src[] = { + "STO2 DAC", "MONO DAC" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_sto2_dd_l_enum, RT5665_STO2_ADC_MIXER, + RT5665_STO2_DD_L_SRC_SFT, rt5665_sto2_dd_l_src); + +static const struct snd_kcontrol_new rt5665_sto2_dd_l_mux = + SOC_DAPM_ENUM("Stereo2 DD L Source", rt5665_sto2_dd_l_enum); + +/* MX-28 [1] */ +static const char * const rt5665_sto2_dd_r_src[] = { + "STO2 DAC", "MONO DAC" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_sto2_dd_r_enum, RT5665_STO2_ADC_MIXER, + RT5665_STO2_DD_R_SRC_SFT, rt5665_sto2_dd_r_src); + +static const struct snd_kcontrol_new rt5665_sto2_dd_r_mux = + SOC_DAPM_ENUM("Stereo2 DD R Source", rt5665_sto2_dd_r_enum); + +/* DAC R1 Source, DAC L1 Source*/ +/* MX-29 [11:10], MX-29 [9:8]*/ +static const char * const rt5665_dac1_src[] = { + "IF1 DAC1", "IF2_1 DAC", "IF2_2 DAC", "IF3 DAC" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_dac_r1_enum, RT5665_AD_DA_MIXER, + RT5665_DAC1_R_SEL_SFT, rt5665_dac1_src); + +static const struct snd_kcontrol_new rt5665_dac_r1_mux = + SOC_DAPM_ENUM("DAC R1 Source", rt5665_dac_r1_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5665_dac_l1_enum, RT5665_AD_DA_MIXER, + RT5665_DAC1_L_SEL_SFT, rt5665_dac1_src); + +static const struct snd_kcontrol_new rt5665_dac_l1_mux = + SOC_DAPM_ENUM("DAC L1 Source", rt5665_dac_l1_enum); + +/* DAC Digital Mixer L Source, DAC Digital Mixer R Source*/ +/* MX-2D [13:12], MX-2D [9:8]*/ +static const char * const rt5665_dig_dac_mix_src[] = { + "Stereo1 DAC Mixer", "Stereo2 DAC Mixer", "Mono DAC Mixer" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_dig_dac_mixl_enum, RT5665_A_DAC1_MUX, + RT5665_DAC_MIX_L_SFT, rt5665_dig_dac_mix_src); + +static const struct snd_kcontrol_new rt5665_dig_dac_mixl_mux = + SOC_DAPM_ENUM("DAC Digital Mixer L Source", rt5665_dig_dac_mixl_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5665_dig_dac_mixr_enum, RT5665_A_DAC1_MUX, + RT5665_DAC_MIX_R_SFT, rt5665_dig_dac_mix_src); + +static const struct snd_kcontrol_new rt5665_dig_dac_mixr_mux = + SOC_DAPM_ENUM("DAC Digital Mixer R Source", rt5665_dig_dac_mixr_enum); + +/* Analog DAC L1 Source, Analog DAC R1 Source*/ +/* MX-2D [5:4], MX-2D [1:0]*/ +static const char * const rt5665_alg_dac1_src[] = { + "Stereo1 DAC Mixer", "DAC1", "DMIC1" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_alg_dac_l1_enum, RT5665_A_DAC1_MUX, + RT5665_A_DACL1_SFT, rt5665_alg_dac1_src); + +static const struct snd_kcontrol_new rt5665_alg_dac_l1_mux = + SOC_DAPM_ENUM("Analog DAC L1 Source", rt5665_alg_dac_l1_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5665_alg_dac_r1_enum, RT5665_A_DAC1_MUX, + RT5665_A_DACR1_SFT, rt5665_alg_dac1_src); + +static const struct snd_kcontrol_new rt5665_alg_dac_r1_mux = + SOC_DAPM_ENUM("Analog DAC R1 Source", rt5665_alg_dac_r1_enum); + +/* Analog DAC LR Source, Analog DAC R2 Source*/ +/* MX-2E [5:4], MX-2E [0]*/ +static const char * const rt5665_alg_dac2_src[] = { + "Mono DAC Mixer", "DAC2" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_alg_dac_l2_enum, RT5665_A_DAC2_MUX, + RT5665_A_DACL2_SFT, rt5665_alg_dac2_src); + +static const struct snd_kcontrol_new rt5665_alg_dac_l2_mux = + SOC_DAPM_ENUM("Analog DAC L2 Source", rt5665_alg_dac_l2_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5665_alg_dac_r2_enum, RT5665_A_DAC2_MUX, + RT5665_A_DACR2_SFT, rt5665_alg_dac2_src); + +static const struct snd_kcontrol_new rt5665_alg_dac_r2_mux = + SOC_DAPM_ENUM("Analog DAC R2 Source", rt5665_alg_dac_r2_enum); + +/* Interface2 ADC Data Input*/ +/* MX-2F [14:12] */ +static const char * const rt5665_if2_1_adc_in_src[] = { + "STO1 ADC", "STO2 ADC", "MONO ADC", "IF1 DAC1", + "IF1 DAC2", "IF2_2 DAC", "IF3 DAC", "DAC1 MIX" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_if2_1_adc_in_enum, RT5665_DIG_INF2_DATA, + RT5665_IF3_ADC_IN_SFT, rt5665_if2_1_adc_in_src); + +static const struct snd_kcontrol_new rt5665_if2_1_adc_in_mux = + SOC_DAPM_ENUM("IF2_1 ADC IN Source", rt5665_if2_1_adc_in_enum); + +/* MX-2F [6:4] */ +static const char * const rt5665_if2_2_adc_in_src[] = { + "STO1 ADC", "STO2 ADC", "MONO ADC", "IF1 DAC1", + "IF1 DAC2", "IF2_1 DAC", "IF3 DAC", "DAC1 MIX" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_if2_2_adc_in_enum, RT5665_DIG_INF2_DATA, + RT5665_IF2_2_ADC_IN_SFT, rt5665_if2_2_adc_in_src); + +static const struct snd_kcontrol_new rt5665_if2_2_adc_in_mux = + SOC_DAPM_ENUM("IF2_1 ADC IN Source", rt5665_if2_2_adc_in_enum); + +/* Interface3 ADC Data Input*/ +/* MX-30 [6:4] */ +static const char * const rt5665_if3_adc_in_src[] = { + "STO1 ADC", "STO2 ADC", "MONO ADC", "IF1 DAC1", + "IF1 DAC2", "IF2_1 DAC", "IF2_2 DAC", "DAC1 MIX" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_if3_adc_in_enum, RT5665_DIG_INF3_DATA, + RT5665_IF3_ADC_IN_SFT, rt5665_if3_adc_in_src); + +static const struct snd_kcontrol_new rt5665_if3_adc_in_mux = + SOC_DAPM_ENUM("IF3 ADC IN Source", rt5665_if3_adc_in_enum); + +/* PDM 1 L/R*/ +/* MX-31 [11:10] [9:8] */ +static const char * const rt5665_pdm_src[] = { + "Stereo1 DAC", "Stereo2 DAC", "Mono DAC" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_pdm_l_enum, RT5665_PDM_OUT_CTRL, + RT5665_PDM1_L_SFT, rt5665_pdm_src); + +static const struct snd_kcontrol_new rt5665_pdm_l_mux = + SOC_DAPM_ENUM("PDM L Source", rt5665_pdm_l_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5665_pdm_r_enum, RT5665_PDM_OUT_CTRL, + RT5665_PDM1_R_SFT, rt5665_pdm_src); + +static const struct snd_kcontrol_new rt5665_pdm_r_mux = + SOC_DAPM_ENUM("PDM R Source", rt5665_pdm_r_enum); + + +/* I2S1 TDM ADCDAT Source */ +/* MX-7a[10] */ +static const char * const rt5665_if1_1_adc1_data_src[] = { + "STO1 ADC", "IF2_1 DAC", +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_if1_1_adc1_data_enum, RT5665_TDM_CTRL_3, + RT5665_IF1_ADC1_SEL_SFT, rt5665_if1_1_adc1_data_src); + +static const struct snd_kcontrol_new rt5665_if1_1_adc1_mux = + SOC_DAPM_ENUM("IF1_1 ADC1 Source", rt5665_if1_1_adc1_data_enum); + +/* MX-7a[9] */ +static const char * const rt5665_if1_1_adc2_data_src[] = { + "STO2 ADC", "IF2_2 DAC", +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_if1_1_adc2_data_enum, RT5665_TDM_CTRL_3, + RT5665_IF1_ADC2_SEL_SFT, rt5665_if1_1_adc2_data_src); + +static const struct snd_kcontrol_new rt5665_if1_1_adc2_mux = + SOC_DAPM_ENUM("IF1_1 ADC2 Source", rt5665_if1_1_adc2_data_enum); + +/* MX-7a[8] */ +static const char * const rt5665_if1_1_adc3_data_src[] = { + "MONO ADC", "IF3 DAC", +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_if1_1_adc3_data_enum, RT5665_TDM_CTRL_3, + RT5665_IF1_ADC3_SEL_SFT, rt5665_if1_1_adc3_data_src); + +static const struct snd_kcontrol_new rt5665_if1_1_adc3_mux = + SOC_DAPM_ENUM("IF1_1 ADC3 Source", rt5665_if1_1_adc3_data_enum); + +/* MX-7b[10] */ +static const char * const rt5665_if1_2_adc1_data_src[] = { + "STO1 ADC", "IF1 DAC", +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_if1_2_adc1_data_enum, RT5665_TDM_CTRL_4, + RT5665_IF1_ADC1_SEL_SFT, rt5665_if1_2_adc1_data_src); + +static const struct snd_kcontrol_new rt5665_if1_2_adc1_mux = + SOC_DAPM_ENUM("IF1_2 ADC1 Source", rt5665_if1_2_adc1_data_enum); + +/* MX-7b[9] */ +static const char * const rt5665_if1_2_adc2_data_src[] = { + "STO2 ADC", "IF2_1 DAC", +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_if1_2_adc2_data_enum, RT5665_TDM_CTRL_4, + RT5665_IF1_ADC2_SEL_SFT, rt5665_if1_2_adc2_data_src); + +static const struct snd_kcontrol_new rt5665_if1_2_adc2_mux = + SOC_DAPM_ENUM("IF1_2 ADC2 Source", rt5665_if1_2_adc2_data_enum); + +/* MX-7b[8] */ +static const char * const rt5665_if1_2_adc3_data_src[] = { + "MONO ADC", "IF2_2 DAC", +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_if1_2_adc3_data_enum, RT5665_TDM_CTRL_4, + RT5665_IF1_ADC3_SEL_SFT, rt5665_if1_2_adc3_data_src); + +static const struct snd_kcontrol_new rt5665_if1_2_adc3_mux = + SOC_DAPM_ENUM("IF1_2 ADC3 Source", rt5665_if1_2_adc3_data_enum); + +/* MX-7b[7] */ +static const char * const rt5665_if1_2_adc4_data_src[] = { + "DAC1", "IF3 DAC", +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_if1_2_adc4_data_enum, RT5665_TDM_CTRL_4, + RT5665_IF1_ADC4_SEL_SFT, rt5665_if1_2_adc4_data_src); + +static const struct snd_kcontrol_new rt5665_if1_2_adc4_mux = + SOC_DAPM_ENUM("IF1_2 ADC4 Source", rt5665_if1_2_adc4_data_enum); + +/* MX-7a[4:0] MX-7b[4:0] */ +static const char * const rt5665_tdm_adc_data_src[] = { + "1234", "1243", "1324", "1342", "1432", "1423", + "2134", "2143", "2314", "2341", "2431", "2413", + "3124", "3142", "3214", "3241", "3412", "3421", + "4123", "4132", "4213", "4231", "4312", "4321" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5665_tdm1_adc_data_enum, RT5665_TDM_CTRL_3, + RT5665_TDM_ADC_SEL_SFT, rt5665_tdm_adc_data_src); + +static const struct snd_kcontrol_new rt5665_tdm1_adc_mux = + SOC_DAPM_ENUM("TDM1 ADC Mux", rt5665_tdm1_adc_data_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5665_tdm2_adc_data_enum, RT5665_TDM_CTRL_4, + RT5665_TDM_ADC_SEL_SFT, rt5665_tdm_adc_data_src); + +static const struct snd_kcontrol_new rt5665_tdm2_adc_mux = + SOC_DAPM_ENUM("TDM2 ADCDAT Source", rt5665_tdm2_adc_data_enum); + +/* Out Volume Switch */ +static const struct snd_kcontrol_new monovol_switch = + SOC_DAPM_SINGLE("Switch", RT5665_MONO_OUT, RT5665_VOL_L_SFT, 1, 1); + +static const struct snd_kcontrol_new outvol_l_switch = + SOC_DAPM_SINGLE("Switch", RT5665_LOUT, RT5665_VOL_L_SFT, 1, 1); + +static const struct snd_kcontrol_new outvol_r_switch = + SOC_DAPM_SINGLE("Switch", RT5665_LOUT, RT5665_VOL_R_SFT, 1, 1); + +/* Out Switch */ +static const struct snd_kcontrol_new mono_switch = + SOC_DAPM_SINGLE("Switch", RT5665_MONO_OUT, RT5665_L_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new hpo_switch = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5665_HP_CTRL_2, + RT5665_VOL_L_SFT, 1, 0); + +static const struct snd_kcontrol_new lout_l_switch = + SOC_DAPM_SINGLE("Switch", RT5665_LOUT, RT5665_L_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new lout_r_switch = + SOC_DAPM_SINGLE("Switch", RT5665_LOUT, RT5665_R_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new pdm_l_switch = + SOC_DAPM_SINGLE("Switch", RT5665_PDM_OUT_CTRL, + RT5665_M_PDM1_L_SFT, 1, 1); + +static const struct snd_kcontrol_new pdm_r_switch = + SOC_DAPM_SINGLE("Switch", RT5665_PDM_OUT_CTRL, + RT5665_M_PDM1_R_SFT, 1, 1); + +static int rt5665_mono_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, RT5665_MONO_NG2_CTRL_1, + RT5665_NG2_EN_MASK, RT5665_NG2_EN); + snd_soc_update_bits(codec, RT5665_MONO_AMP_CALIB_CTRL_1, 0x40, + 0x0); + snd_soc_update_bits(codec, RT5665_MONO_OUT, 0x10, 0x10); + snd_soc_update_bits(codec, RT5665_MONO_OUT, 0x20, 0x20); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, RT5665_MONO_OUT, 0x20, 0); + snd_soc_update_bits(codec, RT5665_MONO_OUT, 0x10, 0); + snd_soc_update_bits(codec, RT5665_MONO_AMP_CALIB_CTRL_1, 0x40, + 0x40); + snd_soc_update_bits(codec, RT5665_MONO_NG2_CTRL_1, + RT5665_NG2_EN_MASK, RT5665_NG2_DIS); + break; + + default: + return 0; + } + + return 0; + +} + +static int rt5665_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, RT5665_STO_NG2_CTRL_1, + RT5665_NG2_EN_MASK, RT5665_NG2_EN); + snd_soc_write(codec, RT5665_HP_LOGIC_CTRL_2, 0x0003); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_write(codec, RT5665_HP_LOGIC_CTRL_2, 0x0002); + snd_soc_update_bits(codec, RT5665_STO_NG2_CTRL_1, + RT5665_NG2_EN_MASK, RT5665_NG2_DIS); + break; + + default: + return 0; + } + + return 0; + +} + +static int rt5665_lout_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, RT5665_DEPOP_1, + RT5665_PUMP_EN, RT5665_PUMP_EN); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, RT5665_DEPOP_1, + RT5665_PUMP_EN, 0); + break; + + default: + return 0; + } + + return 0; + +} + +static int set_dmic_power(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /*Add delay to avoid pop noise*/ + msleep(150); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5655_set_verf(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + switch (w->shift) { + case RT5665_PWR_VREF1_BIT: + snd_soc_update_bits(codec, RT5665_PWR_ANLG_1, + RT5665_PWR_FV1, 0); + break; + + case RT5665_PWR_VREF2_BIT: + snd_soc_update_bits(codec, RT5665_PWR_ANLG_1, + RT5665_PWR_FV2, 0); + break; + + case RT5665_PWR_VREF3_BIT: + snd_soc_update_bits(codec, RT5665_PWR_ANLG_1, + RT5665_PWR_FV3, 0); + break; + + default: + break; + } + break; + + case SND_SOC_DAPM_POST_PMU: + usleep_range(15000, 20000); + switch (w->shift) { + case RT5665_PWR_VREF1_BIT: + snd_soc_update_bits(codec, RT5665_PWR_ANLG_1, + RT5665_PWR_FV1, RT5665_PWR_FV1); + break; + + case RT5665_PWR_VREF2_BIT: + snd_soc_update_bits(codec, RT5665_PWR_ANLG_1, + RT5665_PWR_FV2, RT5665_PWR_FV2); + break; + + case RT5665_PWR_VREF3_BIT: + snd_soc_update_bits(codec, RT5665_PWR_ANLG_1, + RT5665_PWR_FV3, RT5665_PWR_FV3); + break; + + default: + break; + } + break; + + default: + return 0; + } + + return 0; +} + + +static const struct snd_soc_dapm_widget rt5665_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("LDO2", RT5665_PWR_ANLG_3, RT5665_PWR_LDO2_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL", RT5665_PWR_ANLG_3, RT5665_PWR_PLL_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("Mic Det Power", RT5665_PWR_VOL, + RT5665_PWR_MIC_DET_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Vref1", RT5665_PWR_ANLG_1, RT5665_PWR_VREF1_BIT, 0, + rt5655_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("Vref2", RT5665_PWR_ANLG_1, RT5665_PWR_VREF2_BIT, 0, + rt5655_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("Vref3", RT5665_PWR_ANLG_1, RT5665_PWR_VREF3_BIT, 0, + rt5655_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + + /* ASRC */ + SND_SOC_DAPM_SUPPLY_S("I2S1 ASRC", 1, RT5665_ASRC_1, + RT5665_I2S1_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5665_ASRC_1, + RT5665_I2S2_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("I2S3 ASRC", 1, RT5665_ASRC_1, + RT5665_I2S3_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC STO1 ASRC", 1, RT5665_ASRC_1, + RT5665_DAC_STO1_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC STO2 ASRC", 1, RT5665_ASRC_1, + RT5665_DAC_STO2_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC Mono L ASRC", 1, RT5665_ASRC_1, + RT5665_DAC_MONO_L_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC Mono R ASRC", 1, RT5665_ASRC_1, + RT5665_DAC_MONO_R_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5665_ASRC_1, + RT5665_ADC_STO1_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC Mono L ASRC", 1, RT5665_ASRC_1, + RT5665_ADC_MONO_L_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC Mono R ASRC", 1, RT5665_ASRC_1, + RT5665_ADC_MONO_R_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC STO1 ASRC", 1, RT5665_ASRC_1, + RT5665_DMIC_STO1_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC STO2 ASRC", 1, RT5665_ASRC_1, + RT5665_DMIC_STO2_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC MONO L ASRC", 1, RT5665_ASRC_1, + RT5665_DMIC_MONO_L_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC MONO R ASRC", 1, RT5665_ASRC_1, + RT5665_DMIC_MONO_R_ASRC_SFT, 0, NULL, 0), + + /* Input Side */ + SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5665_PWR_ANLG_2, RT5665_PWR_MB1_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS2", RT5665_PWR_ANLG_2, RT5665_PWR_MB2_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS3", RT5665_PWR_ANLG_2, RT5665_PWR_MB3_BIT, + 0, NULL, 0), + + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC L1"), + SND_SOC_DAPM_INPUT("DMIC R1"), + SND_SOC_DAPM_INPUT("DMIC L2"), + SND_SOC_DAPM_INPUT("DMIC R2"), + + SND_SOC_DAPM_INPUT("IN1P"), + SND_SOC_DAPM_INPUT("IN1N"), + SND_SOC_DAPM_INPUT("IN2P"), + SND_SOC_DAPM_INPUT("IN2N"), + SND_SOC_DAPM_INPUT("IN3P"), + SND_SOC_DAPM_INPUT("IN3N"), + SND_SOC_DAPM_INPUT("IN4P"), + SND_SOC_DAPM_INPUT("IN4N"), + + 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_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0, + set_dmic_clk, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5665_DMIC_CTRL_1, + RT5665_DMIC_1_EN_SFT, 0, set_dmic_power, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("DMIC2 Power", RT5665_DMIC_CTRL_1, + RT5665_DMIC_2_EN_SFT, 0, set_dmic_power, SND_SOC_DAPM_POST_PMU), + + /* Boost */ + SND_SOC_DAPM_PGA("BST1", SND_SOC_NOPM, + 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("BST2", SND_SOC_NOPM, + 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("BST3", SND_SOC_NOPM, + 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("BST4", SND_SOC_NOPM, + 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("BST1 CBJ", SND_SOC_NOPM, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BST1 Power", RT5665_PWR_ANLG_2, + RT5665_PWR_BST1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BST2 Power", RT5665_PWR_ANLG_2, + RT5665_PWR_BST2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BST3 Power", RT5665_PWR_ANLG_2, + RT5665_PWR_BST3_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BST4 Power", RT5665_PWR_ANLG_2, + RT5665_PWR_BST4_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BST1P Power", RT5665_PWR_ANLG_2, + RT5665_PWR_BST1_P_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BST2P Power", RT5665_PWR_ANLG_2, + RT5665_PWR_BST2_P_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BST3P Power", RT5665_PWR_ANLG_2, + RT5665_PWR_BST3_P_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BST4P Power", RT5665_PWR_ANLG_2, + RT5665_PWR_BST4_P_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CBJ Power", RT5665_PWR_ANLG_3, + RT5665_PWR_CBJ_BIT, 0, NULL, 0), + + + /* Input Volume */ + SND_SOC_DAPM_PGA("INL VOL", RT5665_PWR_VOL, RT5665_PWR_IN_L_BIT, + 0, NULL, 0), + SND_SOC_DAPM_PGA("INR VOL", RT5665_PWR_VOL, RT5665_PWR_IN_R_BIT, + 0, NULL, 0), + + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIX1L", SND_SOC_NOPM, 0, 0, rt5665_rec1_l_mix, + ARRAY_SIZE(rt5665_rec1_l_mix)), + SND_SOC_DAPM_MIXER("RECMIX1R", SND_SOC_NOPM, 0, 0, rt5665_rec1_r_mix, + ARRAY_SIZE(rt5665_rec1_r_mix)), + SND_SOC_DAPM_MIXER("RECMIX2L", SND_SOC_NOPM, 0, 0, rt5665_rec2_l_mix, + ARRAY_SIZE(rt5665_rec2_l_mix)), + SND_SOC_DAPM_MIXER("RECMIX2R", SND_SOC_NOPM, 0, 0, rt5665_rec2_r_mix, + ARRAY_SIZE(rt5665_rec2_r_mix)), + SND_SOC_DAPM_SUPPLY("RECMIX1L Power", RT5665_PWR_ANLG_2, + RT5665_PWR_RM1_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RECMIX1R Power", RT5665_PWR_ANLG_2, + RT5665_PWR_RM1_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RECMIX2L Power", RT5665_PWR_MIXER, + RT5665_PWR_RM2_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RECMIX2R Power", RT5665_PWR_MIXER, + RT5665_PWR_RM2_R_BIT, 0, NULL, 0), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC1 L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC1 R", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC2 L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC2 R", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY("ADC1 L Power", RT5665_PWR_DIG_1, + RT5665_PWR_ADC_L1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1 R Power", RT5665_PWR_DIG_1, + RT5665_PWR_ADC_R1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC2 L Power", RT5665_PWR_DIG_1, + RT5665_PWR_ADC_L2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC2 R Power", RT5665_PWR_DIG_1, + RT5665_PWR_ADC_R2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1 clock", RT5665_CHOP_ADC, + RT5665_CKGEN_ADC1_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC2 clock", RT5665_CHOP_ADC, + RT5665_CKGEN_ADC2_SFT, 0, NULL, 0), + + /* ADC Mux */ + SND_SOC_DAPM_MUX("Stereo1 DMIC L Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto1_dmic_mux), + SND_SOC_DAPM_MUX("Stereo1 DMIC R Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto1_dmic_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto1_adc1l_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto1_adc1r_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto1_adc2l_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto1_adc2r_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto1_adcl_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto1_adcr_mux), + SND_SOC_DAPM_MUX("Stereo1 DD L Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto1_dd_l_mux), + SND_SOC_DAPM_MUX("Stereo1 DD R Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto1_dd_r_mux), + SND_SOC_DAPM_MUX("Mono ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_mono_adc_l2_mux), + SND_SOC_DAPM_MUX("Mono ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_mono_adc_r2_mux), + SND_SOC_DAPM_MUX("Mono ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_mono_adc_l1_mux), + SND_SOC_DAPM_MUX("Mono ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_mono_adc_r1_mux), + SND_SOC_DAPM_MUX("Mono DMIC L Mux", SND_SOC_NOPM, 0, 0, + &rt5665_mono_dmic_l_mux), + SND_SOC_DAPM_MUX("Mono DMIC R Mux", SND_SOC_NOPM, 0, 0, + &rt5665_mono_dmic_r_mux), + SND_SOC_DAPM_MUX("Mono ADC L Mux", SND_SOC_NOPM, 0, 0, + &rt5665_mono_adc_l_mux), + SND_SOC_DAPM_MUX("Mono ADC R Mux", SND_SOC_NOPM, 0, 0, + &rt5665_mono_adc_r_mux), + SND_SOC_DAPM_MUX("Mono DD L Mux", SND_SOC_NOPM, 0, 0, + &rt5665_mono_dd_l_mux), + SND_SOC_DAPM_MUX("Mono DD R Mux", SND_SOC_NOPM, 0, 0, + &rt5665_mono_dd_r_mux), + SND_SOC_DAPM_MUX("Stereo2 DMIC L Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto2_dmic_mux), + SND_SOC_DAPM_MUX("Stereo2 DMIC R Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto2_dmic_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto2_adc1l_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto2_adc1r_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto2_adc2l_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto2_adc2r_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC L Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto2_adcl_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC R Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto2_adcr_mux), + SND_SOC_DAPM_MUX("Stereo2 DD L Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto2_dd_l_mux), + SND_SOC_DAPM_MUX("Stereo2 DD R Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto2_dd_r_mux), + /* ADC Mixer */ + SND_SOC_DAPM_SUPPLY("ADC Stereo1 Filter", RT5665_PWR_DIG_2, + RT5665_PWR_ADC_S1F_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC Stereo2 Filter", RT5665_PWR_DIG_2, + RT5665_PWR_ADC_S2F_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", RT5665_STO1_ADC_DIG_VOL, + RT5665_L_MUTE_SFT, 1, rt5665_sto1_adc_l_mix, + ARRAY_SIZE(rt5665_sto1_adc_l_mix)), + SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", RT5665_STO1_ADC_DIG_VOL, + RT5665_R_MUTE_SFT, 1, rt5665_sto1_adc_r_mix, + ARRAY_SIZE(rt5665_sto1_adc_r_mix)), + SND_SOC_DAPM_MIXER("Stereo2 ADC MIXL", RT5665_STO2_ADC_DIG_VOL, + RT5665_L_MUTE_SFT, 1, rt5665_sto2_adc_l_mix, + ARRAY_SIZE(rt5665_sto2_adc_l_mix)), + SND_SOC_DAPM_MIXER("Stereo2 ADC MIXR", RT5665_STO2_ADC_DIG_VOL, + RT5665_R_MUTE_SFT, 1, rt5665_sto2_adc_r_mix, + ARRAY_SIZE(rt5665_sto2_adc_r_mix)), + SND_SOC_DAPM_SUPPLY("ADC Mono Left Filter", RT5665_PWR_DIG_2, + RT5665_PWR_ADC_MF_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Mono ADC MIXL", RT5665_MONO_ADC_DIG_VOL, + RT5665_L_MUTE_SFT, 1, rt5665_mono_adc_l_mix, + ARRAY_SIZE(rt5665_mono_adc_l_mix)), + SND_SOC_DAPM_SUPPLY("ADC Mono Right Filter", RT5665_PWR_DIG_2, + RT5665_PWR_ADC_MF_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Mono ADC MIXR", RT5665_MONO_ADC_DIG_VOL, + RT5665_R_MUTE_SFT, 1, rt5665_mono_adc_r_mix, + ARRAY_SIZE(rt5665_mono_adc_r_mix)), + + /* ADC PGA */ + SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo2 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Mono ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("I2S1_1", RT5665_PWR_DIG_1, RT5665_PWR_I2S1_1_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S1_2", RT5665_PWR_DIG_1, RT5665_PWR_I2S1_2_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S2_1", RT5665_PWR_DIG_1, RT5665_PWR_I2S2_1_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S2_2", RT5665_PWR_DIG_1, RT5665_PWR_I2S2_2_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S3", RT5665_PWR_DIG_1, RT5665_PWR_I2S3_BIT, + 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC2 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC2 R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC3 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC3 R", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_PGA("IF2_1 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2_2 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2_1 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2_1 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2_2 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2_2 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2_1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2_2 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_PGA("IF3 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF3 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF3 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF3 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Interface Select */ + SND_SOC_DAPM_MUX("IF1_1_ADC1 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_1_adc1_mux), + SND_SOC_DAPM_MUX("IF1_1_ADC2 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_1_adc2_mux), + SND_SOC_DAPM_MUX("IF1_1_ADC3 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_1_adc3_mux), + SND_SOC_DAPM_PGA("IF1_1_ADC4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MUX("IF1_2_ADC1 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_2_adc1_mux), + SND_SOC_DAPM_MUX("IF1_2_ADC2 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_2_adc2_mux), + SND_SOC_DAPM_MUX("IF1_2_ADC3 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_2_adc3_mux), + SND_SOC_DAPM_MUX("IF1_2_ADC4 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_2_adc4_mux), + SND_SOC_DAPM_MUX("TDM1 slot 01 Data Mux", SND_SOC_NOPM, 0, 0, + &rt5665_tdm1_adc_mux), + SND_SOC_DAPM_MUX("TDM1 slot 23 Data Mux", SND_SOC_NOPM, 0, 0, + &rt5665_tdm1_adc_mux), + SND_SOC_DAPM_MUX("TDM1 slot 45 Data Mux", SND_SOC_NOPM, 0, 0, + &rt5665_tdm1_adc_mux), + SND_SOC_DAPM_MUX("TDM1 slot 67 Data Mux", SND_SOC_NOPM, 0, 0, + &rt5665_tdm1_adc_mux), + SND_SOC_DAPM_MUX("TDM2 slot 01 Data Mux", SND_SOC_NOPM, 0, 0, + &rt5665_tdm2_adc_mux), + SND_SOC_DAPM_MUX("TDM2 slot 23 Data Mux", SND_SOC_NOPM, 0, 0, + &rt5665_tdm2_adc_mux), + SND_SOC_DAPM_MUX("TDM2 slot 45 Data Mux", SND_SOC_NOPM, 0, 0, + &rt5665_tdm2_adc_mux), + SND_SOC_DAPM_MUX("TDM2 slot 67 Data Mux", SND_SOC_NOPM, 0, 0, + &rt5665_tdm2_adc_mux), + SND_SOC_DAPM_MUX("IF2_1 ADC Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if2_1_adc_in_mux), + SND_SOC_DAPM_MUX("IF2_2 ADC Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if2_2_adc_in_mux), + SND_SOC_DAPM_MUX("IF3 ADC Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if3_adc_in_mux), + SND_SOC_DAPM_MUX("IF1_1 0 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_1_01_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_1 1 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_1_01_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_1 2 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_1_23_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_1 3 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_1_23_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_1 4 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_1_45_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_1 5 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_1_45_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_1 6 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_1_67_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_1 7 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_1_67_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_2 0 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_2_01_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_2 1 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_2_01_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_2 2 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_2_23_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_2 3 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_2_23_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_2 4 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_2_45_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_2 5 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_2_45_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_2 6 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_2_67_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_2 7 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_2_67_adc_swap_mux), + SND_SOC_DAPM_MUX("IF2_1 DAC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if2_1_dac_swap_mux), + SND_SOC_DAPM_MUX("IF2_1 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if2_1_adc_swap_mux), + SND_SOC_DAPM_MUX("IF2_2 DAC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if2_2_dac_swap_mux), + SND_SOC_DAPM_MUX("IF2_2 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if2_2_adc_swap_mux), + SND_SOC_DAPM_MUX("IF3 DAC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if3_dac_swap_mux), + SND_SOC_DAPM_MUX("IF3 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if3_adc_swap_mux), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_OUT("AIF1_1TX slot 0", "AIF1_1 Capture", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_1TX slot 1", "AIF1_1 Capture", + 1, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_1TX slot 2", "AIF1_1 Capture", + 2, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_1TX slot 3", "AIF1_1 Capture", + 3, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_1TX slot 4", "AIF1_1 Capture", + 4, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_1TX slot 5", "AIF1_1 Capture", + 5, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_1TX slot 6", "AIF1_1 Capture", + 6, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_1TX slot 7", "AIF1_1 Capture", + 7, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_2TX slot 0", "AIF1_2 Capture", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_2TX slot 1", "AIF1_2 Capture", + 1, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_2TX slot 2", "AIF1_2 Capture", + 2, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_2TX slot 3", "AIF1_2 Capture", + 3, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_2TX slot 4", "AIF1_2 Capture", + 4, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_2TX slot 5", "AIF1_2 Capture", + 5, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_2TX slot 6", "AIF1_2 Capture", + 6, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_2TX slot 7", "AIF1_2 Capture", + 7, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2_1TX", "AIF2_1 Capture", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2_2TX", "AIF2_2 Capture", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF3TX", "AIF3 Capture", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF2_1RX", "AIF2_1 Playback", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF2_2RX", "AIF2_2 Playback", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF3RX", "AIF3 Playback", + 0, SND_SOC_NOPM, 0, 0), + + /* Output Side */ + /* DAC mixer before sound effect */ + SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0, + rt5665_dac_l_mix, ARRAY_SIZE(rt5665_dac_l_mix)), + SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0, + rt5665_dac_r_mix, ARRAY_SIZE(rt5665_dac_r_mix)), + + /* DAC channel Mux */ + SND_SOC_DAPM_MUX("DAC L1 Mux", SND_SOC_NOPM, 0, 0, &rt5665_dac_l1_mux), + SND_SOC_DAPM_MUX("DAC R1 Mux", SND_SOC_NOPM, 0, 0, &rt5665_dac_r1_mux), + SND_SOC_DAPM_MUX("DAC L2 Mux", SND_SOC_NOPM, 0, 0, &rt5665_dac_l2_mux), + SND_SOC_DAPM_MUX("DAC R2 Mux", SND_SOC_NOPM, 0, 0, &rt5665_dac_r2_mux), + SND_SOC_DAPM_MUX("DAC L3 Mux", SND_SOC_NOPM, 0, 0, &rt5665_dac_l3_mux), + SND_SOC_DAPM_MUX("DAC R3 Mux", SND_SOC_NOPM, 0, 0, &rt5665_dac_r3_mux), + + SND_SOC_DAPM_MUX("DAC L1 Source", SND_SOC_NOPM, 0, 0, + &rt5665_alg_dac_l1_mux), + SND_SOC_DAPM_MUX("DAC R1 Source", SND_SOC_NOPM, 0, 0, + &rt5665_alg_dac_r1_mux), + SND_SOC_DAPM_MUX("DAC L2 Source", SND_SOC_NOPM, 0, 0, + &rt5665_alg_dac_l2_mux), + SND_SOC_DAPM_MUX("DAC R2 Source", SND_SOC_NOPM, 0, 0, + &rt5665_alg_dac_r2_mux), + + /* DAC Mixer */ + SND_SOC_DAPM_SUPPLY("DAC Stereo1 Filter", RT5665_PWR_DIG_2, + RT5665_PWR_DAC_S1F_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Stereo2 Filter", RT5665_PWR_DIG_2, + RT5665_PWR_DAC_S2F_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Mono Left Filter", RT5665_PWR_DIG_2, + RT5665_PWR_DAC_MF_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Mono Right Filter", RT5665_PWR_DIG_2, + RT5665_PWR_DAC_MF_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Stereo1 DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5665_sto1_dac_l_mix, ARRAY_SIZE(rt5665_sto1_dac_l_mix)), + SND_SOC_DAPM_MIXER("Stereo1 DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5665_sto1_dac_r_mix, ARRAY_SIZE(rt5665_sto1_dac_r_mix)), + SND_SOC_DAPM_MIXER("Stereo2 DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5665_sto2_dac_l_mix, ARRAY_SIZE(rt5665_sto2_dac_l_mix)), + SND_SOC_DAPM_MIXER("Stereo2 DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5665_sto2_dac_r_mix, ARRAY_SIZE(rt5665_sto2_dac_r_mix)), + SND_SOC_DAPM_MIXER("Mono DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5665_mono_dac_l_mix, ARRAY_SIZE(rt5665_mono_dac_l_mix)), + SND_SOC_DAPM_MIXER("Mono DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5665_mono_dac_r_mix, ARRAY_SIZE(rt5665_mono_dac_r_mix)), + SND_SOC_DAPM_MUX("DAC MIXL", SND_SOC_NOPM, 0, 0, + &rt5665_dig_dac_mixl_mux), + SND_SOC_DAPM_MUX("DAC MIXR", SND_SOC_NOPM, 0, 0, + &rt5665_dig_dac_mixr_mux), + + /* DACs */ + SND_SOC_DAPM_DAC("DAC L1", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC R1", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY("DAC L2 Power", RT5665_PWR_DIG_1, + RT5665_PWR_DAC_L2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC R2 Power", RT5665_PWR_DIG_1, + RT5665_PWR_DAC_R2_BIT, 0, NULL, 0), + SND_SOC_DAPM_DAC("DAC L2", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC R2", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_PGA("DAC1 MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("DAC 1 Clock", 1, RT5665_CHOP_DAC, + RT5665_CKGEN_DAC1_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC 2 Clock", 1, RT5665_CHOP_DAC, + RT5665_CKGEN_DAC2_SFT, 0, NULL, 0), + + /* OUT Mixer */ + SND_SOC_DAPM_MIXER("MONOVOL MIX", RT5665_PWR_MIXER, RT5665_PWR_MM_BIT, + 0, rt5665_monovol_mix, ARRAY_SIZE(rt5665_monovol_mix)), + SND_SOC_DAPM_MIXER("OUT MIXL", RT5665_PWR_MIXER, RT5665_PWR_OM_L_BIT, + 0, rt5665_out_l_mix, ARRAY_SIZE(rt5665_out_l_mix)), + SND_SOC_DAPM_MIXER("OUT MIXR", RT5665_PWR_MIXER, RT5665_PWR_OM_R_BIT, + 0, rt5665_out_r_mix, ARRAY_SIZE(rt5665_out_r_mix)), + + /* Output Volume */ + SND_SOC_DAPM_SWITCH("MONOVOL", RT5665_PWR_VOL, RT5665_PWR_MV_BIT, 0, + &monovol_switch), + SND_SOC_DAPM_SWITCH("OUTVOL L", RT5665_PWR_VOL, RT5665_PWR_OV_L_BIT, 0, + &outvol_l_switch), + SND_SOC_DAPM_SWITCH("OUTVOL R", RT5665_PWR_VOL, RT5665_PWR_OV_R_BIT, 0, + &outvol_r_switch), + + /* MONO/HPO/LOUT */ + SND_SOC_DAPM_MIXER("Mono MIX", SND_SOC_NOPM, 0, 0, rt5665_mono_mix, + ARRAY_SIZE(rt5665_mono_mix)), + SND_SOC_DAPM_MIXER("LOUT L MIX", SND_SOC_NOPM, 0, 0, rt5665_lout_l_mix, + ARRAY_SIZE(rt5665_lout_l_mix)), + SND_SOC_DAPM_MIXER("LOUT R MIX", SND_SOC_NOPM, 0, 0, rt5665_lout_r_mix, + ARRAY_SIZE(rt5665_lout_r_mix)), + SND_SOC_DAPM_PGA_S("Mono Amp", 1, RT5665_PWR_ANLG_1, RT5665_PWR_MA_BIT, + 0, rt5665_mono_event, SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5665_hp_event, + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_PGA_S("LOUT Amp", 1, RT5665_PWR_ANLG_1, + RT5665_PWR_LM_BIT, 0, rt5665_lout_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU), + + SND_SOC_DAPM_SUPPLY("Charge Pump", SND_SOC_NOPM, 0, 0, + rt5665_charge_pump_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SWITCH("Mono Playback", SND_SOC_NOPM, 0, 0, + &mono_switch), + SND_SOC_DAPM_SWITCH("HPO Playback", SND_SOC_NOPM, 0, 0, + &hpo_switch), + SND_SOC_DAPM_SWITCH("LOUT L Playback", SND_SOC_NOPM, 0, 0, + &lout_l_switch), + SND_SOC_DAPM_SWITCH("LOUT R Playback", SND_SOC_NOPM, 0, 0, + &lout_r_switch), + SND_SOC_DAPM_SWITCH("PDM L Playback", SND_SOC_NOPM, 0, 0, + &pdm_l_switch), + SND_SOC_DAPM_SWITCH("PDM R Playback", SND_SOC_NOPM, 0, 0, + &pdm_r_switch), + + /* PDM */ + SND_SOC_DAPM_SUPPLY("PDM Power", RT5665_PWR_DIG_2, + RT5665_PWR_PDM1_BIT, 0, NULL, 0), + SND_SOC_DAPM_MUX("PDM L Mux", SND_SOC_NOPM, + 0, 1, &rt5665_pdm_l_mux), + SND_SOC_DAPM_MUX("PDM R Mux", SND_SOC_NOPM, + 0, 1, &rt5665_pdm_r_mux), + + /* CLK DET */ + SND_SOC_DAPM_SUPPLY("CLKDET SYS", RT5665_CLK_DET, RT5665_SYS_CLK_DET, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET HP", RT5665_CLK_DET, RT5665_HP_CLK_DET, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET MONO", RT5665_CLK_DET, RT5665_MONO_CLK_DET, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET LOUT", RT5665_CLK_DET, RT5665_LOUT_CLK_DET, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET", RT5665_CLK_DET, RT5665_POW_CLK_DET, + 0, NULL, 0), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), + SND_SOC_DAPM_OUTPUT("LOUTL"), + SND_SOC_DAPM_OUTPUT("LOUTR"), + SND_SOC_DAPM_OUTPUT("MONOOUT"), + SND_SOC_DAPM_OUTPUT("PDML"), + SND_SOC_DAPM_OUTPUT("PDMR"), +}; + +static const struct snd_soc_dapm_route rt5665_dapm_routes[] = { + /*PLL*/ + {"ADC Stereo1 Filter", NULL, "PLL", is_sys_clk_from_pll}, + {"ADC Stereo2 Filter", NULL, "PLL", is_sys_clk_from_pll}, + {"ADC Mono Left Filter", NULL, "PLL", is_sys_clk_from_pll}, + {"ADC Mono Right Filter", NULL, "PLL", is_sys_clk_from_pll}, + {"DAC Stereo1 Filter", NULL, "PLL", is_sys_clk_from_pll}, + {"DAC Stereo2 Filter", NULL, "PLL", is_sys_clk_from_pll}, + {"DAC Mono Left Filter", NULL, "PLL", is_sys_clk_from_pll}, + {"DAC Mono Right Filter", NULL, "PLL", is_sys_clk_from_pll}, + + /*ASRC*/ + {"ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc}, + {"ADC Mono Left Filter", NULL, "ADC Mono L ASRC", is_using_asrc}, + {"ADC Mono Right Filter", NULL, "ADC Mono R ASRC", is_using_asrc}, + {"DAC Mono Left Filter", NULL, "DAC Mono L ASRC", is_using_asrc}, + {"DAC Mono Right Filter", NULL, "DAC Mono R ASRC", is_using_asrc}, + {"DAC Stereo1 Filter", NULL, "DAC STO1 ASRC", is_using_asrc}, + {"DAC Stereo2 Filter", NULL, "DAC STO2 ASRC", is_using_asrc}, + + /*Vref*/ + {"Mic Det Power", NULL, "Vref2"}, + {"MICBIAS1", NULL, "Vref1"}, + {"MICBIAS1", NULL, "Vref2"}, + {"MICBIAS2", NULL, "Vref1"}, + {"MICBIAS2", NULL, "Vref2"}, + {"MICBIAS3", NULL, "Vref1"}, + {"MICBIAS3", NULL, "Vref2"}, + + {"Stereo1 DMIC L Mux", NULL, "DMIC STO1 ASRC"}, + {"Stereo1 DMIC R Mux", NULL, "DMIC STO1 ASRC"}, + {"Stereo2 DMIC L Mux", NULL, "DMIC STO2 ASRC"}, + {"Stereo2 DMIC R Mux", NULL, "DMIC STO2 ASRC"}, + {"Mono DMIC L Mux", NULL, "DMIC MONO L ASRC"}, + {"Mono DMIC R Mux", NULL, "DMIC MONO R ASRC"}, + + {"I2S1_1", NULL, "I2S1 ASRC"}, + {"I2S1_2", NULL, "I2S1 ASRC"}, + {"I2S2_1", NULL, "I2S2 ASRC"}, + {"I2S2_2", NULL, "I2S2 ASRC"}, + {"I2S3", NULL, "I2S3 ASRC"}, + + {"CLKDET SYS", NULL, "CLKDET"}, + {"CLKDET HP", NULL, "CLKDET"}, + {"CLKDET MONO", NULL, "CLKDET"}, + {"CLKDET LOUT", NULL, "CLKDET"}, + + {"IN1P", NULL, "LDO2"}, + {"IN2P", NULL, "LDO2"}, + {"IN3P", NULL, "LDO2"}, + {"IN4P", NULL, "LDO2"}, + + {"DMIC1", NULL, "DMIC L1"}, + {"DMIC1", NULL, "DMIC R1"}, + {"DMIC2", NULL, "DMIC L2"}, + {"DMIC2", NULL, "DMIC R2"}, + + {"BST1", NULL, "IN1P"}, + {"BST1", NULL, "IN1N"}, + {"BST1", NULL, "BST1 Power"}, + {"BST1", NULL, "BST1P Power"}, + {"BST2", NULL, "IN2P"}, + {"BST2", NULL, "IN2N"}, + {"BST2", NULL, "BST2 Power"}, + {"BST2", NULL, "BST2P Power"}, + {"BST3", NULL, "IN3P"}, + {"BST3", NULL, "IN3N"}, + {"BST3", NULL, "BST3 Power"}, + {"BST3", NULL, "BST3P Power"}, + {"BST4", NULL, "IN4P"}, + {"BST4", NULL, "IN4N"}, + {"BST4", NULL, "BST4 Power"}, + {"BST4", NULL, "BST4P Power"}, + {"BST1 CBJ", NULL, "IN1P"}, + {"BST1 CBJ", NULL, "IN1N"}, + {"BST1 CBJ", NULL, "CBJ Power"}, + {"CBJ Power", NULL, "Vref2"}, + + {"INL VOL", NULL, "IN3P"}, + {"INR VOL", NULL, "IN3N"}, + + {"RECMIX1L", "CBJ Switch", "BST1 CBJ"}, + {"RECMIX1L", "INL Switch", "INL VOL"}, + {"RECMIX1L", "INR Switch", "INR VOL"}, + {"RECMIX1L", "BST4 Switch", "BST4"}, + {"RECMIX1L", "BST3 Switch", "BST3"}, + {"RECMIX1L", "BST2 Switch", "BST2"}, + {"RECMIX1L", "BST1 Switch", "BST1"}, + {"RECMIX1L", NULL, "RECMIX1L Power"}, + + {"RECMIX1R", "MONOVOL Switch", "MONOVOL"}, + {"RECMIX1R", "INR Switch", "INR VOL"}, + {"RECMIX1R", "BST4 Switch", "BST4"}, + {"RECMIX1R", "BST3 Switch", "BST3"}, + {"RECMIX1R", "BST2 Switch", "BST2"}, + {"RECMIX1R", "BST1 Switch", "BST1"}, + {"RECMIX1R", NULL, "RECMIX1R Power"}, + + {"RECMIX2L", "CBJ Switch", "BST1 CBJ"}, + {"RECMIX2L", "INL Switch", "INL VOL"}, + {"RECMIX2L", "INR Switch", "INR VOL"}, + {"RECMIX2L", "BST4 Switch", "BST4"}, + {"RECMIX2L", "BST3 Switch", "BST3"}, + {"RECMIX2L", "BST2 Switch", "BST2"}, + {"RECMIX2L", "BST1 Switch", "BST1"}, + {"RECMIX2L", NULL, "RECMIX2L Power"}, + + {"RECMIX2R", "MONOVOL Switch", "MONOVOL"}, + {"RECMIX2R", "INL Switch", "INL VOL"}, + {"RECMIX2R", "INR Switch", "INR VOL"}, + {"RECMIX2R", "BST4 Switch", "BST4"}, + {"RECMIX2R", "BST3 Switch", "BST3"}, + {"RECMIX2R", "BST2 Switch", "BST2"}, + {"RECMIX2R", "BST1 Switch", "BST1"}, + {"RECMIX2R", NULL, "RECMIX2R Power"}, + + {"ADC1 L", NULL, "RECMIX1L"}, + {"ADC1 L", NULL, "ADC1 L Power"}, + {"ADC1 L", NULL, "ADC1 clock"}, + {"ADC1 R", NULL, "RECMIX1R"}, + {"ADC1 R", NULL, "ADC1 R Power"}, + {"ADC1 R", NULL, "ADC1 clock"}, + + {"ADC2 L", NULL, "RECMIX2L"}, + {"ADC2 L", NULL, "ADC2 L Power"}, + {"ADC2 L", NULL, "ADC2 clock"}, + {"ADC2 R", NULL, "RECMIX2R"}, + {"ADC2 R", NULL, "ADC2 R Power"}, + {"ADC2 R", NULL, "ADC2 clock"}, + + {"DMIC L1", NULL, "DMIC CLK"}, + {"DMIC L1", NULL, "DMIC1 Power"}, + {"DMIC R1", NULL, "DMIC CLK"}, + {"DMIC R1", NULL, "DMIC1 Power"}, + {"DMIC L2", NULL, "DMIC CLK"}, + {"DMIC L2", NULL, "DMIC2 Power"}, + {"DMIC R2", NULL, "DMIC CLK"}, + {"DMIC R2", NULL, "DMIC2 Power"}, + + {"Stereo1 DMIC L Mux", "DMIC1", "DMIC L1"}, + {"Stereo1 DMIC L Mux", "DMIC2", "DMIC L2"}, + + {"Stereo1 DMIC R Mux", "DMIC1", "DMIC R1"}, + {"Stereo1 DMIC R Mux", "DMIC2", "DMIC R2"}, + + {"Mono DMIC L Mux", "DMIC1 L", "DMIC L1"}, + {"Mono DMIC L Mux", "DMIC2 L", "DMIC L2"}, + + {"Mono DMIC R Mux", "DMIC1 R", "DMIC R1"}, + {"Mono DMIC R Mux", "DMIC2 R", "DMIC R2"}, + + {"Stereo2 DMIC L Mux", "DMIC1", "DMIC L1"}, + {"Stereo2 DMIC L Mux", "DMIC2", "DMIC L2"}, + + {"Stereo2 DMIC R Mux", "DMIC1", "DMIC R1"}, + {"Stereo2 DMIC R Mux", "DMIC2", "DMIC R2"}, + + {"Stereo1 ADC L Mux", "ADC1 L", "ADC1 L"}, + {"Stereo1 ADC L Mux", "ADC1 R", "ADC1 R"}, + {"Stereo1 ADC L Mux", "ADC2 L", "ADC2 L"}, + {"Stereo1 ADC L Mux", "ADC2 R", "ADC2 R"}, + {"Stereo1 ADC R Mux", "ADC1 L", "ADC1 L"}, + {"Stereo1 ADC R Mux", "ADC1 R", "ADC1 R"}, + {"Stereo1 ADC R Mux", "ADC2 L", "ADC2 L"}, + {"Stereo1 ADC R Mux", "ADC2 R", "ADC2 R"}, + + {"Stereo1 DD L Mux", "STO2 DAC", "Stereo2 DAC MIXL"}, + {"Stereo1 DD L Mux", "MONO DAC", "Mono DAC MIXL"}, + + {"Stereo1 DD R Mux", "STO2 DAC", "Stereo2 DAC MIXR"}, + {"Stereo1 DD R Mux", "MONO DAC", "Mono DAC MIXR"}, + + {"Stereo1 ADC L1 Mux", "ADC", "Stereo1 ADC L Mux"}, + {"Stereo1 ADC L1 Mux", "DD Mux", "Stereo1 DD L Mux"}, + {"Stereo1 ADC L2 Mux", "DMIC", "Stereo1 DMIC L Mux"}, + {"Stereo1 ADC L2 Mux", "DAC MIX", "DAC MIXL"}, + + {"Stereo1 ADC R1 Mux", "ADC", "Stereo1 ADC R Mux"}, + {"Stereo1 ADC R1 Mux", "DD Mux", "Stereo1 DD R Mux"}, + {"Stereo1 ADC R2 Mux", "DMIC", "Stereo1 DMIC R Mux"}, + {"Stereo1 ADC R2 Mux", "DAC MIX", "DAC MIXR"}, + + {"Mono ADC L Mux", "ADC1 L", "ADC1 L"}, + {"Mono ADC L Mux", "ADC1 R", "ADC1 R"}, + {"Mono ADC L Mux", "ADC2 L", "ADC2 L"}, + {"Mono ADC L Mux", "ADC2 R", "ADC2 R"}, + + {"Mono ADC R Mux", "ADC1 L", "ADC1 L"}, + {"Mono ADC R Mux", "ADC1 R", "ADC1 R"}, + {"Mono ADC R Mux", "ADC2 L", "ADC2 L"}, + {"Mono ADC R Mux", "ADC2 R", "ADC2 R"}, + + {"Mono DD L Mux", "STO2 DAC", "Stereo2 DAC MIXL"}, + {"Mono DD L Mux", "MONO DAC", "Mono DAC MIXL"}, + + {"Mono DD R Mux", "STO2 DAC", "Stereo2 DAC MIXR"}, + {"Mono DD R Mux", "MONO DAC", "Mono DAC MIXR"}, + + {"Mono ADC L2 Mux", "DMIC", "Mono DMIC L Mux"}, + {"Mono ADC L2 Mux", "DAC MIXL", "DAC MIXL"}, + {"Mono ADC L1 Mux", "DD Mux", "Mono DD L Mux"}, + {"Mono ADC L1 Mux", "ADC", "Mono ADC L Mux"}, + + {"Mono ADC R1 Mux", "DD Mux", "Mono DD R Mux"}, + {"Mono ADC R1 Mux", "ADC", "Mono ADC R Mux"}, + {"Mono ADC R2 Mux", "DMIC", "Mono DMIC R Mux"}, + {"Mono ADC R2 Mux", "DAC MIXR", "DAC MIXR"}, + + {"Stereo2 ADC L Mux", "ADC1 L", "ADC1 L"}, + {"Stereo2 ADC L Mux", "ADC2 L", "ADC2 L"}, + {"Stereo2 ADC L Mux", "ADC1 R", "ADC1 R"}, + {"Stereo2 ADC R Mux", "ADC1 L", "ADC1 L"}, + {"Stereo2 ADC R Mux", "ADC2 L", "ADC2 L"}, + {"Stereo2 ADC R Mux", "ADC1 R", "ADC1 R"}, + + {"Stereo2 DD L Mux", "STO2 DAC", "Stereo2 DAC MIXL"}, + {"Stereo2 DD L Mux", "MONO DAC", "Mono DAC MIXL"}, + + {"Stereo2 DD R Mux", "STO2 DAC", "Stereo2 DAC MIXR"}, + {"Stereo2 DD R Mux", "MONO DAC", "Mono DAC MIXR"}, + + {"Stereo2 ADC L1 Mux", "ADC", "Stereo2 ADC L Mux"}, + {"Stereo2 ADC L1 Mux", "DD Mux", "Stereo2 DD L Mux"}, + {"Stereo2 ADC L2 Mux", "DMIC", "Stereo2 DMIC L Mux"}, + {"Stereo2 ADC L2 Mux", "DAC MIX", "DAC MIXL"}, + + {"Stereo2 ADC R1 Mux", "ADC", "Stereo2 ADC R Mux"}, + {"Stereo2 ADC R1 Mux", "DD Mux", "Stereo2 DD R Mux"}, + {"Stereo2 ADC R2 Mux", "DMIC", "Stereo2 DMIC R Mux"}, + {"Stereo2 ADC R2 Mux", "DAC MIX", "DAC MIXR"}, + + {"Stereo1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux"}, + {"Stereo1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux"}, + {"Stereo1 ADC MIXL", NULL, "ADC Stereo1 Filter"}, + + {"Stereo1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux"}, + {"Stereo1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux"}, + {"Stereo1 ADC MIXR", NULL, "ADC Stereo1 Filter"}, + + {"Mono ADC MIXL", "ADC1 Switch", "Mono ADC L1 Mux"}, + {"Mono ADC MIXL", "ADC2 Switch", "Mono ADC L2 Mux"}, + {"Mono ADC MIXL", NULL, "ADC Mono Left Filter"}, + + {"Mono ADC MIXR", "ADC1 Switch", "Mono ADC R1 Mux"}, + {"Mono ADC MIXR", "ADC2 Switch", "Mono ADC R2 Mux"}, + {"Mono ADC MIXR", NULL, "ADC Mono Right Filter"}, + + {"Stereo2 ADC MIXL", "ADC1 Switch", "Stereo2 ADC L1 Mux"}, + {"Stereo2 ADC MIXL", "ADC2 Switch", "Stereo2 ADC L2 Mux"}, + {"Stereo2 ADC MIXL", NULL, "ADC Stereo2 Filter"}, + + {"Stereo2 ADC MIXR", "ADC1 Switch", "Stereo2 ADC R1 Mux"}, + {"Stereo2 ADC MIXR", "ADC2 Switch", "Stereo2 ADC R2 Mux"}, + {"Stereo2 ADC MIXR", NULL, "ADC Stereo2 Filter"}, + + {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL"}, + {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR"}, + {"Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXL"}, + {"Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXR"}, + {"Mono ADC MIX", NULL, "Mono ADC MIXL"}, + {"Mono ADC MIX", NULL, "Mono ADC MIXR"}, + + {"IF1_1_ADC1 Mux", "STO1 ADC", "Stereo1 ADC MIX"}, + {"IF1_1_ADC1 Mux", "IF2_1 DAC", "IF2_1 DAC"}, + {"IF1_1_ADC2 Mux", "STO2 ADC", "Stereo2 ADC MIX"}, + {"IF1_1_ADC2 Mux", "IF2_2 DAC", "IF2_2 DAC"}, + {"IF1_1_ADC3 Mux", "MONO ADC", "Mono ADC MIX"}, + {"IF1_1_ADC3 Mux", "IF3 DAC", "IF3 DAC"}, + {"IF1_1_ADC4", NULL, "DAC1 MIX"}, + + {"IF1_2_ADC1 Mux", "STO1 ADC", "Stereo1 ADC MIX"}, + {"IF1_2_ADC1 Mux", "IF1 DAC", "IF1 DAC1"}, + {"IF1_2_ADC2 Mux", "STO2 ADC", "Stereo2 ADC MIX"}, + {"IF1_2_ADC2 Mux", "IF2_1 DAC", "IF2_1 DAC"}, + {"IF1_2_ADC3 Mux", "MONO ADC", "Mono ADC MIX"}, + {"IF1_2_ADC3 Mux", "IF2_2 DAC", "IF2_2 DAC"}, + {"IF1_2_ADC4 Mux", "DAC1", "DAC1 MIX"}, + {"IF1_2_ADC4 Mux", "IF3 DAC", "IF3 DAC"}, + + {"TDM1 slot 01 Data Mux", "1234", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 01 Data Mux", "1243", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 01 Data Mux", "1324", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 01 Data Mux", "1342", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 01 Data Mux", "1432", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 01 Data Mux", "1423", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 01 Data Mux", "2134", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 01 Data Mux", "2143", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 01 Data Mux", "2314", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 01 Data Mux", "2341", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 01 Data Mux", "2431", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 01 Data Mux", "2413", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 01 Data Mux", "3124", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 01 Data Mux", "3142", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 01 Data Mux", "3214", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 01 Data Mux", "3241", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 01 Data Mux", "3412", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 01 Data Mux", "3421", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 01 Data Mux", "4123", "IF1_1_ADC4"}, + {"TDM1 slot 01 Data Mux", "4132", "IF1_1_ADC4"}, + {"TDM1 slot 01 Data Mux", "4213", "IF1_1_ADC4"}, + {"TDM1 slot 01 Data Mux", "4231", "IF1_1_ADC4"}, + {"TDM1 slot 01 Data Mux", "4312", "IF1_1_ADC4"}, + {"TDM1 slot 01 Data Mux", "4321", "IF1_1_ADC4"}, + {"TDM1 slot 01 Data Mux", NULL, "I2S1_1"}, + + {"TDM1 slot 23 Data Mux", "1234", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 23 Data Mux", "1243", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 23 Data Mux", "1324", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 23 Data Mux", "1342", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 23 Data Mux", "1432", "IF1_1_ADC4"}, + {"TDM1 slot 23 Data Mux", "1423", "IF1_1_ADC4"}, + {"TDM1 slot 23 Data Mux", "2134", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 23 Data Mux", "2143", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 23 Data Mux", "2314", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 23 Data Mux", "2341", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 23 Data Mux", "2431", "IF1_1_ADC4"}, + {"TDM1 slot 23 Data Mux", "2413", "IF1_1_ADC4"}, + {"TDM1 slot 23 Data Mux", "3124", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 23 Data Mux", "3142", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 23 Data Mux", "3214", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 23 Data Mux", "3241", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 23 Data Mux", "3412", "IF1_1_ADC4"}, + {"TDM1 slot 23 Data Mux", "3421", "IF1_1_ADC4"}, + {"TDM1 slot 23 Data Mux", "4123", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 23 Data Mux", "4132", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 23 Data Mux", "4213", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 23 Data Mux", "4231", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 23 Data Mux", "4312", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 23 Data Mux", "4321", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 23 Data Mux", NULL, "I2S1_1"}, + + {"TDM1 slot 45 Data Mux", "1234", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 45 Data Mux", "1243", "IF1_1_ADC4"}, + {"TDM1 slot 45 Data Mux", "1324", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 45 Data Mux", "1342", "IF1_1_ADC4"}, + {"TDM1 slot 45 Data Mux", "1432", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 45 Data Mux", "1423", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 45 Data Mux", "2134", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 45 Data Mux", "2143", "IF1_1_ADC4"}, + {"TDM1 slot 45 Data Mux", "2314", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 45 Data Mux", "2341", "IF1_1_ADC4"}, + {"TDM1 slot 45 Data Mux", "2431", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 45 Data Mux", "2413", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 45 Data Mux", "3124", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 45 Data Mux", "3142", "IF1_1_ADC4"}, + {"TDM1 slot 45 Data Mux", "3214", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 45 Data Mux", "3241", "IF1_1_ADC4"}, + {"TDM1 slot 45 Data Mux", "3412", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 45 Data Mux", "3421", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 45 Data Mux", "4123", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 45 Data Mux", "4132", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 45 Data Mux", "4213", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 45 Data Mux", "4231", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 45 Data Mux", "4312", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 45 Data Mux", "4321", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 45 Data Mux", NULL, "I2S1_1"}, + + {"TDM1 slot 67 Data Mux", "1234", "IF1_1_ADC4"}, + {"TDM1 slot 67 Data Mux", "1243", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 67 Data Mux", "1324", "IF1_1_ADC4"}, + {"TDM1 slot 67 Data Mux", "1342", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 67 Data Mux", "1432", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 67 Data Mux", "1423", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 67 Data Mux", "2134", "IF1_1_ADC4"}, + {"TDM1 slot 67 Data Mux", "2143", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 67 Data Mux", "2314", "IF1_1_ADC4"}, + {"TDM1 slot 67 Data Mux", "2341", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 67 Data Mux", "2431", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 67 Data Mux", "2413", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 67 Data Mux", "3124", "IF1_1_ADC4"}, + {"TDM1 slot 67 Data Mux", "3142", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 67 Data Mux", "3214", "IF1_1_ADC4"}, + {"TDM1 slot 67 Data Mux", "3241", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 67 Data Mux", "3412", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 67 Data Mux", "3421", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 67 Data Mux", "4123", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 67 Data Mux", "4132", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 67 Data Mux", "4213", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 67 Data Mux", "4231", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 67 Data Mux", "4312", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 67 Data Mux", "4321", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 67 Data Mux", NULL, "I2S1_1"}, + + + {"TDM2 slot 01 Data Mux", "1234", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 01 Data Mux", "1243", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 01 Data Mux", "1324", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 01 Data Mux", "1342", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 01 Data Mux", "1432", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 01 Data Mux", "1423", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 01 Data Mux", "2134", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 01 Data Mux", "2143", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 01 Data Mux", "2314", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 01 Data Mux", "2341", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 01 Data Mux", "2431", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 01 Data Mux", "2413", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 01 Data Mux", "3124", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 01 Data Mux", "3142", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 01 Data Mux", "3214", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 01 Data Mux", "3241", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 01 Data Mux", "3412", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 01 Data Mux", "3421", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 01 Data Mux", "4123", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 01 Data Mux", "4132", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 01 Data Mux", "4213", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 01 Data Mux", "4231", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 01 Data Mux", "4312", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 01 Data Mux", "4321", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 01 Data Mux", NULL, "I2S1_2"}, + + {"TDM2 slot 23 Data Mux", "1234", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 23 Data Mux", "1243", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 23 Data Mux", "1324", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 23 Data Mux", "1342", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 23 Data Mux", "1432", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 23 Data Mux", "1423", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 23 Data Mux", "2134", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 23 Data Mux", "2143", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 23 Data Mux", "2314", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 23 Data Mux", "2341", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 23 Data Mux", "2431", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 23 Data Mux", "2413", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 23 Data Mux", "3124", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 23 Data Mux", "3142", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 23 Data Mux", "3214", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 23 Data Mux", "3241", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 23 Data Mux", "3412", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 23 Data Mux", "3421", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 23 Data Mux", "4123", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 23 Data Mux", "4132", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 23 Data Mux", "4213", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 23 Data Mux", "4231", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 23 Data Mux", "4312", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 23 Data Mux", "4321", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 23 Data Mux", NULL, "I2S1_2"}, + + {"TDM2 slot 45 Data Mux", "1234", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 45 Data Mux", "1243", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 45 Data Mux", "1324", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 45 Data Mux", "1342", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 45 Data Mux", "1432", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 45 Data Mux", "1423", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 45 Data Mux", "2134", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 45 Data Mux", "2143", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 45 Data Mux", "2314", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 45 Data Mux", "2341", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 45 Data Mux", "2431", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 45 Data Mux", "2413", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 45 Data Mux", "3124", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 45 Data Mux", "3142", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 45 Data Mux", "3214", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 45 Data Mux", "3241", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 45 Data Mux", "3412", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 45 Data Mux", "3421", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 45 Data Mux", "4123", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 45 Data Mux", "4132", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 45 Data Mux", "4213", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 45 Data Mux", "4231", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 45 Data Mux", "4312", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 45 Data Mux", "4321", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 45 Data Mux", NULL, "I2S1_2"}, + + {"TDM2 slot 67 Data Mux", "1234", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 67 Data Mux", "1243", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 67 Data Mux", "1324", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 67 Data Mux", "1342", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 67 Data Mux", "1432", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 67 Data Mux", "1423", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 67 Data Mux", "2134", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 67 Data Mux", "2143", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 67 Data Mux", "2314", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 67 Data Mux", "2341", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 67 Data Mux", "2431", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 67 Data Mux", "2413", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 67 Data Mux", "3124", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 67 Data Mux", "3142", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 67 Data Mux", "3214", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 67 Data Mux", "3241", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 67 Data Mux", "3412", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 67 Data Mux", "3421", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 67 Data Mux", "4123", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 67 Data Mux", "4132", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 67 Data Mux", "4213", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 67 Data Mux", "4231", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 67 Data Mux", "4312", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 67 Data Mux", "4321", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 67 Data Mux", NULL, "I2S1_2"}, + + {"IF1_1 0 ADC Swap Mux", "L/R", "TDM1 slot 01 Data Mux"}, + {"IF1_1 0 ADC Swap Mux", "L/L", "TDM1 slot 01 Data Mux"}, + {"IF1_1 1 ADC Swap Mux", "R/L", "TDM1 slot 01 Data Mux"}, + {"IF1_1 1 ADC Swap Mux", "R/R", "TDM1 slot 01 Data Mux"}, + {"IF1_1 2 ADC Swap Mux", "L/R", "TDM1 slot 23 Data Mux"}, + {"IF1_1 2 ADC Swap Mux", "R/L", "TDM1 slot 23 Data Mux"}, + {"IF1_1 3 ADC Swap Mux", "L/L", "TDM1 slot 23 Data Mux"}, + {"IF1_1 3 ADC Swap Mux", "R/R", "TDM1 slot 23 Data Mux"}, + {"IF1_1 4 ADC Swap Mux", "L/R", "TDM1 slot 45 Data Mux"}, + {"IF1_1 4 ADC Swap Mux", "R/L", "TDM1 slot 45 Data Mux"}, + {"IF1_1 5 ADC Swap Mux", "L/L", "TDM1 slot 45 Data Mux"}, + {"IF1_1 5 ADC Swap Mux", "R/R", "TDM1 slot 45 Data Mux"}, + {"IF1_1 6 ADC Swap Mux", "L/R", "TDM1 slot 67 Data Mux"}, + {"IF1_1 6 ADC Swap Mux", "R/L", "TDM1 slot 67 Data Mux"}, + {"IF1_1 7 ADC Swap Mux", "L/L", "TDM1 slot 67 Data Mux"}, + {"IF1_1 7 ADC Swap Mux", "R/R", "TDM1 slot 67 Data Mux"}, + {"IF1_2 0 ADC Swap Mux", "L/R", "TDM2 slot 01 Data Mux"}, + {"IF1_2 0 ADC Swap Mux", "R/L", "TDM2 slot 01 Data Mux"}, + {"IF1_2 1 ADC Swap Mux", "L/L", "TDM2 slot 01 Data Mux"}, + {"IF1_2 1 ADC Swap Mux", "R/R", "TDM2 slot 01 Data Mux"}, + {"IF1_2 2 ADC Swap Mux", "L/R", "TDM2 slot 23 Data Mux"}, + {"IF1_2 2 ADC Swap Mux", "R/L", "TDM2 slot 23 Data Mux"}, + {"IF1_2 3 ADC Swap Mux", "L/L", "TDM2 slot 23 Data Mux"}, + {"IF1_2 3 ADC Swap Mux", "R/R", "TDM2 slot 23 Data Mux"}, + {"IF1_2 4 ADC Swap Mux", "L/R", "TDM2 slot 45 Data Mux"}, + {"IF1_2 4 ADC Swap Mux", "R/L", "TDM2 slot 45 Data Mux"}, + {"IF1_2 5 ADC Swap Mux", "L/L", "TDM2 slot 45 Data Mux"}, + {"IF1_2 5 ADC Swap Mux", "R/R", "TDM2 slot 45 Data Mux"}, + {"IF1_2 6 ADC Swap Mux", "L/R", "TDM2 slot 67 Data Mux"}, + {"IF1_2 6 ADC Swap Mux", "R/L", "TDM2 slot 67 Data Mux"}, + {"IF1_2 7 ADC Swap Mux", "L/L", "TDM2 slot 67 Data Mux"}, + {"IF1_2 7 ADC Swap Mux", "R/R", "TDM2 slot 67 Data Mux"}, + + {"IF2_1 ADC Mux", "STO1 ADC", "Stereo1 ADC MIX"}, + {"IF2_1 ADC Mux", "STO2 ADC", "Stereo2 ADC MIX"}, + {"IF2_1 ADC Mux", "MONO ADC", "Mono ADC MIX"}, + {"IF2_1 ADC Mux", "IF1 DAC1", "IF1 DAC1"}, + {"IF2_1 ADC Mux", "IF1 DAC2", "IF1 DAC2"}, + {"IF2_1 ADC Mux", "IF2_2 DAC", "IF2_2 DAC"}, + {"IF2_1 ADC Mux", "IF3 DAC", "IF3 DAC"}, + {"IF2_1 ADC Mux", "DAC1 MIX", "DAC1 MIX"}, + {"IF2_1 ADC", NULL, "IF2_1 ADC Mux"}, + {"IF2_1 ADC", NULL, "I2S2_1"}, + + {"IF2_2 ADC Mux", "STO1 ADC", "Stereo1 ADC MIX"}, + {"IF2_2 ADC Mux", "STO2 ADC", "Stereo2 ADC MIX"}, + {"IF2_2 ADC Mux", "MONO ADC", "Mono ADC MIX"}, + {"IF2_2 ADC Mux", "IF1 DAC1", "IF1 DAC1"}, + {"IF2_2 ADC Mux", "IF1 DAC2", "IF1 DAC2"}, + {"IF2_2 ADC Mux", "IF2_1 DAC", "IF2_1 DAC"}, + {"IF2_2 ADC Mux", "IF3 DAC", "IF3 DAC"}, + {"IF2_2 ADC Mux", "DAC1 MIX", "DAC1 MIX"}, + {"IF2_2 ADC", NULL, "IF2_2 ADC Mux"}, + {"IF2_2 ADC", NULL, "I2S2_2"}, + + {"IF3 ADC Mux", "STO1 ADC", "Stereo1 ADC MIX"}, + {"IF3 ADC Mux", "STO2 ADC", "Stereo2 ADC MIX"}, + {"IF3 ADC Mux", "MONO ADC", "Mono ADC MIX"}, + {"IF3 ADC Mux", "IF1 DAC1", "IF1 DAC1"}, + {"IF3 ADC Mux", "IF1 DAC2", "IF1 DAC2"}, + {"IF3 ADC Mux", "IF2_1 DAC", "IF2_1 DAC"}, + {"IF3 ADC Mux", "IF2_2 DAC", "IF2_2 DAC"}, + {"IF3 ADC Mux", "DAC1 MIX", "DAC1 MIX"}, + {"IF3 ADC", NULL, "IF3 ADC Mux"}, + {"IF3 ADC", NULL, "I2S3"}, + + {"AIF1_1TX slot 0", NULL, "IF1_1 0 ADC Swap Mux"}, + {"AIF1_1TX slot 1", NULL, "IF1_1 1 ADC Swap Mux"}, + {"AIF1_1TX slot 2", NULL, "IF1_1 2 ADC Swap Mux"}, + {"AIF1_1TX slot 3", NULL, "IF1_1 3 ADC Swap Mux"}, + {"AIF1_1TX slot 4", NULL, "IF1_1 4 ADC Swap Mux"}, + {"AIF1_1TX slot 5", NULL, "IF1_1 5 ADC Swap Mux"}, + {"AIF1_1TX slot 6", NULL, "IF1_1 6 ADC Swap Mux"}, + {"AIF1_1TX slot 7", NULL, "IF1_1 7 ADC Swap Mux"}, + {"AIF1_2TX slot 0", NULL, "IF1_2 0 ADC Swap Mux"}, + {"AIF1_2TX slot 1", NULL, "IF1_2 1 ADC Swap Mux"}, + {"AIF1_2TX slot 2", NULL, "IF1_2 2 ADC Swap Mux"}, + {"AIF1_2TX slot 3", NULL, "IF1_2 3 ADC Swap Mux"}, + {"AIF1_2TX slot 4", NULL, "IF1_2 4 ADC Swap Mux"}, + {"AIF1_2TX slot 5", NULL, "IF1_2 5 ADC Swap Mux"}, + {"AIF1_2TX slot 6", NULL, "IF1_2 6 ADC Swap Mux"}, + {"AIF1_2TX slot 7", NULL, "IF1_2 7 ADC Swap Mux"}, + {"IF2_1 ADC Swap Mux", "L/R", "IF2_1 ADC"}, + {"IF2_1 ADC Swap Mux", "R/L", "IF2_1 ADC"}, + {"IF2_1 ADC Swap Mux", "L/L", "IF2_1 ADC"}, + {"IF2_1 ADC Swap Mux", "R/R", "IF2_1 ADC"}, + {"AIF2_1TX", NULL, "IF2_1 ADC Swap Mux"}, + {"IF2_2 ADC Swap Mux", "L/R", "IF2_2 ADC"}, + {"IF2_2 ADC Swap Mux", "R/L", "IF2_2 ADC"}, + {"IF2_2 ADC Swap Mux", "L/L", "IF2_2 ADC"}, + {"IF2_2 ADC Swap Mux", "R/R", "IF2_2 ADC"}, + {"AIF2_2TX", NULL, "IF2_2 ADC Swap Mux"}, + {"IF3 ADC Swap Mux", "L/R", "IF3 ADC"}, + {"IF3 ADC Swap Mux", "R/L", "IF3 ADC"}, + {"IF3 ADC Swap Mux", "L/L", "IF3 ADC"}, + {"IF3 ADC Swap Mux", "R/R", "IF3 ADC"}, + {"AIF3TX", NULL, "IF3 ADC Swap Mux"}, + + {"IF1 DAC1", NULL, "AIF1RX"}, + {"IF1 DAC2", NULL, "AIF1RX"}, + {"IF1 DAC3", NULL, "AIF1RX"}, + {"IF2_1 DAC Swap Mux", "L/R", "AIF2_1RX"}, + {"IF2_1 DAC Swap Mux", "R/L", "AIF2_1RX"}, + {"IF2_1 DAC Swap Mux", "L/L", "AIF2_1RX"}, + {"IF2_1 DAC Swap Mux", "R/R", "AIF2_1RX"}, + {"IF2_2 DAC Swap Mux", "L/R", "AIF2_2RX"}, + {"IF2_2 DAC Swap Mux", "R/L", "AIF2_2RX"}, + {"IF2_2 DAC Swap Mux", "L/L", "AIF2_2RX"}, + {"IF2_2 DAC Swap Mux", "R/R", "AIF2_2RX"}, + {"IF2_1 DAC", NULL, "IF2_1 DAC Swap Mux"}, + {"IF2_2 DAC", NULL, "IF2_2 DAC Swap Mux"}, + {"IF3 DAC Swap Mux", "L/R", "AIF3RX"}, + {"IF3 DAC Swap Mux", "R/L", "AIF3RX"}, + {"IF3 DAC Swap Mux", "L/L", "AIF3RX"}, + {"IF3 DAC Swap Mux", "R/R", "AIF3RX"}, + {"IF3 DAC", NULL, "IF3 DAC Swap Mux"}, + + {"IF1 DAC1", NULL, "I2S1_1"}, + {"IF1 DAC2", NULL, "I2S1_1"}, + {"IF1 DAC3", NULL, "I2S1_1"}, + {"IF2_1 DAC", NULL, "I2S2_1"}, + {"IF2_2 DAC", NULL, "I2S2_2"}, + {"IF3 DAC", NULL, "I2S3"}, + + {"IF1 DAC1 L", NULL, "IF1 DAC1"}, + {"IF1 DAC1 R", NULL, "IF1 DAC1"}, + {"IF1 DAC2 L", NULL, "IF1 DAC2"}, + {"IF1 DAC2 R", NULL, "IF1 DAC2"}, + {"IF1 DAC3 L", NULL, "IF1 DAC3"}, + {"IF1 DAC3 R", NULL, "IF1 DAC3"}, + {"IF2_1 DAC L", NULL, "IF2_1 DAC"}, + {"IF2_1 DAC R", NULL, "IF2_1 DAC"}, + {"IF2_2 DAC L", NULL, "IF2_2 DAC"}, + {"IF2_2 DAC R", NULL, "IF2_2 DAC"}, + {"IF3 DAC L", NULL, "IF3 DAC"}, + {"IF3 DAC R", NULL, "IF3 DAC"}, + + {"DAC L1 Mux", "IF1 DAC1", "IF1 DAC1 L"}, + {"DAC L1 Mux", "IF2_1 DAC", "IF2_1 DAC L"}, + {"DAC L1 Mux", "IF2_2 DAC", "IF2_2 DAC L"}, + {"DAC L1 Mux", "IF3 DAC", "IF3 DAC L"}, + {"DAC L1 Mux", NULL, "DAC Stereo1 Filter"}, + + {"DAC R1 Mux", "IF1 DAC1", "IF1 DAC1 R"}, + {"DAC R1 Mux", "IF2_1 DAC", "IF2_1 DAC R"}, + {"DAC R1 Mux", "IF2_2 DAC", "IF2_2 DAC R"}, + {"DAC R1 Mux", "IF3 DAC", "IF3 DAC R"}, + {"DAC R1 Mux", NULL, "DAC Stereo1 Filter"}, + + {"DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL"}, + {"DAC1 MIXL", "DAC1 Switch", "DAC L1 Mux"}, + {"DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR"}, + {"DAC1 MIXR", "DAC1 Switch", "DAC R1 Mux"}, + + {"DAC1 MIX", NULL, "DAC1 MIXL"}, + {"DAC1 MIX", NULL, "DAC1 MIXR"}, + + {"DAC L2 Mux", "IF1 DAC2", "IF1 DAC2 L"}, + {"DAC L2 Mux", "IF2_1 DAC", "IF2_1 DAC L"}, + {"DAC L2 Mux", "IF2_2 DAC", "IF2_2 DAC L"}, + {"DAC L2 Mux", "IF3 DAC", "IF3 DAC L"}, + {"DAC L2 Mux", "Mono ADC MIX", "Mono ADC MIXL"}, + {"DAC L2 Mux", NULL, "DAC Mono Left Filter"}, + + {"DAC R2 Mux", "IF1 DAC2", "IF1 DAC2 R"}, + {"DAC R2 Mux", "IF2_1 DAC", "IF2_1 DAC R"}, + {"DAC R2 Mux", "IF2_2 DAC", "IF2_2 DAC R"}, + {"DAC R2 Mux", "IF3 DAC", "IF3 DAC R"}, + {"DAC R2 Mux", "Mono ADC MIX", "Mono ADC MIXR"}, + {"DAC R2 Mux", NULL, "DAC Mono Right Filter"}, + + {"DAC L3 Mux", "IF1 DAC2", "IF1 DAC2 L"}, + {"DAC L3 Mux", "IF2_1 DAC", "IF2_1 DAC L"}, + {"DAC L3 Mux", "IF2_2 DAC", "IF2_2 DAC L"}, + {"DAC L3 Mux", "IF3 DAC", "IF3 DAC L"}, + {"DAC L3 Mux", "STO2 ADC MIX", "Stereo2 ADC MIXL"}, + {"DAC L3 Mux", NULL, "DAC Stereo2 Filter"}, + + {"DAC R3 Mux", "IF1 DAC2", "IF1 DAC2 R"}, + {"DAC R3 Mux", "IF2_1 DAC", "IF2_1 DAC R"}, + {"DAC R3 Mux", "IF2_2 DAC", "IF2_2 DAC R"}, + {"DAC R3 Mux", "IF3 DAC", "IF3 DAC R"}, + {"DAC R3 Mux", "STO2 ADC MIX", "Stereo2 ADC MIXR"}, + {"DAC R3 Mux", NULL, "DAC Stereo2 Filter"}, + + {"Stereo1 DAC MIXL", "DAC L1 Switch", "DAC1 MIXL"}, + {"Stereo1 DAC MIXL", "DAC R1 Switch", "DAC1 MIXR"}, + {"Stereo1 DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"}, + {"Stereo1 DAC MIXL", "DAC R2 Switch", "DAC R2 Mux"}, + + {"Stereo1 DAC MIXR", "DAC R1 Switch", "DAC1 MIXR"}, + {"Stereo1 DAC MIXR", "DAC L1 Switch", "DAC1 MIXL"}, + {"Stereo1 DAC MIXR", "DAC L2 Switch", "DAC L2 Mux"}, + {"Stereo1 DAC MIXR", "DAC R2 Switch", "DAC R2 Mux"}, + + {"Stereo2 DAC MIXL", "DAC L1 Switch", "DAC1 MIXL"}, + {"Stereo2 DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"}, + {"Stereo2 DAC MIXL", "DAC L3 Switch", "DAC L3 Mux"}, + + {"Stereo2 DAC MIXR", "DAC R1 Switch", "DAC1 MIXR"}, + {"Stereo2 DAC MIXR", "DAC R2 Switch", "DAC R2 Mux"}, + {"Stereo2 DAC MIXR", "DAC R3 Switch", "DAC R3 Mux"}, + + {"Mono DAC MIXL", "DAC L1 Switch", "DAC1 MIXL"}, + {"Mono DAC MIXL", "DAC R1 Switch", "DAC1 MIXR"}, + {"Mono DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"}, + {"Mono DAC MIXL", "DAC R2 Switch", "DAC R2 Mux"}, + {"Mono DAC MIXR", "DAC L1 Switch", "DAC1 MIXL"}, + {"Mono DAC MIXR", "DAC R1 Switch", "DAC1 MIXR"}, + {"Mono DAC MIXR", "DAC L2 Switch", "DAC L2 Mux"}, + {"Mono DAC MIXR", "DAC R2 Switch", "DAC R2 Mux"}, + + {"DAC MIXL", "Stereo1 DAC Mixer", "Stereo1 DAC MIXL"}, + {"DAC MIXL", "Stereo2 DAC Mixer", "Stereo2 DAC MIXL"}, + {"DAC MIXL", "Mono DAC Mixer", "Mono DAC MIXL"}, + {"DAC MIXR", "Stereo1 DAC Mixer", "Stereo1 DAC MIXR"}, + {"DAC MIXR", "Stereo2 DAC Mixer", "Stereo2 DAC MIXR"}, + {"DAC MIXR", "Mono DAC Mixer", "Mono DAC MIXR"}, + + {"DAC L1 Source", "DAC1", "DAC1 MIXL"}, + {"DAC L1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXL"}, + {"DAC L1 Source", "DMIC1", "DMIC L1"}, + {"DAC R1 Source", "DAC1", "DAC1 MIXR"}, + {"DAC R1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXR"}, + {"DAC R1 Source", "DMIC1", "DMIC R1"}, + + {"DAC L2 Source", "DAC2", "DAC L2 Mux"}, + {"DAC L2 Source", "Mono DAC Mixer", "Mono DAC MIXL"}, + {"DAC L2 Source", NULL, "DAC L2 Power"}, + {"DAC R2 Source", "DAC2", "DAC R2 Mux"}, + {"DAC R2 Source", "Mono DAC Mixer", "Mono DAC MIXR"}, + {"DAC R2 Source", NULL, "DAC R2 Power"}, + + {"DAC L1", NULL, "DAC L1 Source"}, + {"DAC R1", NULL, "DAC R1 Source"}, + {"DAC L2", NULL, "DAC L2 Source"}, + {"DAC R2", NULL, "DAC R2 Source"}, + + {"DAC L1", NULL, "DAC 1 Clock"}, + {"DAC R1", NULL, "DAC 1 Clock"}, + {"DAC L2", NULL, "DAC 2 Clock"}, + {"DAC R2", NULL, "DAC 2 Clock"}, + + {"MONOVOL MIX", "DAC L2 Switch", "DAC L2"}, + {"MONOVOL MIX", "RECMIX2L Switch", "RECMIX2L"}, + {"MONOVOL MIX", "BST1 Switch", "BST1"}, + {"MONOVOL MIX", "BST2 Switch", "BST2"}, + {"MONOVOL MIX", "BST3 Switch", "BST3"}, + + {"OUT MIXL", "DAC L2 Switch", "DAC L2"}, + {"OUT MIXL", "INL Switch", "INL VOL"}, + {"OUT MIXL", "BST1 Switch", "BST1"}, + {"OUT MIXL", "BST2 Switch", "BST2"}, + {"OUT MIXL", "BST3 Switch", "BST3"}, + {"OUT MIXR", "DAC R2 Switch", "DAC R2"}, + {"OUT MIXR", "INR Switch", "INR VOL"}, + {"OUT MIXR", "BST2 Switch", "BST2"}, + {"OUT MIXR", "BST3 Switch", "BST3"}, + {"OUT MIXR", "BST4 Switch", "BST4"}, + + {"MONOVOL", "Switch", "MONOVOL MIX"}, + {"Mono MIX", "DAC L2 Switch", "DAC L2"}, + {"Mono MIX", "MONOVOL Switch", "MONOVOL"}, + {"Mono Amp", NULL, "Mono MIX"}, + {"Mono Amp", NULL, "Vref2"}, + {"Mono Amp", NULL, "CLKDET SYS"}, + {"Mono Amp", NULL, "CLKDET MONO"}, + {"Mono Playback", "Switch", "Mono Amp"}, + {"MONOOUT", NULL, "Mono Playback"}, + + {"HP Amp", NULL, "DAC L1"}, + {"HP Amp", NULL, "DAC R1"}, + {"HP Amp", NULL, "Charge Pump"}, + {"HP Amp", NULL, "CLKDET SYS"}, + {"HP Amp", NULL, "CLKDET HP"}, + {"HP Amp", NULL, "CBJ Power"}, + {"HP Amp", NULL, "Vref2"}, + {"HPO Playback", "Switch", "HP Amp"}, + {"HPOL", NULL, "HPO Playback"}, + {"HPOR", NULL, "HPO Playback"}, + + {"OUTVOL L", "Switch", "OUT MIXL"}, + {"OUTVOL R", "Switch", "OUT MIXR"}, + {"LOUT L MIX", "DAC L2 Switch", "DAC L2"}, + {"LOUT L MIX", "OUTVOL L Switch", "OUTVOL L"}, + {"LOUT R MIX", "DAC R2 Switch", "DAC R2"}, + {"LOUT R MIX", "OUTVOL R Switch", "OUTVOL R"}, + {"LOUT Amp", NULL, "LOUT L MIX"}, + {"LOUT Amp", NULL, "LOUT R MIX"}, + {"LOUT Amp", NULL, "Vref1"}, + {"LOUT Amp", NULL, "Vref2"}, + {"LOUT Amp", NULL, "CLKDET SYS"}, + {"LOUT Amp", NULL, "CLKDET LOUT"}, + {"LOUT L Playback", "Switch", "LOUT Amp"}, + {"LOUT R Playback", "Switch", "LOUT Amp"}, + {"LOUTL", NULL, "LOUT L Playback"}, + {"LOUTR", NULL, "LOUT R Playback"}, + + {"PDM L Mux", "Mono DAC", "Mono DAC MIXL"}, + {"PDM L Mux", "Stereo1 DAC", "Stereo1 DAC MIXL"}, + {"PDM L Mux", "Stereo2 DAC", "Stereo2 DAC MIXL"}, + {"PDM L Mux", NULL, "PDM Power"}, + {"PDM R Mux", "Mono DAC", "Mono DAC MIXR"}, + {"PDM R Mux", "Stereo1 DAC", "Stereo1 DAC MIXR"}, + {"PDM R Mux", "Stereo2 DAC", "Stereo2 DAC MIXR"}, + {"PDM R Mux", NULL, "PDM Power"}, + {"PDM L Playback", "Switch", "PDM L Mux"}, + {"PDM R Playback", "Switch", "PDM R Mux"}, + {"PDML", NULL, "PDM L Playback"}, + {"PDMR", NULL, "PDM R Playback"}, +}; + +static int rt5665_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 rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec); + unsigned int val_len = 0, val_clk, mask_clk, val_bits = 0x0100; + int pre_div, frame_size; + + rt5665->lrck[dai->id] = params_rate(params); + pre_div = rl6231_get_clk_info(rt5665->sysclk, rt5665->lrck[dai->id]); + if (pre_div < 0) { + dev_err(codec->dev, "Unsupported clock setting %d for DAI %d\n", + rt5665->lrck[dai->id], dai->id); + return -EINVAL; + } + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size); + return -EINVAL; + } + + dev_dbg(dai->dev, "lrck is %dHz and pre_div is %d for iis %d\n", + rt5665->lrck[dai->id], pre_div, dai->id); + + switch (params_width(params)) { + case 16: + val_bits = 0x0100; + break; + case 20: + val_len |= RT5665_I2S_DL_20; + val_bits = 0x1300; + break; + case 24: + val_len |= RT5665_I2S_DL_24; + val_bits = 0x2500; + break; + case 8: + val_len |= RT5665_I2S_DL_8; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5665_AIF1_1: + case RT5665_AIF1_2: + mask_clk = RT5665_I2S_PD1_MASK; + val_clk = pre_div << RT5665_I2S_PD1_SFT; + snd_soc_update_bits(codec, RT5665_I2S1_SDP, + RT5665_I2S_DL_MASK, val_len); + break; + case RT5665_AIF2_1: + case RT5665_AIF2_2: + mask_clk = RT5665_I2S_PD2_MASK; + val_clk = pre_div << RT5665_I2S_PD2_SFT; + snd_soc_update_bits(codec, RT5665_I2S2_SDP, + RT5665_I2S_DL_MASK, val_len); + break; + case RT5665_AIF3: + mask_clk = RT5665_I2S_PD3_MASK; + val_clk = pre_div << RT5665_I2S_PD3_SFT; + snd_soc_update_bits(codec, RT5665_I2S3_SDP, + RT5665_I2S_DL_MASK, val_len); + break; + default: + dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + + snd_soc_update_bits(codec, RT5665_ADDA_CLK_1, mask_clk, val_clk); + snd_soc_update_bits(codec, RT5665_STO1_DAC_SIL_DET, 0x3700, val_bits); + + switch (rt5665->lrck[dai->id]) { + case 192000: + snd_soc_update_bits(codec, RT5665_ADDA_CLK_1, + RT5665_DAC_OSR_MASK | RT5665_ADC_OSR_MASK, + RT5665_DAC_OSR_32 | RT5665_ADC_OSR_32); + break; + case 96000: + snd_soc_update_bits(codec, RT5665_ADDA_CLK_1, + RT5665_DAC_OSR_MASK | RT5665_ADC_OSR_MASK, + RT5665_DAC_OSR_64 | RT5665_ADC_OSR_64); + break; + default: + snd_soc_update_bits(codec, RT5665_ADDA_CLK_1, + RT5665_DAC_OSR_MASK | RT5665_ADC_OSR_MASK, + RT5665_DAC_OSR_128 | RT5665_ADC_OSR_128); + break; + } + + return 0; +} + +static int rt5665_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec); + unsigned int reg_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rt5665->master[dai->id] = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + reg_val |= RT5665_I2S_MS_S; + rt5665->master[dai->id] = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT5665_I2S_BP_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT5665_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5665_I2S_DF_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5665_I2S_DF_PCM_B; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5665_AIF1_1: + case RT5665_AIF1_2: + snd_soc_update_bits(codec, RT5665_I2S1_SDP, + RT5665_I2S_MS_MASK | RT5665_I2S_BP_MASK | + RT5665_I2S_DF_MASK, reg_val); + break; + case RT5665_AIF2_1: + case RT5665_AIF2_2: + snd_soc_update_bits(codec, RT5665_I2S2_SDP, + RT5665_I2S_MS_MASK | RT5665_I2S_BP_MASK | + RT5665_I2S_DF_MASK, reg_val); + break; + case RT5665_AIF3: + snd_soc_update_bits(codec, RT5665_I2S3_SDP, + RT5665_I2S_MS_MASK | RT5665_I2S_BP_MASK | + RT5665_I2S_DF_MASK, reg_val); + break; + default: + dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + return 0; +} + +static int rt5665_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec); + unsigned int reg_val = 0; + + if (freq == rt5665->sysclk && clk_id == rt5665->sysclk_src) + return 0; + + switch (clk_id) { + case RT5665_SCLK_S_MCLK: + reg_val |= RT5665_SCLK_SRC_MCLK; + break; + case RT5665_SCLK_S_PLL1: + reg_val |= RT5665_SCLK_SRC_PLL1; + break; + case RT5665_SCLK_S_RCCLK: + reg_val |= RT5665_SCLK_SRC_RCCLK; + break; + default: + dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + snd_soc_update_bits(codec, RT5665_GLB_CLK, + RT5665_SCLK_SRC_MASK, reg_val); + rt5665->sysclk = freq; + rt5665->sysclk_src = clk_id; + + dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); + + return 0; +} + +static int rt5665_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int Source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec); + struct rl6231_pll_code pll_code; + int ret; + + if (Source == rt5665->pll_src && freq_in == rt5665->pll_in && + freq_out == rt5665->pll_out) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(codec->dev, "PLL disabled\n"); + + rt5665->pll_in = 0; + rt5665->pll_out = 0; + snd_soc_update_bits(codec, RT5665_GLB_CLK, + RT5665_SCLK_SRC_MASK, RT5665_SCLK_SRC_MCLK); + return 0; + } + + switch (Source) { + case RT5665_PLL1_S_MCLK: + snd_soc_update_bits(codec, RT5665_GLB_CLK, + RT5665_PLL1_SRC_MASK, RT5665_PLL1_SRC_MCLK); + break; + case RT5665_PLL1_S_BCLK1: + snd_soc_update_bits(codec, RT5665_GLB_CLK, + RT5665_PLL1_SRC_MASK, RT5665_PLL1_SRC_BCLK1); + break; + case RT5665_PLL1_S_BCLK2: + snd_soc_update_bits(codec, RT5665_GLB_CLK, + RT5665_PLL1_SRC_MASK, RT5665_PLL1_SRC_BCLK2); + break; + case RT5665_PLL1_S_BCLK3: + snd_soc_update_bits(codec, RT5665_GLB_CLK, + RT5665_PLL1_SRC_MASK, RT5665_PLL1_SRC_BCLK3); + break; + default: + dev_err(codec->dev, "Unknown PLL Source %d\n", Source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(codec->dev, "Unsupport input clock %d\n", freq_in); + return ret; + } + + dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + snd_soc_write(codec, RT5665_PLL_CTRL_1, + pll_code.n_code << RT5665_PLL_N_SFT | pll_code.k_code); + snd_soc_write(codec, RT5665_PLL_CTRL_2, + (pll_code.m_bp ? 0 : pll_code.m_code) << RT5665_PLL_M_SFT | + pll_code.m_bp << RT5665_PLL_M_BP_SFT); + + rt5665->pll_in = freq_in; + rt5665->pll_out = freq_out; + rt5665->pll_src = Source; + + return 0; +} + +static int rt5665_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 |= RT5665_I2S1_MODE_TDM; + + switch (slots) { + case 4: + val |= RT5665_TDM_IN_CH_4; + val |= RT5665_TDM_OUT_CH_4; + break; + case 6: + val |= RT5665_TDM_IN_CH_6; + val |= RT5665_TDM_OUT_CH_6; + break; + case 8: + val |= RT5665_TDM_IN_CH_8; + val |= RT5665_TDM_OUT_CH_8; + break; + case 2: + break; + default: + return -EINVAL; + } + + switch (slot_width) { + case 20: + val |= RT5665_TDM_IN_LEN_20; + val |= RT5665_TDM_OUT_LEN_20; + break; + case 24: + val |= RT5665_TDM_IN_LEN_24; + val |= RT5665_TDM_OUT_LEN_24; + break; + case 32: + val |= RT5665_TDM_IN_LEN_32; + val |= RT5665_TDM_OUT_LEN_32; + break; + case 16: + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, RT5665_TDM_CTRL_1, + RT5665_I2S1_MODE_MASK | RT5665_TDM_IN_CH_MASK | + RT5665_TDM_OUT_CH_MASK | RT5665_TDM_IN_LEN_MASK | + RT5665_TDM_OUT_LEN_MASK, val); + + return 0; +} + +static int rt5665_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s ratio=%d\n", __func__, ratio); + + rt5665->bclk[dai->id] = ratio; + + if (ratio == 64) { + switch (dai->id) { + case RT5665_AIF2_1: + case RT5665_AIF2_2: + snd_soc_update_bits(codec, RT5665_ADDA_CLK_1, + RT5665_I2S_BCLK_MS2_MASK, + RT5665_I2S_BCLK_MS2_64); + break; + case RT5665_AIF3: + snd_soc_update_bits(codec, RT5665_ADDA_CLK_1, + RT5665_I2S_BCLK_MS3_MASK, + RT5665_I2S_BCLK_MS3_64); + break; + } + } + + return 0; +} + +static int rt5665_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec); + + switch (level) { + case SND_SOC_BIAS_PREPARE: + regmap_update_bits(rt5665->regmap, RT5665_DIG_MISC, + RT5665_DIG_GATE_CTRL, RT5665_DIG_GATE_CTRL); + break; + + case SND_SOC_BIAS_STANDBY: + regmap_update_bits(rt5665->regmap, RT5665_PWR_DIG_1, + RT5665_PWR_LDO, RT5665_PWR_LDO); + regmap_update_bits(rt5665->regmap, RT5665_PWR_ANLG_1, + RT5665_PWR_MB, RT5665_PWR_MB); + regmap_update_bits(rt5665->regmap, RT5665_DIG_MISC, + RT5665_DIG_GATE_CTRL, 0); + break; + case SND_SOC_BIAS_OFF: + regmap_update_bits(rt5665->regmap, RT5665_PWR_DIG_1, + RT5665_PWR_LDO, 0); + regmap_update_bits(rt5665->regmap, RT5665_PWR_ANLG_1, + RT5665_PWR_MB, 0); + break; + + default: + break; + } + + return 0; +} + +static int rt5665_probe(struct snd_soc_codec *codec) +{ + struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec); + + rt5665->codec = codec; + + schedule_delayed_work(&rt5665->calibrate_work, msecs_to_jiffies(100)); + + return 0; +} + +static int rt5665_remove(struct snd_soc_codec *codec) +{ + struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec); + + regmap_write(rt5665->regmap, RT5665_RESET, 0); + + return 0; +} + +#ifdef CONFIG_PM +static int rt5665_suspend(struct snd_soc_codec *codec) +{ + struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec); + + regcache_cache_only(rt5665->regmap, true); + regcache_mark_dirty(rt5665->regmap); + return 0; +} + +static int rt5665_resume(struct snd_soc_codec *codec) +{ + struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec); + + regcache_cache_only(rt5665->regmap, false); + regcache_sync(rt5665->regmap); + + return 0; +} +#else +#define rt5665_suspend NULL +#define rt5665_resume NULL +#endif + +#define RT5665_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT5665_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt5665_aif_dai_ops = { + .hw_params = rt5665_hw_params, + .set_fmt = rt5665_set_dai_fmt, + .set_sysclk = rt5665_set_dai_sysclk, + .set_tdm_slot = rt5665_set_tdm_slot, + .set_pll = rt5665_set_dai_pll, + .set_bclk_ratio = rt5665_set_bclk_ratio, +}; + +static struct snd_soc_dai_driver rt5665_dai[] = { + { + .name = "rt5665-aif1_1", + .id = RT5665_AIF1_1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 8, + .rates = RT5665_STEREO_RATES, + .formats = RT5665_FORMATS, + }, + .capture = { + .stream_name = "AIF1_1 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = RT5665_STEREO_RATES, + .formats = RT5665_FORMATS, + }, + .ops = &rt5665_aif_dai_ops, + }, + { + .name = "rt5665-aif1_2", + .id = RT5665_AIF1_2, + .capture = { + .stream_name = "AIF1_2 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = RT5665_STEREO_RATES, + .formats = RT5665_FORMATS, + }, + .ops = &rt5665_aif_dai_ops, + }, + { + .name = "rt5665-aif2_1", + .id = RT5665_AIF2_1, + .playback = { + .stream_name = "AIF2_1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5665_STEREO_RATES, + .formats = RT5665_FORMATS, + }, + .capture = { + .stream_name = "AIF2_1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5665_STEREO_RATES, + .formats = RT5665_FORMATS, + }, + .ops = &rt5665_aif_dai_ops, + }, + { + .name = "rt5665-aif2_2", + .id = RT5665_AIF2_2, + .playback = { + .stream_name = "AIF2_2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5665_STEREO_RATES, + .formats = RT5665_FORMATS, + }, + .capture = { + .stream_name = "AIF2_2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5665_STEREO_RATES, + .formats = RT5665_FORMATS, + }, + .ops = &rt5665_aif_dai_ops, + }, + { + .name = "rt5665-aif3", + .id = RT5665_AIF3, + .playback = { + .stream_name = "AIF3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5665_STEREO_RATES, + .formats = RT5665_FORMATS, + }, + .capture = { + .stream_name = "AIF3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5665_STEREO_RATES, + .formats = RT5665_FORMATS, + }, + .ops = &rt5665_aif_dai_ops, + }, +}; + +static struct snd_soc_codec_driver soc_codec_dev_rt5665 = { + .probe = rt5665_probe, + .remove = rt5665_remove, + .suspend = rt5665_suspend, + .resume = rt5665_resume, + .set_bias_level = rt5665_set_bias_level, + .idle_bias_off = true, + .component_driver = { + .controls = rt5665_snd_controls, + .num_controls = ARRAY_SIZE(rt5665_snd_controls), + .dapm_widgets = rt5665_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5665_dapm_widgets), + .dapm_routes = rt5665_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5665_dapm_routes), + } +}; + + +static const struct regmap_config rt5665_regmap = { + .reg_bits = 16, + .val_bits = 16, + .max_register = 0x0400, + .volatile_reg = rt5665_volatile_register, + .readable_reg = rt5665_readable_register, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt5665_reg, + .num_reg_defaults = ARRAY_SIZE(rt5665_reg), + .use_single_rw = true, +}; + +static const struct i2c_device_id rt5665_i2c_id[] = { + {"rt5665", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, rt5665_i2c_id); + +static int rt5665_parse_dt(struct rt5665_priv *rt5665, struct device *dev) +{ + rt5665->pdata.in1_diff = of_property_read_bool(dev->of_node, + "realtek,in1-differential"); + rt5665->pdata.in2_diff = of_property_read_bool(dev->of_node, + "realtek,in2-differential"); + rt5665->pdata.in3_diff = of_property_read_bool(dev->of_node, + "realtek,in3-differential"); + rt5665->pdata.in4_diff = of_property_read_bool(dev->of_node, + "realtek,in4-differential"); + + of_property_read_u32(dev->of_node, "realtek,dmic1-data-pin", + &rt5665->pdata.dmic1_data_pin); + of_property_read_u32(dev->of_node, "realtek,dmic2-data-pin", + &rt5665->pdata.dmic2_data_pin); + of_property_read_u32(dev->of_node, "realtek,jd-src", + &rt5665->pdata.jd_src); + + rt5665->pdata.ldo1_en = of_get_named_gpio(dev->of_node, + "realtek,ldo1-en-gpios", 0); + + return 0; +} + +static void rt5665_calibrate(struct rt5665_priv *rt5665) +{ + int value, count; + + mutex_lock(&rt5665->calibrate_mutex); + + regcache_cache_bypass(rt5665->regmap, true); + + regmap_write(rt5665->regmap, RT5665_RESET, 0); + regmap_write(rt5665->regmap, RT5665_BIAS_CUR_CTRL_8, 0xa602); + regmap_write(rt5665->regmap, RT5665_HP_CHARGE_PUMP_1, 0x0c26); + regmap_write(rt5665->regmap, RT5665_MONOMIX_IN_GAIN, 0x021f); + regmap_write(rt5665->regmap, RT5665_MONO_OUT, 0x480a); + regmap_write(rt5665->regmap, RT5665_PWR_MIXER, 0x083f); + regmap_write(rt5665->regmap, RT5665_PWR_DIG_1, 0x0180); + regmap_write(rt5665->regmap, RT5665_EJD_CTRL_1, 0x4040); + regmap_write(rt5665->regmap, RT5665_HP_LOGIC_CTRL_2, 0x0000); + regmap_write(rt5665->regmap, RT5665_DIG_MISC, 0x0001); + regmap_write(rt5665->regmap, RT5665_MICBIAS_2, 0x0380); + regmap_write(rt5665->regmap, RT5665_GLB_CLK, 0x8000); + regmap_write(rt5665->regmap, RT5665_ADDA_CLK_1, 0x1000); + regmap_write(rt5665->regmap, RT5665_CHOP_DAC, 0x3030); + regmap_write(rt5665->regmap, RT5665_CALIB_ADC_CTRL, 0x3c05); + regmap_write(rt5665->regmap, RT5665_PWR_ANLG_1, 0xaa3e); + usleep_range(15000, 20000); + regmap_write(rt5665->regmap, RT5665_PWR_ANLG_1, 0xfe7e); + regmap_write(rt5665->regmap, RT5665_HP_CALIB_CTRL_2, 0x0321); + + regmap_write(rt5665->regmap, RT5665_HP_CALIB_CTRL_1, 0xfc00); + count = 0; + while (true) { + regmap_read(rt5665->regmap, RT5665_HP_CALIB_STA_1, &value); + if (value & 0x8000) + usleep_range(10000, 10005); + else + break; + + if (count > 60) { + pr_err("HP Calibration Failure\n"); + regmap_write(rt5665->regmap, RT5665_RESET, 0); + regcache_cache_bypass(rt5665->regmap, false); + goto out_unlock; + } + + count++; + } + + regmap_write(rt5665->regmap, RT5665_MONO_AMP_CALIB_CTRL_1, 0x9e24); + count = 0; + while (true) { + regmap_read(rt5665->regmap, RT5665_MONO_AMP_CALIB_STA1, &value); + if (value & 0x8000) + usleep_range(10000, 10005); + else + break; + + if (count > 60) { + pr_err("MONO Calibration Failure\n"); + regmap_write(rt5665->regmap, RT5665_RESET, 0); + regcache_cache_bypass(rt5665->regmap, false); + goto out_unlock; + } + + count++; + } + + regmap_write(rt5665->regmap, RT5665_RESET, 0); + regcache_cache_bypass(rt5665->regmap, false); + + regcache_mark_dirty(rt5665->regmap); + regcache_sync(rt5665->regmap); + + regmap_write(rt5665->regmap, RT5665_BIAS_CUR_CTRL_8, 0xa602); + regmap_write(rt5665->regmap, RT5665_ASRC_8, 0x0120); + +out_unlock: + mutex_unlock(&rt5665->calibrate_mutex); +} + +static void rt5665_calibrate_handler(struct work_struct *work) +{ + struct rt5665_priv *rt5665 = container_of(work, struct rt5665_priv, + calibrate_work.work); + + while (!rt5665->codec->component.card->instantiated) { + pr_debug("%s\n", __func__); + usleep_range(10000, 15000); + } + + rt5665_calibrate(rt5665); +} + +static int rt5665_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct rt5665_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct rt5665_priv *rt5665; + int i, ret; + unsigned int val; + + rt5665 = devm_kzalloc(&i2c->dev, sizeof(struct rt5665_priv), + GFP_KERNEL); + + if (rt5665 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt5665); + + if (pdata) + rt5665->pdata = *pdata; + else + rt5665_parse_dt(rt5665, &i2c->dev); + + for (i = 0; i < ARRAY_SIZE(rt5665->supplies); i++) + rt5665->supplies[i].supply = rt5665_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(rt5665->supplies), + rt5665->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(rt5665->supplies), + rt5665->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + if (gpio_is_valid(rt5665->pdata.ldo1_en)) { + if (devm_gpio_request_one(&i2c->dev, rt5665->pdata.ldo1_en, + GPIOF_OUT_INIT_HIGH, "rt5665")) + dev_err(&i2c->dev, "Fail gpio_request gpio_ldo\n"); + } + + /* Sleep for 300 ms miniumum */ + usleep_range(300000, 350000); + + rt5665->regmap = devm_regmap_init_i2c(i2c, &rt5665_regmap); + if (IS_ERR(rt5665->regmap)) { + ret = PTR_ERR(rt5665->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + regmap_read(rt5665->regmap, RT5665_DEVICE_ID, &val); + if (val != DEVICE_ID) { + dev_err(&i2c->dev, + "Device with ID register %x is not rt5665\n", val); + return -ENODEV; + } + + regmap_read(rt5665->regmap, RT5665_RESET, &val); + switch (val) { + case 0x0: + rt5665->id = CODEC_5666; + break; + case 0x6: + rt5665->id = CODEC_5668; + break; + case 0x3: + default: + rt5665->id = CODEC_5665; + break; + } + + regmap_write(rt5665->regmap, RT5665_RESET, 0); + + /* line in diff mode*/ + if (rt5665->pdata.in1_diff) + regmap_update_bits(rt5665->regmap, RT5665_IN1_IN2, + RT5665_IN1_DF_MASK, RT5665_IN1_DF_MASK); + if (rt5665->pdata.in2_diff) + regmap_update_bits(rt5665->regmap, RT5665_IN1_IN2, + RT5665_IN2_DF_MASK, RT5665_IN2_DF_MASK); + if (rt5665->pdata.in3_diff) + regmap_update_bits(rt5665->regmap, RT5665_IN3_IN4, + RT5665_IN3_DF_MASK, RT5665_IN3_DF_MASK); + if (rt5665->pdata.in4_diff) + regmap_update_bits(rt5665->regmap, RT5665_IN3_IN4, + RT5665_IN4_DF_MASK, RT5665_IN4_DF_MASK); + + /* DMIC pin*/ + if (rt5665->pdata.dmic1_data_pin != RT5665_DMIC1_NULL || + rt5665->pdata.dmic2_data_pin != RT5665_DMIC2_NULL) { + regmap_update_bits(rt5665->regmap, RT5665_GPIO_CTRL_2, + RT5665_GP9_PIN_MASK, RT5665_GP9_PIN_DMIC1_SCL); + regmap_update_bits(rt5665->regmap, RT5665_GPIO_CTRL_1, + RT5665_GP8_PIN_MASK, RT5665_GP8_PIN_DMIC2_SCL); + switch (rt5665->pdata.dmic1_data_pin) { + case RT5665_DMIC1_DATA_IN2N: + regmap_update_bits(rt5665->regmap, RT5665_DMIC_CTRL_1, + RT5665_DMIC_1_DP_MASK, RT5665_DMIC_1_DP_IN2N); + break; + + case RT5665_DMIC1_DATA_GPIO4: + regmap_update_bits(rt5665->regmap, RT5665_DMIC_CTRL_1, + RT5665_DMIC_1_DP_MASK, RT5665_DMIC_1_DP_GPIO4); + regmap_update_bits(rt5665->regmap, RT5665_GPIO_CTRL_1, + RT5665_GP4_PIN_MASK, RT5665_GP4_PIN_DMIC1_SDA); + break; + + default: + dev_dbg(&i2c->dev, "no DMIC1\n"); + break; + } + + switch (rt5665->pdata.dmic2_data_pin) { + case RT5665_DMIC2_DATA_IN2P: + regmap_update_bits(rt5665->regmap, RT5665_DMIC_CTRL_1, + RT5665_DMIC_2_DP_MASK, RT5665_DMIC_2_DP_IN2P); + break; + + case RT5665_DMIC2_DATA_GPIO5: + regmap_update_bits(rt5665->regmap, + RT5665_DMIC_CTRL_1, + RT5665_DMIC_2_DP_MASK, + RT5665_DMIC_2_DP_GPIO5); + regmap_update_bits(rt5665->regmap, RT5665_GPIO_CTRL_1, + RT5665_GP5_PIN_MASK, RT5665_GP5_PIN_DMIC2_SDA); + break; + + default: + dev_dbg(&i2c->dev, "no DMIC2\n"); + break; + + } + } + + regmap_write(rt5665->regmap, RT5665_HP_LOGIC_CTRL_2, 0x0002); + regmap_update_bits(rt5665->regmap, RT5665_EJD_CTRL_1, + 0xf000 | RT5665_VREF_POW_MASK, 0xd000 | RT5665_VREF_POW_REG); + /* Work around for pow_pump */ + regmap_update_bits(rt5665->regmap, RT5665_STO1_DAC_SIL_DET, + RT5665_DEB_STO_DAC_MASK, RT5665_DEB_80_MS); + + regmap_update_bits(rt5665->regmap, RT5665_HP_CHARGE_PUMP_1, + RT5665_PM_HP_MASK, RT5665_PM_HP_HV); + + /* Set GPIO4,8 as input for combo jack */ + if (rt5665->id == CODEC_5666) { + regmap_update_bits(rt5665->regmap, RT5665_GPIO_CTRL_2, + RT5665_GP4_PF_MASK, RT5665_GP4_PF_IN); + regmap_update_bits(rt5665->regmap, RT5665_GPIO_CTRL_3, + RT5665_GP8_PF_MASK, RT5665_GP8_PF_IN); + } + + /* Enhance performance*/ + regmap_update_bits(rt5665->regmap, RT5665_PWR_ANLG_1, + RT5665_HP_DRIVER_MASK | RT5665_LDO1_DVO_MASK, + RT5665_HP_DRIVER_5X | RT5665_LDO1_DVO_09); + + INIT_DELAYED_WORK(&rt5665->jack_detect_work, + rt5665_jack_detect_handler); + INIT_DELAYED_WORK(&rt5665->calibrate_work, + rt5665_calibrate_handler); + INIT_DELAYED_WORK(&rt5665->jd_check_work, + rt5665_jd_check_handler); + + mutex_init(&rt5665->calibrate_mutex); + + if (i2c->irq) { + ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, + rt5665_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING + | IRQF_ONESHOT, "rt5665", rt5665); + if (ret) + dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + + } + + return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5665, + rt5665_dai, ARRAY_SIZE(rt5665_dai)); +} + +static int rt5665_i2c_remove(struct i2c_client *i2c) +{ + snd_soc_unregister_codec(&i2c->dev); + + return 0; +} + +static void rt5665_i2c_shutdown(struct i2c_client *client) +{ + struct rt5665_priv *rt5665 = i2c_get_clientdata(client); + + regmap_write(rt5665->regmap, RT5665_RESET, 0); +} + +#ifdef CONFIG_OF +static const struct of_device_id rt5665_of_match[] = { + {.compatible = "realtek,rt5665"}, + {.compatible = "realtek,rt5666"}, + {.compatible = "realtek,rt5668"}, + {}, +}; +MODULE_DEVICE_TABLE(of, rt5665_of_match); +#endif + +#ifdef CONFIG_ACPI +static struct acpi_device_id rt5665_acpi_match[] = { + {"10EC5665", 0,}, + {"10EC5666", 0,}, + {"10EC5668", 0,}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, rt5665_acpi_match); +#endif + +struct i2c_driver rt5665_i2c_driver = { + .driver = { + .name = "rt5665", + .of_match_table = of_match_ptr(rt5665_of_match), + .acpi_match_table = ACPI_PTR(rt5665_acpi_match), + }, + .probe = rt5665_i2c_probe, + .remove = rt5665_i2c_remove, + .shutdown = rt5665_i2c_shutdown, + .id_table = rt5665_i2c_id, +}; +module_i2c_driver(rt5665_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5665 driver"); +MODULE_AUTHOR("Bard Liao <bardliao@realtek.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5665.h b/sound/soc/codecs/rt5665.h new file mode 100644 index 000000000000..12f7080a0d3c --- /dev/null +++ b/sound/soc/codecs/rt5665.h @@ -0,0 +1,1990 @@ +/* + * rt5665.h -- RT5665/RT5658 ALSA SoC audio driver + * + * Copyright 2016 Realtek Microelectronics + * Author: Bard Liao <bardliao@realtek.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 __RT5665_H__ +#define __RT5665_H__ + +#include <sound/rt5665.h> + +#define DEVICE_ID 0x6451 + +/* Info */ +#define RT5665_RESET 0x0000 +#define RT5665_VENDOR_ID 0x00fd +#define RT5665_VENDOR_ID_1 0x00fe +#define RT5665_DEVICE_ID 0x00ff +/* I/O - Output */ +#define RT5665_LOUT 0x0001 +#define RT5665_HP_CTRL_1 0x0002 +#define RT5665_HP_CTRL_2 0x0003 +#define RT5665_MONO_OUT 0x0004 +#define RT5665_HPL_GAIN 0x0005 +#define RT5665_HPR_GAIN 0x0006 +#define RT5665_MONO_GAIN 0x0007 + +/* I/O - Input */ +#define RT5665_CAL_BST_CTRL 0x000a +#define RT5665_CBJ_BST_CTRL 0x000b +#define RT5665_IN1_IN2 0x000c +#define RT5665_IN3_IN4 0x000d +#define RT5665_INL1_INR1_VOL 0x000f +/* I/O - Speaker */ +#define RT5665_EJD_CTRL_1 0x0010 +#define RT5665_EJD_CTRL_2 0x0011 +#define RT5665_EJD_CTRL_3 0x0012 +#define RT5665_EJD_CTRL_4 0x0013 +#define RT5665_EJD_CTRL_5 0x0014 +#define RT5665_EJD_CTRL_6 0x0015 +#define RT5665_EJD_CTRL_7 0x0016 +/* I/O - ADC/DAC/DMIC */ +#define RT5665_DAC2_CTRL 0x0017 +#define RT5665_DAC2_DIG_VOL 0x0018 +#define RT5665_DAC1_DIG_VOL 0x0019 +#define RT5665_DAC3_DIG_VOL 0x001a +#define RT5665_DAC3_CTRL 0x001b +#define RT5665_STO1_ADC_DIG_VOL 0x001c +#define RT5665_MONO_ADC_DIG_VOL 0x001d +#define RT5665_STO2_ADC_DIG_VOL 0x001e +#define RT5665_STO1_ADC_BOOST 0x001f +#define RT5665_MONO_ADC_BOOST 0x0020 +#define RT5665_STO2_ADC_BOOST 0x0021 +#define RT5665_HP_IMP_GAIN_1 0x0022 +#define RT5665_HP_IMP_GAIN_2 0x0023 +/* Mixer - D-D */ +#define RT5665_STO1_ADC_MIXER 0x0026 +#define RT5665_MONO_ADC_MIXER 0x0027 +#define RT5665_STO2_ADC_MIXER 0x0028 +#define RT5665_AD_DA_MIXER 0x0029 +#define RT5665_STO1_DAC_MIXER 0x002a +#define RT5665_MONO_DAC_MIXER 0x002b +#define RT5665_STO2_DAC_MIXER 0x002c +#define RT5665_A_DAC1_MUX 0x002d +#define RT5665_A_DAC2_MUX 0x002e +#define RT5665_DIG_INF2_DATA 0x002f +#define RT5665_DIG_INF3_DATA 0x0030 +/* Mixer - PDM */ +#define RT5665_PDM_OUT_CTRL 0x0031 +#define RT5665_PDM_DATA_CTRL_1 0x0032 +#define RT5665_PDM_DATA_CTRL_2 0x0033 +#define RT5665_PDM_DATA_CTRL_3 0x0034 +#define RT5665_PDM_DATA_CTRL_4 0x0035 +/* Mixer - ADC */ +#define RT5665_REC1_GAIN 0x003a +#define RT5665_REC1_L1_MIXER 0x003b +#define RT5665_REC1_L2_MIXER 0x003c +#define RT5665_REC1_R1_MIXER 0x003d +#define RT5665_REC1_R2_MIXER 0x003e +#define RT5665_REC2_GAIN 0x003f +#define RT5665_REC2_L1_MIXER 0x0040 +#define RT5665_REC2_L2_MIXER 0x0041 +#define RT5665_REC2_R1_MIXER 0x0042 +#define RT5665_REC2_R2_MIXER 0x0043 +#define RT5665_CAL_REC 0x0044 +/* Mixer - DAC */ +#define RT5665_ALC_BACK_GAIN 0x0049 +#define RT5665_MONOMIX_GAIN 0x004a +#define RT5665_MONOMIX_IN_GAIN 0x004b +#define RT5665_OUT_L_GAIN 0x004d +#define RT5665_OUT_L_MIXER 0x004e +#define RT5665_OUT_R_GAIN 0x004f +#define RT5665_OUT_R_MIXER 0x0050 +#define RT5665_LOUT_MIXER 0x0052 +/* Power */ +#define RT5665_PWR_DIG_1 0x0061 +#define RT5665_PWR_DIG_2 0x0062 +#define RT5665_PWR_ANLG_1 0x0063 +#define RT5665_PWR_ANLG_2 0x0064 +#define RT5665_PWR_ANLG_3 0x0065 +#define RT5665_PWR_MIXER 0x0066 +#define RT5665_PWR_VOL 0x0067 +/* Clock Detect */ +#define RT5665_CLK_DET 0x006b +/* Filter */ +#define RT5665_HPF_CTRL1 0x006d +/* DMIC */ +#define RT5665_DMIC_CTRL_1 0x006e +#define RT5665_DMIC_CTRL_2 0x006f +/* Format - ADC/DAC */ +#define RT5665_I2S1_SDP 0x0070 +#define RT5665_I2S2_SDP 0x0071 +#define RT5665_I2S3_SDP 0x0072 +#define RT5665_ADDA_CLK_1 0x0073 +#define RT5665_ADDA_CLK_2 0x0074 +#define RT5665_I2S1_F_DIV_CTRL_1 0x0075 +#define RT5665_I2S1_F_DIV_CTRL_2 0x0076 +/* Format - TDM Control */ +#define RT5665_TDM_CTRL_1 0x0078 +#define RT5665_TDM_CTRL_2 0x0079 +#define RT5665_TDM_CTRL_3 0x007a +#define RT5665_TDM_CTRL_4 0x007b +#define RT5665_TDM_CTRL_5 0x007c +#define RT5665_TDM_CTRL_6 0x007d +#define RT5665_TDM_CTRL_7 0x007e +#define RT5665_TDM_CTRL_8 0x007f +/* Function - Analog */ +#define RT5665_GLB_CLK 0x0080 +#define RT5665_PLL_CTRL_1 0x0081 +#define RT5665_PLL_CTRL_2 0x0082 +#define RT5665_ASRC_1 0x0083 +#define RT5665_ASRC_2 0x0084 +#define RT5665_ASRC_3 0x0085 +#define RT5665_ASRC_4 0x0086 +#define RT5665_ASRC_5 0x0087 +#define RT5665_ASRC_6 0x0088 +#define RT5665_ASRC_7 0x0089 +#define RT5665_ASRC_8 0x008a +#define RT5665_ASRC_9 0x008b +#define RT5665_ASRC_10 0x008c +#define RT5665_DEPOP_1 0x008e +#define RT5665_DEPOP_2 0x008f +#define RT5665_HP_CHARGE_PUMP_1 0x0091 +#define RT5665_HP_CHARGE_PUMP_2 0x0092 +#define RT5665_MICBIAS_1 0x0093 +#define RT5665_MICBIAS_2 0x0094 +#define RT5665_ASRC_12 0x0098 +#define RT5665_ASRC_13 0x0099 +#define RT5665_ASRC_14 0x009a +#define RT5665_RC_CLK_CTRL 0x009f +#define RT5665_I2S_M_CLK_CTRL_1 0x00a0 +#define RT5665_I2S2_F_DIV_CTRL_1 0x00a1 +#define RT5665_I2S2_F_DIV_CTRL_2 0x00a2 +#define RT5665_I2S3_F_DIV_CTRL_1 0x00a3 +#define RT5665_I2S3_F_DIV_CTRL_2 0x00a4 +/* Function - Digital */ +#define RT5665_EQ_CTRL_1 0x00ae +#define RT5665_EQ_CTRL_2 0x00af +#define RT5665_IRQ_CTRL_1 0x00b6 +#define RT5665_IRQ_CTRL_2 0x00b7 +#define RT5665_IRQ_CTRL_3 0x00b8 +#define RT5665_IRQ_CTRL_4 0x00b9 +#define RT5665_IRQ_CTRL_5 0x00ba +#define RT5665_IRQ_CTRL_6 0x00bb +#define RT5665_INT_ST_1 0x00be +#define RT5665_GPIO_CTRL_1 0x00c0 +#define RT5665_GPIO_CTRL_2 0x00c1 +#define RT5665_GPIO_CTRL_3 0x00c2 +#define RT5665_GPIO_CTRL_4 0x00c3 +#define RT5665_GPIO_STA 0x00c4 +#define RT5665_HP_AMP_DET_CTRL_1 0x00d0 +#define RT5665_HP_AMP_DET_CTRL_2 0x00d1 +#define RT5665_MID_HP_AMP_DET 0x00d3 +#define RT5665_LOW_HP_AMP_DET 0x00d4 +#define RT5665_SV_ZCD_1 0x00d9 +#define RT5665_SV_ZCD_2 0x00da +#define RT5665_IL_CMD_1 0x00db +#define RT5665_IL_CMD_2 0x00dc +#define RT5665_IL_CMD_3 0x00dd +#define RT5665_IL_CMD_4 0x00de +#define RT5665_4BTN_IL_CMD_1 0x00df +#define RT5665_4BTN_IL_CMD_2 0x00e0 +#define RT5665_4BTN_IL_CMD_3 0x00e1 +#define RT5665_PSV_IL_CMD_1 0x00e2 + +#define RT5665_ADC_STO1_HP_CTRL_1 0x00ea +#define RT5665_ADC_STO1_HP_CTRL_2 0x00eb +#define RT5665_ADC_MONO_HP_CTRL_1 0x00ec +#define RT5665_ADC_MONO_HP_CTRL_2 0x00ed +#define RT5665_ADC_STO2_HP_CTRL_1 0x00ee +#define RT5665_ADC_STO2_HP_CTRL_2 0x00ef +#define RT5665_AJD1_CTRL 0x00f0 +#define RT5665_JD1_THD 0x00f1 +#define RT5665_JD2_THD 0x00f2 +#define RT5665_JD_CTRL_1 0x00f6 +#define RT5665_JD_CTRL_2 0x00f7 +#define RT5665_JD_CTRL_3 0x00f8 +/* General Control */ +#define RT5665_DIG_MISC 0x00fa +#define RT5665_DUMMY_2 0x00fb +#define RT5665_DUMMY_3 0x00fc + +#define RT5665_DAC_ADC_DIG_VOL1 0x0100 +#define RT5665_DAC_ADC_DIG_VOL2 0x0101 +#define RT5665_BIAS_CUR_CTRL_1 0x010a +#define RT5665_BIAS_CUR_CTRL_2 0x010b +#define RT5665_BIAS_CUR_CTRL_3 0x010c +#define RT5665_BIAS_CUR_CTRL_4 0x010d +#define RT5665_BIAS_CUR_CTRL_5 0x010e +#define RT5665_BIAS_CUR_CTRL_6 0x010f +#define RT5665_BIAS_CUR_CTRL_7 0x0110 +#define RT5665_BIAS_CUR_CTRL_8 0x0111 +#define RT5665_BIAS_CUR_CTRL_9 0x0112 +#define RT5665_BIAS_CUR_CTRL_10 0x0113 +#define RT5665_VREF_REC_OP_FB_CAP_CTRL 0x0117 +#define RT5665_CHARGE_PUMP_1 0x0125 +#define RT5665_DIG_IN_CTRL_1 0x0132 +#define RT5665_DIG_IN_CTRL_2 0x0133 +#define RT5665_PAD_DRIVING_CTRL 0x0137 +#define RT5665_SOFT_RAMP_DEPOP 0x0138 +#define RT5665_PLL 0x0139 +#define RT5665_CHOP_DAC 0x013a +#define RT5665_CHOP_ADC 0x013b +#define RT5665_CALIB_ADC_CTRL 0x013c +#define RT5665_VOL_TEST 0x013f +#define RT5665_TEST_MODE_CTRL_1 0x0145 +#define RT5665_TEST_MODE_CTRL_2 0x0146 +#define RT5665_TEST_MODE_CTRL_3 0x0147 +#define RT5665_TEST_MODE_CTRL_4 0x0148 +#define RT5665_BASSBACK_CTRL 0x0150 +#define RT5665_STO_NG2_CTRL_1 0x0160 +#define RT5665_STO_NG2_CTRL_2 0x0161 +#define RT5665_STO_NG2_CTRL_3 0x0162 +#define RT5665_STO_NG2_CTRL_4 0x0163 +#define RT5665_STO_NG2_CTRL_5 0x0164 +#define RT5665_STO_NG2_CTRL_6 0x0165 +#define RT5665_STO_NG2_CTRL_7 0x0166 +#define RT5665_STO_NG2_CTRL_8 0x0167 +#define RT5665_MONO_NG2_CTRL_1 0x0170 +#define RT5665_MONO_NG2_CTRL_2 0x0171 +#define RT5665_MONO_NG2_CTRL_3 0x0172 +#define RT5665_MONO_NG2_CTRL_4 0x0173 +#define RT5665_MONO_NG2_CTRL_5 0x0174 +#define RT5665_MONO_NG2_CTRL_6 0x0175 +#define RT5665_STO1_DAC_SIL_DET 0x0190 +#define RT5665_MONOL_DAC_SIL_DET 0x0191 +#define RT5665_MONOR_DAC_SIL_DET 0x0192 +#define RT5665_STO2_DAC_SIL_DET 0x0193 +#define RT5665_SIL_PSV_CTRL1 0x0194 +#define RT5665_SIL_PSV_CTRL2 0x0195 +#define RT5665_SIL_PSV_CTRL3 0x0196 +#define RT5665_SIL_PSV_CTRL4 0x0197 +#define RT5665_SIL_PSV_CTRL5 0x0198 +#define RT5665_SIL_PSV_CTRL6 0x0199 +#define RT5665_MONO_AMP_CALIB_CTRL_1 0x01a0 +#define RT5665_MONO_AMP_CALIB_CTRL_2 0x01a1 +#define RT5665_MONO_AMP_CALIB_CTRL_3 0x01a2 +#define RT5665_MONO_AMP_CALIB_CTRL_4 0x01a3 +#define RT5665_MONO_AMP_CALIB_CTRL_5 0x01a4 +#define RT5665_MONO_AMP_CALIB_CTRL_6 0x01a5 +#define RT5665_MONO_AMP_CALIB_CTRL_7 0x01a6 +#define RT5665_MONO_AMP_CALIB_STA1 0x01a7 +#define RT5665_MONO_AMP_CALIB_STA2 0x01a8 +#define RT5665_MONO_AMP_CALIB_STA3 0x01a9 +#define RT5665_MONO_AMP_CALIB_STA4 0x01aa +#define RT5665_MONO_AMP_CALIB_STA6 0x01ab +#define RT5665_HP_IMP_SENS_CTRL_01 0x01b5 +#define RT5665_HP_IMP_SENS_CTRL_02 0x01b6 +#define RT5665_HP_IMP_SENS_CTRL_03 0x01b7 +#define RT5665_HP_IMP_SENS_CTRL_04 0x01b8 +#define RT5665_HP_IMP_SENS_CTRL_05 0x01b9 +#define RT5665_HP_IMP_SENS_CTRL_06 0x01ba +#define RT5665_HP_IMP_SENS_CTRL_07 0x01bb +#define RT5665_HP_IMP_SENS_CTRL_08 0x01bc +#define RT5665_HP_IMP_SENS_CTRL_09 0x01bd +#define RT5665_HP_IMP_SENS_CTRL_10 0x01be +#define RT5665_HP_IMP_SENS_CTRL_11 0x01bf +#define RT5665_HP_IMP_SENS_CTRL_12 0x01c0 +#define RT5665_HP_IMP_SENS_CTRL_13 0x01c1 +#define RT5665_HP_IMP_SENS_CTRL_14 0x01c2 +#define RT5665_HP_IMP_SENS_CTRL_15 0x01c3 +#define RT5665_HP_IMP_SENS_CTRL_16 0x01c4 +#define RT5665_HP_IMP_SENS_CTRL_17 0x01c5 +#define RT5665_HP_IMP_SENS_CTRL_18 0x01c6 +#define RT5665_HP_IMP_SENS_CTRL_19 0x01c7 +#define RT5665_HP_IMP_SENS_CTRL_20 0x01c8 +#define RT5665_HP_IMP_SENS_CTRL_21 0x01c9 +#define RT5665_HP_IMP_SENS_CTRL_22 0x01ca +#define RT5665_HP_IMP_SENS_CTRL_23 0x01cb +#define RT5665_HP_IMP_SENS_CTRL_24 0x01cc +#define RT5665_HP_IMP_SENS_CTRL_25 0x01cd +#define RT5665_HP_IMP_SENS_CTRL_26 0x01ce +#define RT5665_HP_IMP_SENS_CTRL_27 0x01cf +#define RT5665_HP_IMP_SENS_CTRL_28 0x01d0 +#define RT5665_HP_IMP_SENS_CTRL_29 0x01d1 +#define RT5665_HP_IMP_SENS_CTRL_30 0x01d2 +#define RT5665_HP_IMP_SENS_CTRL_31 0x01d3 +#define RT5665_HP_IMP_SENS_CTRL_32 0x01d4 +#define RT5665_HP_IMP_SENS_CTRL_33 0x01d5 +#define RT5665_HP_IMP_SENS_CTRL_34 0x01d6 +#define RT5665_HP_LOGIC_CTRL_1 0x01da +#define RT5665_HP_LOGIC_CTRL_2 0x01db +#define RT5665_HP_LOGIC_CTRL_3 0x01dc +#define RT5665_HP_CALIB_CTRL_1 0x01de +#define RT5665_HP_CALIB_CTRL_2 0x01df +#define RT5665_HP_CALIB_CTRL_3 0x01e0 +#define RT5665_HP_CALIB_CTRL_4 0x01e1 +#define RT5665_HP_CALIB_CTRL_5 0x01e2 +#define RT5665_HP_CALIB_CTRL_6 0x01e3 +#define RT5665_HP_CALIB_CTRL_7 0x01e4 +#define RT5665_HP_CALIB_CTRL_9 0x01e6 +#define RT5665_HP_CALIB_CTRL_10 0x01e7 +#define RT5665_HP_CALIB_CTRL_11 0x01e8 +#define RT5665_HP_CALIB_STA_1 0x01ea +#define RT5665_HP_CALIB_STA_2 0x01eb +#define RT5665_HP_CALIB_STA_3 0x01ec +#define RT5665_HP_CALIB_STA_4 0x01ed +#define RT5665_HP_CALIB_STA_5 0x01ee +#define RT5665_HP_CALIB_STA_6 0x01ef +#define RT5665_HP_CALIB_STA_7 0x01f0 +#define RT5665_HP_CALIB_STA_8 0x01f1 +#define RT5665_HP_CALIB_STA_9 0x01f2 +#define RT5665_HP_CALIB_STA_10 0x01f3 +#define RT5665_HP_CALIB_STA_11 0x01f4 +#define RT5665_PGM_TAB_CTRL1 0x0200 +#define RT5665_PGM_TAB_CTRL2 0x0201 +#define RT5665_PGM_TAB_CTRL3 0x0202 +#define RT5665_PGM_TAB_CTRL4 0x0203 +#define RT5665_PGM_TAB_CTRL5 0x0204 +#define RT5665_PGM_TAB_CTRL6 0x0205 +#define RT5665_PGM_TAB_CTRL7 0x0206 +#define RT5665_PGM_TAB_CTRL8 0x0207 +#define RT5665_PGM_TAB_CTRL9 0x0208 +#define RT5665_SAR_IL_CMD_1 0x0210 +#define RT5665_SAR_IL_CMD_2 0x0211 +#define RT5665_SAR_IL_CMD_3 0x0212 +#define RT5665_SAR_IL_CMD_4 0x0213 +#define RT5665_SAR_IL_CMD_5 0x0214 +#define RT5665_SAR_IL_CMD_6 0x0215 +#define RT5665_SAR_IL_CMD_7 0x0216 +#define RT5665_SAR_IL_CMD_8 0x0217 +#define RT5665_SAR_IL_CMD_9 0x0218 +#define RT5665_SAR_IL_CMD_10 0x0219 +#define RT5665_SAR_IL_CMD_11 0x021a +#define RT5665_SAR_IL_CMD_12 0x021b +#define RT5665_DRC1_CTRL_0 0x02ff +#define RT5665_DRC1_CTRL_1 0x0300 +#define RT5665_DRC1_CTRL_2 0x0301 +#define RT5665_DRC1_CTRL_3 0x0302 +#define RT5665_DRC1_CTRL_4 0x0303 +#define RT5665_DRC1_CTRL_5 0x0304 +#define RT5665_DRC1_CTRL_6 0x0305 +#define RT5665_DRC1_HARD_LMT_CTRL_1 0x0306 +#define RT5665_DRC1_HARD_LMT_CTRL_2 0x0307 +#define RT5665_DRC1_PRIV_1 0x0310 +#define RT5665_DRC1_PRIV_2 0x0311 +#define RT5665_DRC1_PRIV_3 0x0312 +#define RT5665_DRC1_PRIV_4 0x0313 +#define RT5665_DRC1_PRIV_5 0x0314 +#define RT5665_DRC1_PRIV_6 0x0315 +#define RT5665_DRC1_PRIV_7 0x0316 +#define RT5665_DRC1_PRIV_8 0x0317 +#define RT5665_ALC_PGA_CTRL_1 0x0330 +#define RT5665_ALC_PGA_CTRL_2 0x0331 +#define RT5665_ALC_PGA_CTRL_3 0x0332 +#define RT5665_ALC_PGA_CTRL_4 0x0333 +#define RT5665_ALC_PGA_CTRL_5 0x0334 +#define RT5665_ALC_PGA_CTRL_6 0x0335 +#define RT5665_ALC_PGA_CTRL_7 0x0336 +#define RT5665_ALC_PGA_CTRL_8 0x0337 +#define RT5665_ALC_PGA_STA_1 0x0338 +#define RT5665_ALC_PGA_STA_2 0x0339 +#define RT5665_ALC_PGA_STA_3 0x033a +#define RT5665_EQ_AUTO_RCV_CTRL1 0x03c0 +#define RT5665_EQ_AUTO_RCV_CTRL2 0x03c1 +#define RT5665_EQ_AUTO_RCV_CTRL3 0x03c2 +#define RT5665_EQ_AUTO_RCV_CTRL4 0x03c3 +#define RT5665_EQ_AUTO_RCV_CTRL5 0x03c4 +#define RT5665_EQ_AUTO_RCV_CTRL6 0x03c5 +#define RT5665_EQ_AUTO_RCV_CTRL7 0x03c6 +#define RT5665_EQ_AUTO_RCV_CTRL8 0x03c7 +#define RT5665_EQ_AUTO_RCV_CTRL9 0x03c8 +#define RT5665_EQ_AUTO_RCV_CTRL10 0x03c9 +#define RT5665_EQ_AUTO_RCV_CTRL11 0x03ca +#define RT5665_EQ_AUTO_RCV_CTRL12 0x03cb +#define RT5665_EQ_AUTO_RCV_CTRL13 0x03cc +#define RT5665_ADC_L_EQ_LPF1_A1 0x03d0 +#define RT5665_R_EQ_LPF1_A1 0x03d1 +#define RT5665_L_EQ_LPF1_H0 0x03d2 +#define RT5665_R_EQ_LPF1_H0 0x03d3 +#define RT5665_L_EQ_BPF1_A1 0x03d4 +#define RT5665_R_EQ_BPF1_A1 0x03d5 +#define RT5665_L_EQ_BPF1_A2 0x03d6 +#define RT5665_R_EQ_BPF1_A2 0x03d7 +#define RT5665_L_EQ_BPF1_H0 0x03d8 +#define RT5665_R_EQ_BPF1_H0 0x03d9 +#define RT5665_L_EQ_BPF2_A1 0x03da +#define RT5665_R_EQ_BPF2_A1 0x03db +#define RT5665_L_EQ_BPF2_A2 0x03dc +#define RT5665_R_EQ_BPF2_A2 0x03dd +#define RT5665_L_EQ_BPF2_H0 0x03de +#define RT5665_R_EQ_BPF2_H0 0x03df +#define RT5665_L_EQ_BPF3_A1 0x03e0 +#define RT5665_R_EQ_BPF3_A1 0x03e1 +#define RT5665_L_EQ_BPF3_A2 0x03e2 +#define RT5665_R_EQ_BPF3_A2 0x03e3 +#define RT5665_L_EQ_BPF3_H0 0x03e4 +#define RT5665_R_EQ_BPF3_H0 0x03e5 +#define RT5665_L_EQ_BPF4_A1 0x03e6 +#define RT5665_R_EQ_BPF4_A1 0x03e7 +#define RT5665_L_EQ_BPF4_A2 0x03e8 +#define RT5665_R_EQ_BPF4_A2 0x03e9 +#define RT5665_L_EQ_BPF4_H0 0x03ea +#define RT5665_R_EQ_BPF4_H0 0x03eb +#define RT5665_L_EQ_HPF1_A1 0x03ec +#define RT5665_R_EQ_HPF1_A1 0x03ed +#define RT5665_L_EQ_HPF1_H0 0x03ee +#define RT5665_R_EQ_HPF1_H0 0x03ef +#define RT5665_L_EQ_PRE_VOL 0x03f0 +#define RT5665_R_EQ_PRE_VOL 0x03f1 +#define RT5665_L_EQ_POST_VOL 0x03f2 +#define RT5665_R_EQ_POST_VOL 0x03f3 +#define RT5665_SCAN_MODE_CTRL 0x07f0 +#define RT5665_I2C_MODE 0x07fa + + + +/* global definition */ +#define RT5665_L_MUTE (0x1 << 15) +#define RT5665_L_MUTE_SFT 15 +#define RT5665_VOL_L_MUTE (0x1 << 14) +#define RT5665_VOL_L_SFT 14 +#define RT5665_R_MUTE (0x1 << 7) +#define RT5665_R_MUTE_SFT 7 +#define RT5665_VOL_R_MUTE (0x1 << 6) +#define RT5665_VOL_R_SFT 6 +#define RT5665_L_VOL_MASK (0x3f << 8) +#define RT5665_L_VOL_SFT 8 +#define RT5665_R_VOL_MASK (0x3f) +#define RT5665_R_VOL_SFT 0 + +/*Headphone Amp L/R Analog Gain and Digital NG2 Gain Control (0x0005 0x0006)*/ +#define RT5665_G_HP (0xf << 8) +#define RT5665_G_HP_SFT 8 +#define RT5665_G_STO_DA_DMIX (0xf) +#define RT5665_G_STO_DA_SFT 0 + +/* CBJ Control (0x000b) */ +#define RT5665_BST_CBJ_MASK (0xf << 8) +#define RT5665_BST_CBJ_SFT 8 + +/* IN1/IN2 Control (0x000c) */ +#define RT5665_IN1_DF_MASK (0x1 << 15) +#define RT5665_IN1_DF 15 +#define RT5665_BST1_MASK (0x7f << 8) +#define RT5665_BST1_SFT 8 +#define RT5665_IN2_DF_MASK (0x1 << 7) +#define RT5665_IN2_DF 7 +#define RT5665_BST2_MASK (0x7f) +#define RT5665_BST2_SFT 0 + +/* IN3/IN4 Control (0x000d) */ +#define RT5665_IN3_DF_MASK (0x1 << 15) +#define RT5665_IN3_DF 15 +#define RT5665_BST3_MASK (0x7f << 8) +#define RT5665_BST3_SFT 8 +#define RT5665_IN4_DF_MASK (0x1 << 7) +#define RT5665_IN4_DF 7 +#define RT5665_BST4_MASK (0x7f) +#define RT5665_BST4_SFT 0 + +/* INL and INR Volume Control (0x000f) */ +#define RT5665_INL_VOL_MASK (0x1f << 8) +#define RT5665_INL_VOL_SFT 8 +#define RT5665_INR_VOL_MASK (0x1f) +#define RT5665_INR_VOL_SFT 0 + +/* Embeeded Jack and Type Detection Control 1 (0x0010) */ +#define RT5665_EMB_JD_EN (0x1 << 15) +#define RT5665_EMB_JD_EN_SFT 15 +#define RT5665_JD_MODE (0x1 << 13) +#define RT5665_JD_MODE_SFT 13 +#define RT5665_POLA_EXT_JD_MASK (0x1 << 11) +#define RT5665_POLA_EXT_JD_LOW (0x1 << 11) +#define RT5665_POLA_EXT_JD_HIGH (0x0 << 11) +#define RT5665_EXT_JD_DIG (0x1 << 9) +#define RT5665_POL_FAST_OFF_MASK (0x1 << 8) +#define RT5665_POL_FAST_OFF_HIGH (0x1 << 8) +#define RT5665_POL_FAST_OFF_LOW (0x0 << 8) +#define RT5665_VREF_POW_MASK (0x1 << 6) +#define RT5665_VREF_POW_FSM (0x0 << 6) +#define RT5665_VREF_POW_REG (0x1 << 6) +#define RT5665_MB1_PATH_MASK (0x1 << 5) +#define RT5665_CTRL_MB1_REG (0x1 << 5) +#define RT5665_CTRL_MB1_FSM (0x0 << 5) +#define RT5665_MB2_PATH_MASK (0x1 << 4) +#define RT5665_CTRL_MB2_REG (0x1 << 4) +#define RT5665_CTRL_MB2_FSM (0x0 << 4) +#define RT5665_TRIG_JD_MASK (0x1 << 3) +#define RT5665_TRIG_JD_HIGH (0x1 << 3) +#define RT5665_TRIG_JD_LOW (0x0 << 3) + +/* Embeeded Jack and Type Detection Control 2 (0x0011) */ +#define RT5665_EXT_JD_SRC (0x7 << 4) +#define RT5665_EXT_JD_SRC_SFT 4 +#define RT5665_EXT_JD_SRC_GPIO_JD1 (0x0 << 4) +#define RT5665_EXT_JD_SRC_GPIO_JD2 (0x1 << 4) +#define RT5665_EXT_JD_SRC_JD1_1 (0x2 << 4) +#define RT5665_EXT_JD_SRC_JD1_2 (0x3 << 4) +#define RT5665_EXT_JD_SRC_JD2 (0x4 << 4) +#define RT5665_EXT_JD_SRC_JD3 (0x5 << 4) +#define RT5665_EXT_JD_SRC_MANUAL (0x6 << 4) + +/* Combo Jack and Type Detection Control 4 (0x0013) */ +#define RT5665_SEL_SHT_MID_TON_MASK (0x3 << 12) +#define RT5665_SEL_SHT_MID_TON_2 (0x0 << 12) +#define RT5665_SEL_SHT_MID_TON_3 (0x1 << 12) +#define RT5665_CBJ_JD_TEST_MASK (0x1 << 6) +#define RT5665_CBJ_JD_TEST_NORM (0x0 << 6) +#define RT5665_CBJ_JD_TEST_MODE (0x1 << 6) + +/* Slience Detection Control (0x0015) */ +#define RT5665_SIL_DET_MASK (0x1 << 15) +#define RT5665_SIL_DET_DIS (0x0 << 15) +#define RT5665_SIL_DET_EN (0x1 << 15) + +/* DAC2 Control (0x0017) */ +#define RT5665_M_DAC2_L_VOL (0x1 << 13) +#define RT5665_M_DAC2_L_VOL_SFT 13 +#define RT5665_M_DAC2_R_VOL (0x1 << 12) +#define RT5665_M_DAC2_R_VOL_SFT 12 +#define RT5665_DAC_L2_SEL_MASK (0x7 << 4) +#define RT5665_DAC_L2_SEL_SFT 4 +#define RT5665_DAC_R2_SEL_MASK (0x7 << 0) +#define RT5665_DAC_R2_SEL_SFT 0 + +/* Sidetone Control (0x0018) */ +#define RT5665_ST_SEL_MASK (0x7 << 9) +#define RT5665_ST_SEL_SFT 9 +#define RT5665_ST_EN (0x1 << 6) +#define RT5665_ST_EN_SFT 6 + +/* DAC1 Digital Volume (0x0019) */ +#define RT5665_DAC_L1_VOL_MASK (0xff << 8) +#define RT5665_DAC_L1_VOL_SFT 8 +#define RT5665_DAC_R1_VOL_MASK (0xff) +#define RT5665_DAC_R1_VOL_SFT 0 + +/* DAC2 Digital Volume (0x001a) */ +#define RT5665_DAC_L2_VOL_MASK (0xff << 8) +#define RT5665_DAC_L2_VOL_SFT 8 +#define RT5665_DAC_R2_VOL_MASK (0xff) +#define RT5665_DAC_R2_VOL_SFT 0 + +/* DAC3 Control (0x001b) */ +#define RT5665_M_DAC3_L_VOL (0x1 << 13) +#define RT5665_M_DAC3_L_VOL_SFT 13 +#define RT5665_M_DAC3_R_VOL (0x1 << 12) +#define RT5665_M_DAC3_R_VOL_SFT 12 +#define RT5665_DAC_L3_SEL_MASK (0x7 << 4) +#define RT5665_DAC_L3_SEL_SFT 4 +#define RT5665_DAC_R3_SEL_MASK (0x7 << 0) +#define RT5665_DAC_R3_SEL_SFT 0 + +/* ADC Digital Volume Control (0x001c) */ +#define RT5665_ADC_L_VOL_MASK (0x7f << 8) +#define RT5665_ADC_L_VOL_SFT 8 +#define RT5665_ADC_R_VOL_MASK (0x7f) +#define RT5665_ADC_R_VOL_SFT 0 + +/* Mono ADC Digital Volume Control (0x001d) */ +#define RT5665_MONO_ADC_L_VOL_MASK (0x7f << 8) +#define RT5665_MONO_ADC_L_VOL_SFT 8 +#define RT5665_MONO_ADC_R_VOL_MASK (0x7f) +#define RT5665_MONO_ADC_R_VOL_SFT 0 + +/* Stereo1 ADC Boost Gain Control (0x001f) */ +#define RT5665_STO1_ADC_L_BST_MASK (0x3 << 14) +#define RT5665_STO1_ADC_L_BST_SFT 14 +#define RT5665_STO1_ADC_R_BST_MASK (0x3 << 12) +#define RT5665_STO1_ADC_R_BST_SFT 12 + +/* Mono ADC Boost Gain Control (0x0020) */ +#define RT5665_MONO_ADC_L_BST_MASK (0x3 << 14) +#define RT5665_MONO_ADC_L_BST_SFT 14 +#define RT5665_MONO_ADC_R_BST_MASK (0x3 << 12) +#define RT5665_MONO_ADC_R_BST_SFT 12 + +/* Stereo1 ADC Boost Gain Control (0x001f) */ +#define RT5665_STO2_ADC_L_BST_MASK (0x3 << 14) +#define RT5665_STO2_ADC_L_BST_SFT 14 +#define RT5665_STO2_ADC_R_BST_MASK (0x3 << 12) +#define RT5665_STO2_ADC_R_BST_SFT 12 + +/* Stereo1 ADC Mixer Control (0x0026) */ +#define RT5665_M_STO1_ADC_L1 (0x1 << 15) +#define RT5665_M_STO1_ADC_L1_SFT 15 +#define RT5665_M_STO1_ADC_L2 (0x1 << 14) +#define RT5665_M_STO1_ADC_L2_SFT 14 +#define RT5665_STO1_ADC1L_SRC_MASK (0x1 << 13) +#define RT5665_STO1_ADC1L_SRC_SFT 13 +#define RT5665_STO1_ADC1_SRC_ADC (0x1 << 13) +#define RT5665_STO1_ADC1_SRC_DACMIX (0x0 << 13) +#define RT5665_STO1_ADC2L_SRC_MASK (0x1 << 12) +#define RT5665_STO1_ADC2L_SRC_SFT 12 +#define RT5665_STO1_ADCL_SRC_MASK (0x3 << 10) +#define RT5665_STO1_ADCL_SRC_SFT 10 +#define RT5665_STO1_DD_L_SRC_MASK (0x1 << 9) +#define RT5665_STO1_DD_L_SRC_SFT 9 +#define RT5665_STO1_DMIC_SRC_MASK (0x1 << 8) +#define RT5665_STO1_DMIC_SRC_SFT 8 +#define RT5665_STO1_DMIC_SRC_DMIC2 (0x1 << 8) +#define RT5665_STO1_DMIC_SRC_DMIC1 (0x0 << 8) +#define RT5665_M_STO1_ADC_R1 (0x1 << 7) +#define RT5665_M_STO1_ADC_R1_SFT 7 +#define RT5665_M_STO1_ADC_R2 (0x1 << 6) +#define RT5665_M_STO1_ADC_R2_SFT 6 +#define RT5665_STO1_ADC1R_SRC_MASK (0x1 << 5) +#define RT5665_STO1_ADC1R_SRC_SFT 5 +#define RT5665_STO1_ADC2R_SRC_MASK (0x1 << 4) +#define RT5665_STO1_ADC2R_SRC_SFT 4 +#define RT5665_STO1_ADCR_SRC_MASK (0x3 << 2) +#define RT5665_STO1_ADCR_SRC_SFT 2 +#define RT5665_STO1_DD_R_SRC_MASK (0x3) +#define RT5665_STO1_DD_R_SRC_SFT 0 + + +/* Mono1 ADC Mixer control (0x0027) */ +#define RT5665_M_MONO_ADC_L1 (0x1 << 15) +#define RT5665_M_MONO_ADC_L1_SFT 15 +#define RT5665_M_MONO_ADC_L2 (0x1 << 14) +#define RT5665_M_MONO_ADC_L2_SFT 14 +#define RT5665_MONO_ADC_L1_SRC_MASK (0x1 << 13) +#define RT5665_MONO_ADC_L1_SRC_SFT 13 +#define RT5665_MONO_ADC_L2_SRC_MASK (0x1 << 12) +#define RT5665_MONO_ADC_L2_SRC_SFT 12 +#define RT5665_MONO_ADC_L_SRC_MASK (0x3 << 10) +#define RT5665_MONO_ADC_L_SRC_SFT 10 +#define RT5665_MONO_DD_L_SRC_MASK (0x1 << 9) +#define RT5665_MONO_DD_L_SRC_SFT 9 +#define RT5665_MONO_DMIC_L_SRC_MASK (0x1 << 8) +#define RT5665_MONO_DMIC_L_SRC_SFT 8 +#define RT5665_M_MONO_ADC_R1 (0x1 << 7) +#define RT5665_M_MONO_ADC_R1_SFT 7 +#define RT5665_M_MONO_ADC_R2 (0x1 << 6) +#define RT5665_M_MONO_ADC_R2_SFT 6 +#define RT5665_MONO_ADC_R1_SRC_MASK (0x1 << 5) +#define RT5665_MONO_ADC_R1_SRC_SFT 5 +#define RT5665_MONO_ADC_R2_SRC_MASK (0x1 << 4) +#define RT5665_MONO_ADC_R2_SRC_SFT 4 +#define RT5665_MONO_ADC_R_SRC_MASK (0x3 << 2) +#define RT5665_MONO_ADC_R_SRC_SFT 2 +#define RT5665_MONO_DD_R_SRC_MASK (0x1 << 1) +#define RT5665_MONO_DD_R_SRC_SFT 1 +#define RT5665_MONO_DMIC_R_SRC_MASK 0x1 +#define RT5665_MONO_DMIC_R_SRC_SFT 0 + +/* Stereo2 ADC Mixer Control (0x0028) */ +#define RT5665_M_STO2_ADC_L1 (0x1 << 15) +#define RT5665_M_STO2_ADC_L1_UN (0x0 << 15) +#define RT5665_M_STO2_ADC_L1_SFT 15 +#define RT5665_M_STO2_ADC_L2 (0x1 << 14) +#define RT5665_M_STO2_ADC_L2_SFT 14 +#define RT5665_STO2_ADC1L_SRC_MASK (0x1 << 13) +#define RT5665_STO2_ADC1L_SRC_SFT 13 +#define RT5665_STO2_ADC1_SRC_ADC (0x1 << 13) +#define RT5665_STO2_ADC1_SRC_DACMIX (0x0 << 13) +#define RT5665_STO2_ADC2L_SRC_MASK (0x1 << 12) +#define RT5665_STO2_ADC2L_SRC_SFT 12 +#define RT5665_STO2_ADCL_SRC_MASK (0x3 << 10) +#define RT5665_STO2_ADCL_SRC_SFT 10 +#define RT5665_STO2_DD_L_SRC_MASK (0x1 << 9) +#define RT5665_STO2_DD_L_SRC_SFT 9 +#define RT5665_STO2_DMIC_SRC_MASK (0x1 << 8) +#define RT5665_STO2_DMIC_SRC_SFT 8 +#define RT5665_STO2_DMIC_SRC_DMIC2 (0x1 << 8) +#define RT5665_STO2_DMIC_SRC_DMIC1 (0x0 << 8) +#define RT5665_M_STO2_ADC_R1 (0x1 << 7) +#define RT5665_M_STO2_ADC_R1_UN (0x0 << 7) +#define RT5665_M_STO2_ADC_R1_SFT 7 +#define RT5665_M_STO2_ADC_R2 (0x1 << 6) +#define RT5665_M_STO2_ADC_R2_SFT 6 +#define RT5665_STO2_ADC1R_SRC_MASK (0x1 << 5) +#define RT5665_STO2_ADC1R_SRC_SFT 5 +#define RT5665_STO2_ADC2R_SRC_MASK (0x1 << 4) +#define RT5665_STO2_ADC2R_SRC_SFT 4 +#define RT5665_STO2_ADCR_SRC_MASK (0x3 << 2) +#define RT5665_STO2_ADCR_SRC_SFT 2 +#define RT5665_STO2_DD_R_SRC_MASK (0x1 << 1) +#define RT5665_STO2_DD_R_SRC_SFT 1 + +/* ADC Mixer to DAC Mixer Control (0x0029) */ +#define RT5665_M_ADCMIX_L (0x1 << 15) +#define RT5665_M_ADCMIX_L_SFT 15 +#define RT5665_M_DAC1_L (0x1 << 14) +#define RT5665_M_DAC1_L_SFT 14 +#define RT5665_DAC1_R_SEL_MASK (0x3 << 10) +#define RT5665_DAC1_R_SEL_SFT 10 +#define RT5665_DAC1_L_SEL_MASK (0x3 << 8) +#define RT5665_DAC1_L_SEL_SFT 8 +#define RT5665_M_ADCMIX_R (0x1 << 7) +#define RT5665_M_ADCMIX_R_SFT 7 +#define RT5665_M_DAC1_R (0x1 << 6) +#define RT5665_M_DAC1_R_SFT 6 + +/* Stereo1 DAC Mixer Control (0x002a) */ +#define RT5665_M_DAC_L1_STO_L (0x1 << 15) +#define RT5665_M_DAC_L1_STO_L_SFT 15 +#define RT5665_G_DAC_L1_STO_L_MASK (0x1 << 14) +#define RT5665_G_DAC_L1_STO_L_SFT 14 +#define RT5665_M_DAC_R1_STO_L (0x1 << 13) +#define RT5665_M_DAC_R1_STO_L_SFT 13 +#define RT5665_G_DAC_R1_STO_L_MASK (0x1 << 12) +#define RT5665_G_DAC_R1_STO_L_SFT 12 +#define RT5665_M_DAC_L2_STO_L (0x1 << 11) +#define RT5665_M_DAC_L2_STO_L_SFT 11 +#define RT5665_G_DAC_L2_STO_L_MASK (0x1 << 10) +#define RT5665_G_DAC_L2_STO_L_SFT 10 +#define RT5665_M_DAC_R2_STO_L (0x1 << 9) +#define RT5665_M_DAC_R2_STO_L_SFT 9 +#define RT5665_G_DAC_R2_STO_L_MASK (0x1 << 8) +#define RT5665_G_DAC_R2_STO_L_SFT 8 +#define RT5665_M_DAC_L1_STO_R (0x1 << 7) +#define RT5665_M_DAC_L1_STO_R_SFT 7 +#define RT5665_G_DAC_L1_STO_R_MASK (0x1 << 6) +#define RT5665_G_DAC_L1_STO_R_SFT 6 +#define RT5665_M_DAC_R1_STO_R (0x1 << 5) +#define RT5665_M_DAC_R1_STO_R_SFT 5 +#define RT5665_G_DAC_R1_STO_R_MASK (0x1 << 4) +#define RT5665_G_DAC_R1_STO_R_SFT 4 +#define RT5665_M_DAC_L2_STO_R (0x1 << 3) +#define RT5665_M_DAC_L2_STO_R_SFT 3 +#define RT5665_G_DAC_L2_STO_R_MASK (0x1 << 2) +#define RT5665_G_DAC_L2_STO_R_SFT 2 +#define RT5665_M_DAC_R2_STO_R (0x1 << 1) +#define RT5665_M_DAC_R2_STO_R_SFT 1 +#define RT5665_G_DAC_R2_STO_R_MASK (0x1) +#define RT5665_G_DAC_R2_STO_R_SFT 0 + +/* Mono DAC Mixer Control (0x002b) */ +#define RT5665_M_DAC_L1_MONO_L (0x1 << 15) +#define RT5665_M_DAC_L1_MONO_L_SFT 15 +#define RT5665_G_DAC_L1_MONO_L_MASK (0x1 << 14) +#define RT5665_G_DAC_L1_MONO_L_SFT 14 +#define RT5665_M_DAC_R1_MONO_L (0x1 << 13) +#define RT5665_M_DAC_R1_MONO_L_SFT 13 +#define RT5665_G_DAC_R1_MONO_L_MASK (0x1 << 12) +#define RT5665_G_DAC_R1_MONO_L_SFT 12 +#define RT5665_M_DAC_L2_MONO_L (0x1 << 11) +#define RT5665_M_DAC_L2_MONO_L_SFT 11 +#define RT5665_G_DAC_L2_MONO_L_MASK (0x1 << 10) +#define RT5665_G_DAC_L2_MONO_L_SFT 10 +#define RT5665_M_DAC_R2_MONO_L (0x1 << 9) +#define RT5665_M_DAC_R2_MONO_L_SFT 9 +#define RT5665_G_DAC_R2_MONO_L_MASK (0x1 << 8) +#define RT5665_G_DAC_R2_MONO_L_SFT 8 +#define RT5665_M_DAC_L1_MONO_R (0x1 << 7) +#define RT5665_M_DAC_L1_MONO_R_SFT 7 +#define RT5665_G_DAC_L1_MONO_R_MASK (0x1 << 6) +#define RT5665_G_DAC_L1_MONO_R_SFT 6 +#define RT5665_M_DAC_R1_MONO_R (0x1 << 5) +#define RT5665_M_DAC_R1_MONO_R_SFT 5 +#define RT5665_G_DAC_R1_MONO_R_MASK (0x1 << 4) +#define RT5665_G_DAC_R1_MONO_R_SFT 4 +#define RT5665_M_DAC_L2_MONO_R (0x1 << 3) +#define RT5665_M_DAC_L2_MONO_R_SFT 3 +#define RT5665_G_DAC_L2_MONO_R_MASK (0x1 << 2) +#define RT5665_G_DAC_L2_MONO_R_SFT 2 +#define RT5665_M_DAC_R2_MONO_R (0x1 << 1) +#define RT5665_M_DAC_R2_MONO_R_SFT 1 +#define RT5665_G_DAC_R2_MONO_R_MASK (0x1) +#define RT5665_G_DAC_R2_MONO_R_SFT 0 + +/* Stereo2 DAC Mixer Control (0x002c) */ +#define RT5665_M_DAC_L1_STO2_L (0x1 << 15) +#define RT5665_M_DAC_L1_STO2_L_SFT 15 +#define RT5665_G_DAC_L1_STO2_L_MASK (0x1 << 14) +#define RT5665_G_DAC_L1_STO2_L_SFT 14 +#define RT5665_M_DAC_L2_STO2_L (0x1 << 13) +#define RT5665_M_DAC_L2_STO2_L_SFT 13 +#define RT5665_G_DAC_L2_STO2_L_MASK (0x1 << 12) +#define RT5665_G_DAC_L2_STO2_L_SFT 12 +#define RT5665_M_DAC_L3_STO2_L (0x1 << 11) +#define RT5665_M_DAC_L3_STO2_L_SFT 11 +#define RT5665_G_DAC_L3_STO2_L_MASK (0x1 << 10) +#define RT5665_G_DAC_L3_STO2_L_SFT 10 +#define RT5665_M_ST_DAC_L1 (0x1 << 9) +#define RT5665_M_ST_DAC_L1_SFT 9 +#define RT5665_M_ST_DAC_R1 (0x1 << 8) +#define RT5665_M_ST_DAC_R1_SFT 8 +#define RT5665_M_DAC_R1_STO2_R (0x1 << 7) +#define RT5665_M_DAC_R1_STO2_R_SFT 7 +#define RT5665_G_DAC_R1_STO2_R_MASK (0x1 << 6) +#define RT5665_G_DAC_R1_STO2_R_SFT 6 +#define RT5665_M_DAC_R2_STO2_R (0x1 << 5) +#define RT5665_M_DAC_R2_STO2_R_SFT 5 +#define RT5665_G_DAC_R2_STO2_R_MASK (0x1 << 4) +#define RT5665_G_DAC_R2_STO2_R_SFT 4 +#define RT5665_M_DAC_R3_STO2_R (0x1 << 3) +#define RT5665_M_DAC_R3_STO2_R_SFT 3 +#define RT5665_G_DAC_R3_STO2_R_MASK (0x1 << 2) +#define RT5665_G_DAC_R3_STO2_R_SFT 2 + +/* Analog DAC1 Input Source Control (0x002d) */ +#define RT5665_DAC_MIX_L_MASK (0x3 << 12) +#define RT5665_DAC_MIX_L_SFT 12 +#define RT5665_DAC_MIX_R_MASK (0x3 << 8) +#define RT5665_DAC_MIX_R_SFT 8 +#define RT5665_DAC_L1_SRC_MASK (0x3 << 4) +#define RT5665_A_DACL1_SFT 4 +#define RT5665_DAC_R1_SRC_MASK (0x3) +#define RT5665_A_DACR1_SFT 0 + +/* Analog DAC Input Source Control (0x002e) */ +#define RT5665_A_DACL2_SEL (0x1 << 4) +#define RT5665_A_DACL2_SFT 4 +#define RT5665_A_DACR2_SEL (0x1 << 0) +#define RT5665_A_DACR2_SFT 0 + +/* Digital Interface Data Control (0x002f) */ +#define RT5665_IF2_1_ADC_IN_MASK (0x7 << 12) +#define RT5665_IF2_1_ADC_IN_SFT 12 +#define RT5665_IF2_1_DAC_SEL_MASK (0x3 << 10) +#define RT5665_IF2_1_DAC_SEL_SFT 10 +#define RT5665_IF2_1_ADC_SEL_MASK (0x3 << 8) +#define RT5665_IF2_1_ADC_SEL_SFT 8 +#define RT5665_IF2_2_ADC_IN_MASK (0x7 << 4) +#define RT5665_IF2_2_ADC_IN_SFT 4 +#define RT5665_IF2_2_DAC_SEL_MASK (0x3 << 2) +#define RT5665_IF2_2_DAC_SEL_SFT 2 +#define RT5665_IF2_2_ADC_SEL_MASK (0x3 << 0) +#define RT5665_IF2_2_ADC_SEL_SFT 0 + +/* Digital Interface Data Control (0x0030) */ +#define RT5665_IF3_ADC_IN_MASK (0x7 << 4) +#define RT5665_IF3_ADC_IN_SFT 4 +#define RT5665_IF3_DAC_SEL_MASK (0x3 << 2) +#define RT5665_IF3_DAC_SEL_SFT 2 +#define RT5665_IF3_ADC_SEL_MASK (0x3 << 0) +#define RT5665_IF3_ADC_SEL_SFT 0 + +/* PDM Output Control (0x0031) */ +#define RT5665_M_PDM1_L (0x1 << 14) +#define RT5665_M_PDM1_L_SFT 14 +#define RT5665_M_PDM1_R (0x1 << 12) +#define RT5665_M_PDM1_R_SFT 12 +#define RT5665_PDM1_L_MASK (0x3 << 10) +#define RT5665_PDM1_L_SFT 10 +#define RT5665_PDM1_R_MASK (0x3 << 8) +#define RT5665_PDM1_R_SFT 8 +#define RT5665_PDM1_BUSY (0x1 << 6) +#define RT5665_PDM_PATTERN (0x1 << 5) +#define RT5665_PDM_GAIN (0x1 << 4) +#define RT5665_LRCK_PDM_PI2C (0x1 << 3) +#define RT5665_PDM_DIV_MASK (0x3) + +/*S/PDIF Output Control (0x0036) */ +#define RT5665_SPDIF_SEL_MASK (0x3 << 0) +#define RT5665_SPDIF_SEL_SFT 0 + +/* REC Left Mixer Control 2 (0x003c) */ +#define RT5665_M_CBJ_RM1_L (0x1 << 7) +#define RT5665_M_CBJ_RM1_L_SFT 7 +#define RT5665_M_BST1_RM1_L (0x1 << 5) +#define RT5665_M_BST1_RM1_L_SFT 5 +#define RT5665_M_BST2_RM1_L (0x1 << 4) +#define RT5665_M_BST2_RM1_L_SFT 4 +#define RT5665_M_BST3_RM1_L (0x1 << 3) +#define RT5665_M_BST3_RM1_L_SFT 3 +#define RT5665_M_BST4_RM1_L (0x1 << 2) +#define RT5665_M_BST4_RM1_L_SFT 2 +#define RT5665_M_INL_RM1_L (0x1 << 1) +#define RT5665_M_INL_RM1_L_SFT 1 +#define RT5665_M_INR_RM1_L (0x1) +#define RT5665_M_INR_RM1_L_SFT 0 + +/* REC Right Mixer Control 2 (0x003e) */ +#define RT5665_M_AEC_REF_RM1_R (0x1 << 7) +#define RT5665_M_AEC_REF_RM1_R_SFT 7 +#define RT5665_M_BST1_RM1_R (0x1 << 5) +#define RT5665_M_BST1_RM1_R_SFT 5 +#define RT5665_M_BST2_RM1_R (0x1 << 4) +#define RT5665_M_BST2_RM1_R_SFT 4 +#define RT5665_M_BST3_RM1_R (0x1 << 3) +#define RT5665_M_BST3_RM1_R_SFT 3 +#define RT5665_M_BST4_RM1_R (0x1 << 2) +#define RT5665_M_BST4_RM1_R_SFT 2 +#define RT5665_M_INR_RM1_R (0x1 << 1) +#define RT5665_M_INR_RM1_R_SFT 1 +#define RT5665_M_MONOVOL_RM1_R (0x1) +#define RT5665_M_MONOVOL_RM1_R_SFT 0 + +/* REC Mixer 2 Left Control 2 (0x0041) */ +#define RT5665_M_CBJ_RM2_L (0x1 << 7) +#define RT5665_M_CBJ_RM2_L_SFT 7 +#define RT5665_M_BST1_RM2_L (0x1 << 5) +#define RT5665_M_BST1_RM2_L_SFT 5 +#define RT5665_M_BST2_RM2_L (0x1 << 4) +#define RT5665_M_BST2_RM2_L_SFT 4 +#define RT5665_M_BST3_RM2_L (0x1 << 3) +#define RT5665_M_BST3_RM2_L_SFT 3 +#define RT5665_M_BST4_RM2_L (0x1 << 2) +#define RT5665_M_BST4_RM2_L_SFT 2 +#define RT5665_M_INL_RM2_L (0x1 << 1) +#define RT5665_M_INL_RM2_L_SFT 1 +#define RT5665_M_INR_RM2_L (0x1) +#define RT5665_M_INR_RM2_L_SFT 0 + +/* REC Mixer 2 Right Control 2 (0x0043) */ +#define RT5665_M_MONOVOL_RM2_R (0x1 << 7) +#define RT5665_M_MONOVOL_RM2_R_SFT 7 +#define RT5665_M_BST1_RM2_R (0x1 << 5) +#define RT5665_M_BST1_RM2_R_SFT 5 +#define RT5665_M_BST2_RM2_R (0x1 << 4) +#define RT5665_M_BST2_RM2_R_SFT 4 +#define RT5665_M_BST3_RM2_R (0x1 << 3) +#define RT5665_M_BST3_RM2_R_SFT 3 +#define RT5665_M_BST4_RM2_R (0x1 << 2) +#define RT5665_M_BST4_RM2_R_SFT 2 +#define RT5665_M_INL_RM2_R (0x1 << 1) +#define RT5665_M_INL_RM2_R_SFT 1 +#define RT5665_M_INR_RM2_R (0x1) +#define RT5665_M_INR_RM2_R_SFT 0 + +/* SPK Left Mixer Control (0x0046) */ +#define RT5665_M_BST3_SM_L (0x1 << 4) +#define RT5665_M_BST3_SM_L_SFT 4 +#define RT5665_M_IN_R_SM_L (0x1 << 3) +#define RT5665_M_IN_R_SM_L_SFT 3 +#define RT5665_M_IN_L_SM_L (0x1 << 2) +#define RT5665_M_IN_L_SM_L_SFT 2 +#define RT5665_M_BST1_SM_L (0x1 << 1) +#define RT5665_M_BST1_SM_L_SFT 1 +#define RT5665_M_DAC_L2_SM_L (0x1) +#define RT5665_M_DAC_L2_SM_L_SFT 0 + +/* SPK Right Mixer Control (0x0047) */ +#define RT5665_M_BST3_SM_R (0x1 << 4) +#define RT5665_M_BST3_SM_R_SFT 4 +#define RT5665_M_IN_R_SM_R (0x1 << 3) +#define RT5665_M_IN_R_SM_R_SFT 3 +#define RT5665_M_IN_L_SM_R (0x1 << 2) +#define RT5665_M_IN_L_SM_R_SFT 2 +#define RT5665_M_BST4_SM_R (0x1 << 1) +#define RT5665_M_BST4_SM_R_SFT 1 +#define RT5665_M_DAC_R2_SM_R (0x1) +#define RT5665_M_DAC_R2_SM_R_SFT 0 + +/* SPO Amp Input and Gain Control (0x0048) */ +#define RT5665_M_DAC_L2_SPKOMIX (0x1 << 13) +#define RT5665_M_DAC_L2_SPKOMIX_SFT 13 +#define RT5665_M_SPKVOLL_SPKOMIX (0x1 << 12) +#define RT5665_M_SPKVOLL_SPKOMIX_SFT 12 +#define RT5665_M_DAC_R2_SPKOMIX (0x1 << 9) +#define RT5665_M_DAC_R2_SPKOMIX_SFT 9 +#define RT5665_M_SPKVOLR_SPKOMIX (0x1 << 8) +#define RT5665_M_SPKVOLR_SPKOMIX_SFT 8 + +/* MONOMIX Input and Gain Control (0x004b) */ +#define RT5665_G_MONOVOL_MA (0x1 << 10) +#define RT5665_G_MONOVOL_MA_SFT 10 +#define RT5665_M_MONOVOL_MA (0x1 << 9) +#define RT5665_M_MONOVOL_MA_SFT 9 +#define RT5665_M_DAC_L2_MA (0x1 << 8) +#define RT5665_M_DAC_L2_MA_SFT 8 +#define RT5665_M_BST3_MM (0x1 << 4) +#define RT5665_M_BST3_MM_SFT 4 +#define RT5665_M_BST2_MM (0x1 << 3) +#define RT5665_M_BST2_MM_SFT 3 +#define RT5665_M_BST1_MM (0x1 << 2) +#define RT5665_M_BST1_MM_SFT 2 +#define RT5665_M_RECMIC2L_MM (0x1 << 1) +#define RT5665_M_RECMIC2L_MM_SFT 1 +#define RT5665_M_DAC_L2_MM (0x1) +#define RT5665_M_DAC_L2_MM_SFT 0 + +/* Output Left Mixer Control 1 (0x004d) */ +#define RT5665_G_BST3_OM_L_MASK (0x7 << 12) +#define RT5665_G_BST3_OM_L_SFT 12 +#define RT5665_G_BST2_OM_L_MASK (0x7 << 9) +#define RT5665_G_BST2_OM_L_SFT 9 +#define RT5665_G_BST1_OM_L_MASK (0x7 << 6) +#define RT5665_G_BST1_OM_L_SFT 6 +#define RT5665_G_IN_L_OM_L_MASK (0x7 << 3) +#define RT5665_G_IN_L_OM_L_SFT 3 +#define RT5665_G_DAC_L2_OM_L_MASK (0x7 << 0) +#define RT5665_G_DAC_L2_OM_L_SFT 0 + +/* Output Left Mixer Input Control (0x004e) */ +#define RT5665_M_BST3_OM_L (0x1 << 4) +#define RT5665_M_BST3_OM_L_SFT 4 +#define RT5665_M_BST2_OM_L (0x1 << 3) +#define RT5665_M_BST2_OM_L_SFT 3 +#define RT5665_M_BST1_OM_L (0x1 << 2) +#define RT5665_M_BST1_OM_L_SFT 2 +#define RT5665_M_IN_L_OM_L (0x1 << 1) +#define RT5665_M_IN_L_OM_L_SFT 1 +#define RT5665_M_DAC_L2_OM_L (0x1) +#define RT5665_M_DAC_L2_OM_L_SFT 0 + +/* Output Right Mixer Input Control (0x0050) */ +#define RT5665_M_BST4_OM_R (0x1 << 4) +#define RT5665_M_BST4_OM_R_SFT 4 +#define RT5665_M_BST3_OM_R (0x1 << 3) +#define RT5665_M_BST3_OM_R_SFT 3 +#define RT5665_M_BST2_OM_R (0x1 << 2) +#define RT5665_M_BST2_OM_R_SFT 2 +#define RT5665_M_IN_R_OM_R (0x1 << 1) +#define RT5665_M_IN_R_OM_R_SFT 1 +#define RT5665_M_DAC_R2_OM_R (0x1) +#define RT5665_M_DAC_R2_OM_R_SFT 0 + +/* LOUT Mixer Control (0x0052) */ +#define RT5665_M_DAC_L2_LM (0x1 << 15) +#define RT5665_M_DAC_L2_LM_SFT 15 +#define RT5665_M_DAC_R2_LM (0x1 << 14) +#define RT5665_M_DAC_R2_LM_SFT 14 +#define RT5665_M_OV_L_LM (0x1 << 13) +#define RT5665_M_OV_L_LM_SFT 13 +#define RT5665_M_OV_R_LM (0x1 << 12) +#define RT5665_M_OV_R_LM_SFT 12 +#define RT5665_LOUT_BST_SFT 11 +#define RT5665_LOUT_DF (0x1 << 11) +#define RT5665_LOUT_DF_SFT 11 + +/* Power Management for Digital 1 (0x0061) */ +#define RT5665_PWR_I2S1_1 (0x1 << 15) +#define RT5665_PWR_I2S1_1_BIT 15 +#define RT5665_PWR_I2S1_2 (0x1 << 14) +#define RT5665_PWR_I2S1_2_BIT 14 +#define RT5665_PWR_I2S2_1 (0x1 << 13) +#define RT5665_PWR_I2S2_1_BIT 13 +#define RT5665_PWR_I2S2_2 (0x1 << 12) +#define RT5665_PWR_I2S2_2_BIT 12 +#define RT5665_PWR_DAC_L1 (0x1 << 11) +#define RT5665_PWR_DAC_L1_BIT 11 +#define RT5665_PWR_DAC_R1 (0x1 << 10) +#define RT5665_PWR_DAC_R1_BIT 10 +#define RT5665_PWR_I2S3 (0x1 << 9) +#define RT5665_PWR_I2S3_BIT 9 +#define RT5665_PWR_LDO (0x1 << 8) +#define RT5665_PWR_LDO_BIT 8 +#define RT5665_PWR_DAC_L2 (0x1 << 7) +#define RT5665_PWR_DAC_L2_BIT 7 +#define RT5665_PWR_DAC_R2 (0x1 << 6) +#define RT5665_PWR_DAC_R2_BIT 6 +#define RT5665_PWR_ADC_L1 (0x1 << 4) +#define RT5665_PWR_ADC_L1_BIT 4 +#define RT5665_PWR_ADC_R1 (0x1 << 3) +#define RT5665_PWR_ADC_R1_BIT 3 +#define RT5665_PWR_ADC_L2 (0x1 << 2) +#define RT5665_PWR_ADC_L2_BIT 2 +#define RT5665_PWR_ADC_R2 (0x1 << 1) +#define RT5665_PWR_ADC_R2_BIT 1 + +/* Power Management for Digital 2 (0x0062) */ +#define RT5665_PWR_ADC_S1F (0x1 << 15) +#define RT5665_PWR_ADC_S1F_BIT 15 +#define RT5665_PWR_ADC_S2F (0x1 << 14) +#define RT5665_PWR_ADC_S2F_BIT 14 +#define RT5665_PWR_ADC_MF_L (0x1 << 13) +#define RT5665_PWR_ADC_MF_L_BIT 13 +#define RT5665_PWR_ADC_MF_R (0x1 << 12) +#define RT5665_PWR_ADC_MF_R_BIT 12 +#define RT5665_PWR_DAC_S2F (0x1 << 11) +#define RT5665_PWR_DAC_S2F_BIT 11 +#define RT5665_PWR_DAC_S1F (0x1 << 10) +#define RT5665_PWR_DAC_S1F_BIT 10 +#define RT5665_PWR_DAC_MF_L (0x1 << 9) +#define RT5665_PWR_DAC_MF_L_BIT 9 +#define RT5665_PWR_DAC_MF_R (0x1 << 8) +#define RT5665_PWR_DAC_MF_R_BIT 8 +#define RT5665_PWR_PDM1 (0x1 << 7) +#define RT5665_PWR_PDM1_BIT 7 + +/* Power Management for Analog 1 (0x0063) */ +#define RT5665_PWR_VREF1 (0x1 << 15) +#define RT5665_PWR_VREF1_BIT 15 +#define RT5665_PWR_FV1 (0x1 << 14) +#define RT5665_PWR_FV1_BIT 14 +#define RT5665_PWR_VREF2 (0x1 << 13) +#define RT5665_PWR_VREF2_BIT 13 +#define RT5665_PWR_FV2 (0x1 << 12) +#define RT5665_PWR_FV2_BIT 12 +#define RT5665_PWR_VREF3 (0x1 << 11) +#define RT5665_PWR_VREF3_BIT 11 +#define RT5665_PWR_FV3 (0x1 << 10) +#define RT5665_PWR_FV3_BIT 10 +#define RT5665_PWR_MB (0x1 << 9) +#define RT5665_PWR_MB_BIT 9 +#define RT5665_PWR_LM (0x1 << 8) +#define RT5665_PWR_LM_BIT 8 +#define RT5665_PWR_BG (0x1 << 7) +#define RT5665_PWR_BG_BIT 7 +#define RT5665_PWR_MA (0x1 << 6) +#define RT5665_PWR_MA_BIT 6 +#define RT5665_PWR_HA_L (0x1 << 5) +#define RT5665_PWR_HA_L_BIT 5 +#define RT5665_PWR_HA_R (0x1 << 4) +#define RT5665_PWR_HA_R_BIT 4 +#define RT5665_HP_DRIVER_MASK (0x3 << 2) +#define RT5665_HP_DRIVER_1X (0x0 << 2) +#define RT5665_HP_DRIVER_3X (0x1 << 2) +#define RT5665_HP_DRIVER_5X (0x2 << 2) +#define RT5665_LDO1_DVO_MASK (0x3) +#define RT5665_LDO1_DVO_09 (0x0) +#define RT5665_LDO1_DVO_10 (0x1) +#define RT5665_LDO1_DVO_12 (0x2) +#define RT5665_LDO1_DVO_14 (0x3) + +/* Power Management for Analog 2 (0x0064) */ +#define RT5665_PWR_BST1 (0x1 << 15) +#define RT5665_PWR_BST1_BIT 15 +#define RT5665_PWR_BST2 (0x1 << 14) +#define RT5665_PWR_BST2_BIT 14 +#define RT5665_PWR_BST3 (0x1 << 13) +#define RT5665_PWR_BST3_BIT 13 +#define RT5665_PWR_BST4 (0x1 << 12) +#define RT5665_PWR_BST4_BIT 12 +#define RT5665_PWR_MB1 (0x1 << 11) +#define RT5665_PWR_MB1_PWR_DOWN (0x0 << 11) +#define RT5665_PWR_MB1_BIT 11 +#define RT5665_PWR_MB2 (0x1 << 10) +#define RT5665_PWR_MB2_PWR_DOWN (0x0 << 10) +#define RT5665_PWR_MB2_BIT 10 +#define RT5665_PWR_MB3 (0x1 << 9) +#define RT5665_PWR_MB3_BIT 9 +#define RT5665_PWR_BST1_P (0x1 << 7) +#define RT5665_PWR_BST1_P_BIT 7 +#define RT5665_PWR_BST2_P (0x1 << 6) +#define RT5665_PWR_BST2_P_BIT 6 +#define RT5665_PWR_BST3_P (0x1 << 5) +#define RT5665_PWR_BST3_P_BIT 5 +#define RT5665_PWR_BST4_P (0x1 << 4) +#define RT5665_PWR_BST4_P_BIT 4 +#define RT5665_PWR_JD1 (0x1 << 3) +#define RT5665_PWR_JD1_BIT 3 +#define RT5665_PWR_JD2 (0x1 << 2) +#define RT5665_PWR_JD2_BIT 2 +#define RT5665_PWR_RM1_L (0x1 << 1) +#define RT5665_PWR_RM1_L_BIT 1 +#define RT5665_PWR_RM1_R (0x1) +#define RT5665_PWR_RM1_R_BIT 0 + +/* Power Management for Analog 3 (0x0065) */ +#define RT5665_PWR_CBJ (0x1 << 9) +#define RT5665_PWR_CBJ_BIT 9 +#define RT5665_PWR_BST_L (0x1 << 8) +#define RT5665_PWR_BST_L_BIT 8 +#define RT5665_PWR_BST_R (0x1 << 7) +#define RT5665_PWR_BST_R_BIT 7 +#define RT5665_PWR_PLL (0x1 << 6) +#define RT5665_PWR_PLL_BIT 6 +#define RT5665_PWR_LDO2 (0x1 << 2) +#define RT5665_PWR_LDO2_BIT 2 +#define RT5665_PWR_SVD (0x1 << 1) +#define RT5665_PWR_SVD_BIT 1 + +/* Power Management for Mixer (0x0066) */ +#define RT5665_PWR_RM2_L (0x1 << 15) +#define RT5665_PWR_RM2_L_BIT 15 +#define RT5665_PWR_RM2_R (0x1 << 14) +#define RT5665_PWR_RM2_R_BIT 14 +#define RT5665_PWR_OM_L (0x1 << 13) +#define RT5665_PWR_OM_L_BIT 13 +#define RT5665_PWR_OM_R (0x1 << 12) +#define RT5665_PWR_OM_R_BIT 12 +#define RT5665_PWR_MM (0x1 << 11) +#define RT5665_PWR_MM_BIT 11 +#define RT5665_PWR_AEC_REF (0x1 << 6) +#define RT5665_PWR_AEC_REF_BIT 6 +#define RT5665_PWR_STO1_DAC_L (0x1 << 5) +#define RT5665_PWR_STO1_DAC_L_BIT 5 +#define RT5665_PWR_STO1_DAC_R (0x1 << 4) +#define RT5665_PWR_STO1_DAC_R_BIT 4 +#define RT5665_PWR_MONO_DAC_L (0x1 << 3) +#define RT5665_PWR_MONO_DAC_L_BIT 3 +#define RT5665_PWR_MONO_DAC_R (0x1 << 2) +#define RT5665_PWR_MONO_DAC_R_BIT 2 +#define RT5665_PWR_STO2_DAC_L (0x1 << 1) +#define RT5665_PWR_STO2_DAC_L_BIT 1 +#define RT5665_PWR_STO2_DAC_R (0x1) +#define RT5665_PWR_STO2_DAC_R_BIT 0 + +/* Power Management for Volume (0x0067) */ +#define RT5665_PWR_OV_L (0x1 << 13) +#define RT5665_PWR_OV_L_BIT 13 +#define RT5665_PWR_OV_R (0x1 << 12) +#define RT5665_PWR_OV_R_BIT 12 +#define RT5665_PWR_IN_L (0x1 << 9) +#define RT5665_PWR_IN_L_BIT 9 +#define RT5665_PWR_IN_R (0x1 << 8) +#define RT5665_PWR_IN_R_BIT 8 +#define RT5665_PWR_MV (0x1 << 7) +#define RT5665_PWR_MV_BIT 7 +#define RT5665_PWR_MIC_DET (0x1 << 5) +#define RT5665_PWR_MIC_DET_BIT 5 + +/* (0x006b) */ +#define RT5665_SYS_CLK_DET 15 +#define RT5665_HP_CLK_DET 14 +#define RT5665_MONO_CLK_DET 13 +#define RT5665_LOUT_CLK_DET 12 +#define RT5665_POW_CLK_DET 0 + +/* Digital Microphone Control 1 (0x006e) */ +#define RT5665_DMIC_1_EN_MASK (0x1 << 15) +#define RT5665_DMIC_1_EN_SFT 15 +#define RT5665_DMIC_1_DIS (0x0 << 15) +#define RT5665_DMIC_1_EN (0x1 << 15) +#define RT5665_DMIC_2_EN_MASK (0x1 << 14) +#define RT5665_DMIC_2_EN_SFT 14 +#define RT5665_DMIC_2_DIS (0x0 << 14) +#define RT5665_DMIC_2_EN (0x1 << 14) +#define RT5665_DMIC_2_DP_MASK (0x1 << 9) +#define RT5665_DMIC_2_DP_SFT 9 +#define RT5665_DMIC_2_DP_GPIO5 (0x0 << 9) +#define RT5665_DMIC_2_DP_IN2P (0x1 << 9) +#define RT5665_DMIC_CLK_MASK (0x7 << 5) +#define RT5665_DMIC_CLK_SFT 5 +#define RT5665_DMIC_1_DP_MASK (0x1 << 1) +#define RT5665_DMIC_1_DP_SFT 1 +#define RT5665_DMIC_1_DP_GPIO4 (0x0 << 1) +#define RT5665_DMIC_1_DP_IN2N (0x1 << 1) + + +/* Digital Microphone Control 1 (0x006f) */ +#define RT5665_DMIC_2L_LH_MASK (0x1 << 3) +#define RT5665_DMIC_2L_LH_SFT 3 +#define RT5665_DMIC_2L_LH_RISING (0x0 << 3) +#define RT5665_DMIC_2L_LH_FALLING (0x1 << 3) +#define RT5665_DMIC_2R_LH_MASK (0x1 << 2) +#define RT5665_DMIC_2R_LH_SFT 2 +#define RT5665_DMIC_2R_LH_RISING (0x0 << 2) +#define RT5665_DMIC_2R_LH_FALLING (0x1 << 2) +#define RT5665_DMIC_1L_LH_MASK (0x1 << 1) +#define RT5665_DMIC_1L_LH_SFT 1 +#define RT5665_DMIC_1L_LH_RISING (0x0 << 1) +#define RT5665_DMIC_1L_LH_FALLING (0x1 << 1) +#define RT5665_DMIC_1R_LH_MASK (0x1 << 0) +#define RT5665_DMIC_1R_LH_SFT 0 +#define RT5665_DMIC_1R_LH_RISING (0x0) +#define RT5665_DMIC_1R_LH_FALLING (0x1) + +/* I2S1/2/3 Audio Serial Data Port Control (0x0070 0x0071 0x0072) */ +#define RT5665_I2S_MS_MASK (0x1 << 15) +#define RT5665_I2S_MS_SFT 15 +#define RT5665_I2S_MS_M (0x0 << 15) +#define RT5665_I2S_MS_S (0x1 << 15) +#define RT5665_I2S_PIN_CFG_MASK (0x1 << 14) +#define RT5665_I2S_PIN_CFG_SFT 14 +#define RT5665_I2S_CLK_SEL_MASK (0x1 << 11) +#define RT5665_I2S_CLK_SEL_SFT 11 +#define RT5665_I2S_BP_MASK (0x1 << 8) +#define RT5665_I2S_BP_SFT 8 +#define RT5665_I2S_BP_NOR (0x0 << 8) +#define RT5665_I2S_BP_INV (0x1 << 8) +#define RT5665_I2S_DL_MASK (0x3 << 4) +#define RT5665_I2S_DL_SFT 4 +#define RT5665_I2S_DL_16 (0x0 << 4) +#define RT5665_I2S_DL_20 (0x1 << 4) +#define RT5665_I2S_DL_24 (0x2 << 4) +#define RT5665_I2S_DL_8 (0x3 << 4) +#define RT5665_I2S_DF_MASK (0x7) +#define RT5665_I2S_DF_SFT 0 +#define RT5665_I2S_DF_I2S (0x0) +#define RT5665_I2S_DF_LEFT (0x1) +#define RT5665_I2S_DF_PCM_A (0x2) +#define RT5665_I2S_DF_PCM_B (0x3) +#define RT5665_I2S_DF_PCM_A_N (0x6) +#define RT5665_I2S_DF_PCM_B_N (0x7) + +/* ADC/DAC Clock Control 1 (0x0073) */ +#define RT5665_I2S_PD1_MASK (0x7 << 12) +#define RT5665_I2S_PD1_SFT 12 +#define RT5665_I2S_PD1_1 (0x0 << 12) +#define RT5665_I2S_PD1_2 (0x1 << 12) +#define RT5665_I2S_PD1_3 (0x2 << 12) +#define RT5665_I2S_PD1_4 (0x3 << 12) +#define RT5665_I2S_PD1_6 (0x4 << 12) +#define RT5665_I2S_PD1_8 (0x5 << 12) +#define RT5665_I2S_PD1_12 (0x6 << 12) +#define RT5665_I2S_PD1_16 (0x7 << 12) +#define RT5665_I2S_M_PD2_MASK (0x7 << 8) +#define RT5665_I2S_M_PD2_SFT 8 +#define RT5665_I2S_M_PD2_1 (0x0 << 8) +#define RT5665_I2S_M_PD2_2 (0x1 << 8) +#define RT5665_I2S_M_PD2_3 (0x2 << 8) +#define RT5665_I2S_M_PD2_4 (0x3 << 8) +#define RT5665_I2S_M_PD2_6 (0x4 << 8) +#define RT5665_I2S_M_PD2_8 (0x5 << 8) +#define RT5665_I2S_M_PD2_12 (0x6 << 8) +#define RT5665_I2S_M_PD2_16 (0x7 << 8) +#define RT5665_I2S_CLK_SRC_MASK (0x3 << 4) +#define RT5665_I2S_CLK_SRC_SFT 4 +#define RT5665_I2S_CLK_SRC_MCLK (0x0 << 4) +#define RT5665_I2S_CLK_SRC_PLL1 (0x1 << 4) +#define RT5665_I2S_CLK_SRC_RCCLK (0x2 << 4) +#define RT5665_DAC_OSR_MASK (0x3 << 2) +#define RT5665_DAC_OSR_SFT 2 +#define RT5665_DAC_OSR_128 (0x0 << 2) +#define RT5665_DAC_OSR_64 (0x1 << 2) +#define RT5665_DAC_OSR_32 (0x2 << 2) +#define RT5665_ADC_OSR_MASK (0x3) +#define RT5665_ADC_OSR_SFT 0 +#define RT5665_ADC_OSR_128 (0x0) +#define RT5665_ADC_OSR_64 (0x1) +#define RT5665_ADC_OSR_32 (0x2) + +/* ADC/DAC Clock Control 2 (0x0074) */ +#define RT5665_I2S_BCLK_MS2_MASK (0x1 << 15) +#define RT5665_I2S_BCLK_MS2_SFT 15 +#define RT5665_I2S_BCLK_MS2_32 (0x0 << 15) +#define RT5665_I2S_BCLK_MS2_64 (0x1 << 15) +#define RT5665_I2S_PD2_MASK (0x7 << 12) +#define RT5665_I2S_PD2_SFT 12 +#define RT5665_I2S_PD2_1 (0x0 << 12) +#define RT5665_I2S_PD2_2 (0x1 << 12) +#define RT5665_I2S_PD2_3 (0x2 << 12) +#define RT5665_I2S_PD2_4 (0x3 << 12) +#define RT5665_I2S_PD2_6 (0x4 << 12) +#define RT5665_I2S_PD2_8 (0x5 << 12) +#define RT5665_I2S_PD2_12 (0x6 << 12) +#define RT5665_I2S_PD2_16 (0x7 << 12) +#define RT5665_I2S_BCLK_MS3_MASK (0x1 << 11) +#define RT5665_I2S_BCLK_MS3_SFT 11 +#define RT5665_I2S_BCLK_MS3_32 (0x0 << 11) +#define RT5665_I2S_BCLK_MS3_64 (0x1 << 11) +#define RT5665_I2S_PD3_MASK (0x7 << 8) +#define RT5665_I2S_PD3_SFT 8 +#define RT5665_I2S_PD3_1 (0x0 << 8) +#define RT5665_I2S_PD3_2 (0x1 << 8) +#define RT5665_I2S_PD3_3 (0x2 << 8) +#define RT5665_I2S_PD3_4 (0x3 << 8) +#define RT5665_I2S_PD3_6 (0x4 << 8) +#define RT5665_I2S_PD3_8 (0x5 << 8) +#define RT5665_I2S_PD3_12 (0x6 << 8) +#define RT5665_I2S_PD3_16 (0x7 << 8) +#define RT5665_I2S_PD4_MASK (0x7 << 4) +#define RT5665_I2S_PD4_SFT 4 +#define RT5665_I2S_PD4_1 (0x0 << 4) +#define RT5665_I2S_PD4_2 (0x1 << 4) +#define RT5665_I2S_PD4_3 (0x2 << 4) +#define RT5665_I2S_PD4_4 (0x3 << 4) +#define RT5665_I2S_PD4_6 (0x4 << 4) +#define RT5665_I2S_PD4_8 (0x5 << 4) +#define RT5665_I2S_PD4_12 (0x6 << 4) +#define RT5665_I2S_PD4_16 (0x7 << 4) + +/* TDM control 1 (0x0078) */ +#define RT5665_I2S1_MODE_MASK (0x1 << 15) +#define RT5665_I2S1_MODE_I2S (0x0 << 15) +#define RT5665_I2S1_MODE_TDM (0x1 << 15) +#define RT5665_TDM_IN_CH_MASK (0x3 << 10) +#define RT5665_TDM_IN_CH_2 (0x0 << 10) +#define RT5665_TDM_IN_CH_4 (0x1 << 10) +#define RT5665_TDM_IN_CH_6 (0x2 << 10) +#define RT5665_TDM_IN_CH_8 (0x3 << 10) +#define RT5665_TDM_OUT_CH_MASK (0x3 << 8) +#define RT5665_TDM_OUT_CH_2 (0x0 << 8) +#define RT5665_TDM_OUT_CH_4 (0x1 << 8) +#define RT5665_TDM_OUT_CH_6 (0x2 << 8) +#define RT5665_TDM_OUT_CH_8 (0x3 << 8) +#define RT5665_TDM_IN_LEN_MASK (0x3 << 6) +#define RT5665_TDM_IN_LEN_16 (0x0 << 6) +#define RT5665_TDM_IN_LEN_20 (0x1 << 6) +#define RT5665_TDM_IN_LEN_24 (0x2 << 6) +#define RT5665_TDM_IN_LEN_32 (0x3 << 6) +#define RT5665_TDM_OUT_LEN_MASK (0x3 << 4) +#define RT5665_TDM_OUT_LEN_16 (0x0 << 4) +#define RT5665_TDM_OUT_LEN_20 (0x1 << 4) +#define RT5665_TDM_OUT_LEN_24 (0x2 << 4) +#define RT5665_TDM_OUT_LEN_32 (0x3 << 4) + + +/* TDM control 2 (0x0079) */ +#define RT5665_I2S1_1_DS_ADC_SLOT01_SFT 14 +#define RT5665_I2S1_1_DS_ADC_SLOT23_SFT 12 +#define RT5665_I2S1_1_DS_ADC_SLOT45_SFT 10 +#define RT5665_I2S1_1_DS_ADC_SLOT67_SFT 8 +#define RT5665_I2S1_2_DS_ADC_SLOT01_SFT 6 +#define RT5665_I2S1_2_DS_ADC_SLOT23_SFT 4 +#define RT5665_I2S1_2_DS_ADC_SLOT45_SFT 2 +#define RT5665_I2S1_2_DS_ADC_SLOT67_SFT 0 + +/* TDM control 3/4 (0x007a) (0x007b) */ +#define RT5665_IF1_ADC1_SEL_SFT 10 +#define RT5665_IF1_ADC2_SEL_SFT 9 +#define RT5665_IF1_ADC3_SEL_SFT 8 +#define RT5665_IF1_ADC4_SEL_SFT 7 +#define RT5665_TDM_ADC_SEL_SFT 0 +#define RT5665_TDM_ADC_CTRL_MASK (0x1f << 0) +#define RT5665_TDM_ADC_DATA_06 (0x6 << 0) + +/* Global Clock Control (0x0080) */ +#define RT5665_SCLK_SRC_MASK (0x3 << 14) +#define RT5665_SCLK_SRC_SFT 14 +#define RT5665_SCLK_SRC_MCLK (0x0 << 14) +#define RT5665_SCLK_SRC_PLL1 (0x1 << 14) +#define RT5665_SCLK_SRC_RCCLK (0x2 << 14) +#define RT5665_PLL1_SRC_MASK (0x7 << 8) +#define RT5665_PLL1_SRC_SFT 8 +#define RT5665_PLL1_SRC_MCLK (0x0 << 8) +#define RT5665_PLL1_SRC_BCLK1 (0x1 << 8) +#define RT5665_PLL1_SRC_BCLK2 (0x2 << 8) +#define RT5665_PLL1_SRC_BCLK3 (0x3 << 8) +#define RT5665_PLL1_PD_MASK (0x7 << 4) +#define RT5665_PLL1_PD_SFT 4 + + +#define RT5665_PLL_INP_MAX 40000000 +#define RT5665_PLL_INP_MIN 256000 +/* PLL M/N/K Code Control 1 (0x0081) */ +#define RT5665_PLL_N_MAX 0x001ff +#define RT5665_PLL_N_MASK (RT5665_PLL_N_MAX << 7) +#define RT5665_PLL_N_SFT 7 +#define RT5665_PLL_K_MAX 0x001f +#define RT5665_PLL_K_MASK (RT5665_PLL_K_MAX) +#define RT5665_PLL_K_SFT 0 + +/* PLL M/N/K Code Control 2 (0x0082) */ +#define RT5665_PLL_M_MAX 0x00f +#define RT5665_PLL_M_MASK (RT5665_PLL_M_MAX << 12) +#define RT5665_PLL_M_SFT 12 +#define RT5665_PLL_M_BP (0x1 << 11) +#define RT5665_PLL_M_BP_SFT 11 +#define RT5665_PLL_K_BP (0x1 << 10) +#define RT5665_PLL_K_BP_SFT 10 + +/* PLL tracking mode 1 (0x0083) */ +#define RT5665_I2S3_ASRC_MASK (0x1 << 15) +#define RT5665_I2S3_ASRC_SFT 15 +#define RT5665_I2S2_ASRC_MASK (0x1 << 14) +#define RT5665_I2S2_ASRC_SFT 14 +#define RT5665_I2S1_ASRC_MASK (0x1 << 13) +#define RT5665_I2S1_ASRC_SFT 13 +#define RT5665_DAC_STO1_ASRC_MASK (0x1 << 12) +#define RT5665_DAC_STO1_ASRC_SFT 12 +#define RT5665_DAC_STO2_ASRC_MASK (0x1 << 11) +#define RT5665_DAC_STO2_ASRC_SFT 11 +#define RT5665_DAC_MONO_L_ASRC_MASK (0x1 << 10) +#define RT5665_DAC_MONO_L_ASRC_SFT 10 +#define RT5665_DAC_MONO_R_ASRC_MASK (0x1 << 9) +#define RT5665_DAC_MONO_R_ASRC_SFT 9 +#define RT5665_DMIC_STO1_ASRC_MASK (0x1 << 8) +#define RT5665_DMIC_STO1_ASRC_SFT 8 +#define RT5665_DMIC_STO2_ASRC_MASK (0x1 << 7) +#define RT5665_DMIC_STO2_ASRC_SFT 7 +#define RT5665_DMIC_MONO_L_ASRC_MASK (0x1 << 6) +#define RT5665_DMIC_MONO_L_ASRC_SFT 6 +#define RT5665_DMIC_MONO_R_ASRC_MASK (0x1 << 5) +#define RT5665_DMIC_MONO_R_ASRC_SFT 5 +#define RT5665_ADC_STO1_ASRC_MASK (0x1 << 4) +#define RT5665_ADC_STO1_ASRC_SFT 4 +#define RT5665_ADC_STO2_ASRC_MASK (0x1 << 3) +#define RT5665_ADC_STO2_ASRC_SFT 3 +#define RT5665_ADC_MONO_L_ASRC_MASK (0x1 << 2) +#define RT5665_ADC_MONO_L_ASRC_SFT 2 +#define RT5665_ADC_MONO_R_ASRC_MASK (0x1 << 1) +#define RT5665_ADC_MONO_R_ASRC_SFT 1 + +/* PLL tracking mode 2 (0x0084)*/ +#define RT5665_DA_STO1_CLK_SEL_MASK (0x7 << 12) +#define RT5665_DA_STO1_CLK_SEL_SFT 12 +#define RT5665_DA_STO2_CLK_SEL_MASK (0x7 << 8) +#define RT5665_DA_STO2_CLK_SEL_SFT 8 +#define RT5665_DA_MONOL_CLK_SEL_MASK (0x7 << 4) +#define RT5665_DA_MONOL_CLK_SEL_SFT 4 +#define RT5665_DA_MONOR_CLK_SEL_MASK (0x7) +#define RT5665_DA_MONOR_CLK_SEL_SFT 0 + +/* PLL tracking mode 3 (0x0085)*/ +#define RT5665_AD_STO1_CLK_SEL_MASK (0x7 << 12) +#define RT5665_AD_STO1_CLK_SEL_SFT 12 +#define RT5665_AD_STO2_CLK_SEL_MASK (0x7 << 8) +#define RT5665_AD_STO2_CLK_SEL_SFT 8 +#define RT5665_AD_MONOL_CLK_SEL_MASK (0x7 << 4) +#define RT5665_AD_MONOL_CLK_SEL_SFT 4 +#define RT5665_AD_MONOR_CLK_SEL_MASK (0x7) +#define RT5665_AD_MONOR_CLK_SEL_SFT 0 + +/* ASRC Control 4 (0x0086) */ +#define RT5665_I2S1_RATE_MASK (0xf << 12) +#define RT5665_I2S1_RATE_SFT 12 +#define RT5665_I2S2_RATE_MASK (0xf << 8) +#define RT5665_I2S2_RATE_SFT 8 +#define RT5665_I2S3_RATE_MASK (0xf << 4) +#define RT5665_I2S3_RATE_SFT 4 + +/* Depop Mode Control 1 (0x008e) */ +#define RT5665_PUMP_EN (0x1 << 3) + +/* Depop Mode Control 2 (0x8f) */ +#define RT5665_DEPOP_MASK (0x1 << 13) +#define RT5665_DEPOP_SFT 13 +#define RT5665_DEPOP_AUTO (0x0 << 13) +#define RT5665_DEPOP_MAN (0x1 << 13) +#define RT5665_RAMP_MASK (0x1 << 12) +#define RT5665_RAMP_SFT 12 +#define RT5665_RAMP_DIS (0x0 << 12) +#define RT5665_RAMP_EN (0x1 << 12) +#define RT5665_BPS_MASK (0x1 << 11) +#define RT5665_BPS_SFT 11 +#define RT5665_BPS_DIS (0x0 << 11) +#define RT5665_BPS_EN (0x1 << 11) +#define RT5665_FAST_UPDN_MASK (0x1 << 10) +#define RT5665_FAST_UPDN_SFT 10 +#define RT5665_FAST_UPDN_DIS (0x0 << 10) +#define RT5665_FAST_UPDN_EN (0x1 << 10) +#define RT5665_MRES_MASK (0x3 << 8) +#define RT5665_MRES_SFT 8 +#define RT5665_MRES_15MO (0x0 << 8) +#define RT5665_MRES_25MO (0x1 << 8) +#define RT5665_MRES_35MO (0x2 << 8) +#define RT5665_MRES_45MO (0x3 << 8) +#define RT5665_VLO_MASK (0x1 << 7) +#define RT5665_VLO_SFT 7 +#define RT5665_VLO_3V (0x0 << 7) +#define RT5665_VLO_32V (0x1 << 7) +#define RT5665_DIG_DP_MASK (0x1 << 6) +#define RT5665_DIG_DP_SFT 6 +#define RT5665_DIG_DP_DIS (0x0 << 6) +#define RT5665_DIG_DP_EN (0x1 << 6) +#define RT5665_DP_TH_MASK (0x3 << 4) +#define RT5665_DP_TH_SFT 4 + +/* Depop Mode Control 3 (0x90) */ +#define RT5665_CP_SYS_MASK (0x7 << 12) +#define RT5665_CP_SYS_SFT 12 +#define RT5665_CP_FQ1_MASK (0x7 << 8) +#define RT5665_CP_FQ1_SFT 8 +#define RT5665_CP_FQ2_MASK (0x7 << 4) +#define RT5665_CP_FQ2_SFT 4 +#define RT5665_CP_FQ3_MASK (0x7) +#define RT5665_CP_FQ3_SFT 0 +#define RT5665_CP_FQ_1_5_KHZ 0 +#define RT5665_CP_FQ_3_KHZ 1 +#define RT5665_CP_FQ_6_KHZ 2 +#define RT5665_CP_FQ_12_KHZ 3 +#define RT5665_CP_FQ_24_KHZ 4 +#define RT5665_CP_FQ_48_KHZ 5 +#define RT5665_CP_FQ_96_KHZ 6 +#define RT5665_CP_FQ_192_KHZ 7 + +/* HPOUT charge pump 1 (0x0091) */ +#define RT5665_OSW_L_MASK (0x1 << 11) +#define RT5665_OSW_L_SFT 11 +#define RT5665_OSW_L_DIS (0x0 << 11) +#define RT5665_OSW_L_EN (0x1 << 11) +#define RT5665_OSW_R_MASK (0x1 << 10) +#define RT5665_OSW_R_SFT 10 +#define RT5665_OSW_R_DIS (0x0 << 10) +#define RT5665_OSW_R_EN (0x1 << 10) +#define RT5665_PM_HP_MASK (0x3 << 8) +#define RT5665_PM_HP_SFT 8 +#define RT5665_PM_HP_LV (0x0 << 8) +#define RT5665_PM_HP_MV (0x1 << 8) +#define RT5665_PM_HP_HV (0x2 << 8) +#define RT5665_IB_HP_MASK (0x3 << 6) +#define RT5665_IB_HP_SFT 6 +#define RT5665_IB_HP_125IL (0x0 << 6) +#define RT5665_IB_HP_25IL (0x1 << 6) +#define RT5665_IB_HP_5IL (0x2 << 6) +#define RT5665_IB_HP_1IL (0x3 << 6) + +/* PV detection and SPK gain control (0x92) */ +#define RT5665_PVDD_DET_MASK (0x1 << 15) +#define RT5665_PVDD_DET_SFT 15 +#define RT5665_PVDD_DET_DIS (0x0 << 15) +#define RT5665_PVDD_DET_EN (0x1 << 15) +#define RT5665_SPK_AG_MASK (0x1 << 14) +#define RT5665_SPK_AG_SFT 14 +#define RT5665_SPK_AG_DIS (0x0 << 14) +#define RT5665_SPK_AG_EN (0x1 << 14) + +/* Micbias Control1 (0x93) */ +#define RT5665_MIC1_BS_MASK (0x1 << 15) +#define RT5665_MIC1_BS_SFT 15 +#define RT5665_MIC1_BS_9AV (0x0 << 15) +#define RT5665_MIC1_BS_75AV (0x1 << 15) +#define RT5665_MIC2_BS_MASK (0x1 << 14) +#define RT5665_MIC2_BS_SFT 14 +#define RT5665_MIC2_BS_9AV (0x0 << 14) +#define RT5665_MIC2_BS_75AV (0x1 << 14) +#define RT5665_MIC1_CLK_MASK (0x1 << 13) +#define RT5665_MIC1_CLK_SFT 13 +#define RT5665_MIC1_CLK_DIS (0x0 << 13) +#define RT5665_MIC1_CLK_EN (0x1 << 13) +#define RT5665_MIC2_CLK_MASK (0x1 << 12) +#define RT5665_MIC2_CLK_SFT 12 +#define RT5665_MIC2_CLK_DIS (0x0 << 12) +#define RT5665_MIC2_CLK_EN (0x1 << 12) +#define RT5665_MIC1_OVCD_MASK (0x1 << 11) +#define RT5665_MIC1_OVCD_SFT 11 +#define RT5665_MIC1_OVCD_DIS (0x0 << 11) +#define RT5665_MIC1_OVCD_EN (0x1 << 11) +#define RT5665_MIC1_OVTH_MASK (0x3 << 9) +#define RT5665_MIC1_OVTH_SFT 9 +#define RT5665_MIC1_OVTH_600UA (0x0 << 9) +#define RT5665_MIC1_OVTH_1500UA (0x1 << 9) +#define RT5665_MIC1_OVTH_2000UA (0x2 << 9) +#define RT5665_MIC2_OVCD_MASK (0x1 << 8) +#define RT5665_MIC2_OVCD_SFT 8 +#define RT5665_MIC2_OVCD_DIS (0x0 << 8) +#define RT5665_MIC2_OVCD_EN (0x1 << 8) +#define RT5665_MIC2_OVTH_MASK (0x3 << 6) +#define RT5665_MIC2_OVTH_SFT 6 +#define RT5665_MIC2_OVTH_600UA (0x0 << 6) +#define RT5665_MIC2_OVTH_1500UA (0x1 << 6) +#define RT5665_MIC2_OVTH_2000UA (0x2 << 6) +#define RT5665_PWR_MB_MASK (0x1 << 5) +#define RT5665_PWR_MB_SFT 5 +#define RT5665_PWR_MB_PD (0x0 << 5) +#define RT5665_PWR_MB_PU (0x1 << 5) + +/* Micbias Control2 (0x94) */ +#define RT5665_PWR_CLK25M_MASK (0x1 << 9) +#define RT5665_PWR_CLK25M_SFT 9 +#define RT5665_PWR_CLK25M_PD (0x0 << 9) +#define RT5665_PWR_CLK25M_PU (0x1 << 9) +#define RT5665_PWR_CLK1M_MASK (0x1 << 8) +#define RT5665_PWR_CLK1M_SFT 8 +#define RT5665_PWR_CLK1M_PD (0x0 << 8) +#define RT5665_PWR_CLK1M_PU (0x1 << 8) + + +/* EQ Control 1 (0x00b0) */ +#define RT5665_EQ_SRC_DAC (0x0 << 15) +#define RT5665_EQ_SRC_ADC (0x1 << 15) +#define RT5665_EQ_UPD (0x1 << 14) +#define RT5665_EQ_UPD_BIT 14 +#define RT5665_EQ_CD_MASK (0x1 << 13) +#define RT5665_EQ_CD_SFT 13 +#define RT5665_EQ_CD_DIS (0x0 << 13) +#define RT5665_EQ_CD_EN (0x1 << 13) +#define RT5665_EQ_DITH_MASK (0x3 << 8) +#define RT5665_EQ_DITH_SFT 8 +#define RT5665_EQ_DITH_NOR (0x0 << 8) +#define RT5665_EQ_DITH_LSB (0x1 << 8) +#define RT5665_EQ_DITH_LSB_1 (0x2 << 8) +#define RT5665_EQ_DITH_LSB_2 (0x3 << 8) + +/* IRQ Control 1 (0x00b7) */ +#define RT5665_JD1_1_EN_MASK (0x1 << 15) +#define RT5665_JD1_1_EN_SFT 15 +#define RT5665_JD1_1_DIS (0x0 << 15) +#define RT5665_JD1_1_EN (0x1 << 15) +#define RT5665_JD1_2_EN_MASK (0x1 << 12) +#define RT5665_JD1_2_EN_SFT 12 +#define RT5665_JD1_2_DIS (0x0 << 12) +#define RT5665_JD1_2_EN (0x1 << 12) + +/* IRQ Control 2 (0x00b8) */ +#define RT5665_IL_IRQ_MASK (0x1 << 6) +#define RT5665_IL_IRQ_DIS (0x0 << 6) +#define RT5665_IL_IRQ_EN (0x1 << 6) + +/* IRQ Control 5 (0x00ba) */ +#define RT5665_IRQ_JD_EN (0x1 << 3) +#define RT5665_IRQ_JD_EN_SFT 3 + +/* GPIO Control 1 (0x00c0) */ +#define RT5665_GP1_PIN_MASK (0x1 << 15) +#define RT5665_GP1_PIN_SFT 15 +#define RT5665_GP1_PIN_GPIO1 (0x0 << 15) +#define RT5665_GP1_PIN_IRQ (0x1 << 15) +#define RT5665_GP2_PIN_MASK (0x3 << 13) +#define RT5665_GP2_PIN_SFT 13 +#define RT5665_GP2_PIN_GPIO2 (0x0 << 13) +#define RT5665_GP2_PIN_BCLK2 (0x1 << 13) +#define RT5665_GP2_PIN_PDM_SCL (0x2 << 13) +#define RT5665_GP3_PIN_MASK (0x3 << 11) +#define RT5665_GP3_PIN_SFT 11 +#define RT5665_GP3_PIN_GPIO3 (0x0 << 11) +#define RT5665_GP3_PIN_LRCK2 (0x1 << 11) +#define RT5665_GP3_PIN_PDM_SDA (0x2 << 11) +#define RT5665_GP4_PIN_MASK (0x3 << 9) +#define RT5665_GP4_PIN_SFT 9 +#define RT5665_GP4_PIN_GPIO4 (0x0 << 9) +#define RT5665_GP4_PIN_DACDAT2_1 (0x1 << 9) +#define RT5665_GP4_PIN_DMIC1_SDA (0x2 << 9) +#define RT5665_GP5_PIN_MASK (0x3 << 7) +#define RT5665_GP5_PIN_SFT 7 +#define RT5665_GP5_PIN_GPIO5 (0x0 << 7) +#define RT5665_GP5_PIN_ADCDAT2_1 (0x1 << 7) +#define RT5665_GP5_PIN_DMIC2_SDA (0x2 << 7) +#define RT5665_GP6_PIN_MASK (0x3 << 5) +#define RT5665_GP6_PIN_SFT 5 +#define RT5665_GP6_PIN_GPIO6 (0x0 << 5) +#define RT5665_GP6_PIN_BCLK3 (0x0 << 5) +#define RT5665_GP6_PIN_PDM_SCL (0x1 << 5) +#define RT5665_GP7_PIN_MASK (0x3 << 3) +#define RT5665_GP7_PIN_SFT 3 +#define RT5665_GP7_PIN_GPIO7 (0x0 << 3) +#define RT5665_GP7_PIN_LRCK3 (0x1 << 3) +#define RT5665_GP7_PIN_PDM_SDA (0x2 << 3) +#define RT5665_GP8_PIN_MASK (0x3 << 1) +#define RT5665_GP8_PIN_SFT 1 +#define RT5665_GP8_PIN_GPIO8 (0x0 << 1) +#define RT5665_GP8_PIN_DACDAT3 (0x1 << 1) +#define RT5665_GP8_PIN_DMIC2_SCL (0x2 << 1) +#define RT5665_GP8_PIN_DACDAT2_2 (0x3 << 1) + + +/* GPIO Control 2 (0x00c1)*/ +#define RT5665_GP9_PIN_MASK (0x3 << 14) +#define RT5665_GP9_PIN_SFT 14 +#define RT5665_GP9_PIN_GPIO9 (0x0 << 14) +#define RT5665_GP9_PIN_ADCDAT3 (0x1 << 14) +#define RT5665_GP9_PIN_DMIC1_SCL (0x2 << 14) +#define RT5665_GP9_PIN_ADCDAT2_2 (0x3 << 14) +#define RT5665_GP10_PIN_MASK (0x3 << 12) +#define RT5665_GP10_PIN_SFT 12 +#define RT5665_GP10_PIN_GPIO10 (0x0 << 12) +#define RT5665_GP10_PIN_ADCDAT1_2 (0x1 << 12) +#define RT5665_GP10_PIN_LPD (0x2 << 12) +#define RT5665_GP1_PF_MASK (0x1 << 11) +#define RT5665_GP1_PF_IN (0x0 << 11) +#define RT5665_GP1_PF_OUT (0x1 << 11) +#define RT5665_GP1_OUT_MASK (0x1 << 10) +#define RT5665_GP1_OUT_H (0x0 << 10) +#define RT5665_GP1_OUT_L (0x1 << 10) +#define RT5665_GP2_PF_MASK (0x1 << 9) +#define RT5665_GP2_PF_IN (0x0 << 9) +#define RT5665_GP2_PF_OUT (0x1 << 9) +#define RT5665_GP2_OUT_MASK (0x1 << 8) +#define RT5665_GP2_OUT_H (0x0 << 8) +#define RT5665_GP2_OUT_L (0x1 << 8) +#define RT5665_GP3_PF_MASK (0x1 << 7) +#define RT5665_GP3_PF_IN (0x0 << 7) +#define RT5665_GP3_PF_OUT (0x1 << 7) +#define RT5665_GP3_OUT_MASK (0x1 << 6) +#define RT5665_GP3_OUT_H (0x0 << 6) +#define RT5665_GP3_OUT_L (0x1 << 6) +#define RT5665_GP4_PF_MASK (0x1 << 5) +#define RT5665_GP4_PF_IN (0x0 << 5) +#define RT5665_GP4_PF_OUT (0x1 << 5) +#define RT5665_GP4_OUT_MASK (0x1 << 4) +#define RT5665_GP4_OUT_H (0x0 << 4) +#define RT5665_GP4_OUT_L (0x1 << 4) +#define RT5665_GP5_PF_MASK (0x1 << 3) +#define RT5665_GP5_PF_IN (0x0 << 3) +#define RT5665_GP5_PF_OUT (0x1 << 3) +#define RT5665_GP5_OUT_MASK (0x1 << 2) +#define RT5665_GP5_OUT_H (0x0 << 2) +#define RT5665_GP5_OUT_L (0x1 << 2) +#define RT5665_GP6_PF_MASK (0x1 << 1) +#define RT5665_GP6_PF_IN (0x0 << 1) +#define RT5665_GP6_PF_OUT (0x1 << 1) +#define RT5665_GP6_OUT_MASK (0x1) +#define RT5665_GP6_OUT_H (0x0) +#define RT5665_GP6_OUT_L (0x1) + + +/* GPIO Control 3 (0x00c2) */ +#define RT5665_GP7_PF_MASK (0x1 << 15) +#define RT5665_GP7_PF_IN (0x0 << 15) +#define RT5665_GP7_PF_OUT (0x1 << 15) +#define RT5665_GP7_OUT_MASK (0x1 << 14) +#define RT5665_GP7_OUT_H (0x0 << 14) +#define RT5665_GP7_OUT_L (0x1 << 14) +#define RT5665_GP8_PF_MASK (0x1 << 13) +#define RT5665_GP8_PF_IN (0x0 << 13) +#define RT5665_GP8_PF_OUT (0x1 << 13) +#define RT5665_GP8_OUT_MASK (0x1 << 12) +#define RT5665_GP8_OUT_H (0x0 << 12) +#define RT5665_GP8_OUT_L (0x1 << 12) +#define RT5665_GP9_PF_MASK (0x1 << 11) +#define RT5665_GP9_PF_IN (0x0 << 11) +#define RT5665_GP9_PF_OUT (0x1 << 11) +#define RT5665_GP9_OUT_MASK (0x1 << 10) +#define RT5665_GP9_OUT_H (0x0 << 10) +#define RT5665_GP9_OUT_L (0x1 << 10) +#define RT5665_GP10_PF_MASK (0x1 << 9) +#define RT5665_GP10_PF_IN (0x0 << 9) +#define RT5665_GP10_PF_OUT (0x1 << 9) +#define RT5665_GP10_OUT_MASK (0x1 << 8) +#define RT5665_GP10_OUT_H (0x0 << 8) +#define RT5665_GP10_OUT_L (0x1 << 8) +#define RT5665_GP11_PF_MASK (0x1 << 7) +#define RT5665_GP11_PF_IN (0x0 << 7) +#define RT5665_GP11_PF_OUT (0x1 << 7) +#define RT5665_GP11_OUT_MASK (0x1 << 6) +#define RT5665_GP11_OUT_H (0x0 << 6) +#define RT5665_GP11_OUT_L (0x1 << 6) + +/* Soft volume and zero cross control 1 (0x00d9) */ +#define RT5665_SV_MASK (0x1 << 15) +#define RT5665_SV_SFT 15 +#define RT5665_SV_DIS (0x0 << 15) +#define RT5665_SV_EN (0x1 << 15) +#define RT5665_OUT_SV_MASK (0x1 << 13) +#define RT5665_OUT_SV_SFT 13 +#define RT5665_OUT_SV_DIS (0x0 << 13) +#define RT5665_OUT_SV_EN (0x1 << 13) +#define RT5665_HP_SV_MASK (0x1 << 12) +#define RT5665_HP_SV_SFT 12 +#define RT5665_HP_SV_DIS (0x0 << 12) +#define RT5665_HP_SV_EN (0x1 << 12) +#define RT5665_ZCD_DIG_MASK (0x1 << 11) +#define RT5665_ZCD_DIG_SFT 11 +#define RT5665_ZCD_DIG_DIS (0x0 << 11) +#define RT5665_ZCD_DIG_EN (0x1 << 11) +#define RT5665_ZCD_MASK (0x1 << 10) +#define RT5665_ZCD_SFT 10 +#define RT5665_ZCD_PD (0x0 << 10) +#define RT5665_ZCD_PU (0x1 << 10) +#define RT5665_SV_DLY_MASK (0xf) +#define RT5665_SV_DLY_SFT 0 + +/* Soft volume and zero cross control 2 (0x00da) */ +#define RT5665_ZCD_HP_MASK (0x1 << 15) +#define RT5665_ZCD_HP_SFT 15 +#define RT5665_ZCD_HP_DIS (0x0 << 15) +#define RT5665_ZCD_HP_EN (0x1 << 15) + +/* 4 Button Inline Command Control 2 (0x00e0) */ +#define RT5665_4BTN_IL_MASK (0x1 << 15) +#define RT5665_4BTN_IL_EN (0x1 << 15) +#define RT5665_4BTN_IL_DIS (0x0 << 15) +#define RT5665_4BTN_IL_RST_MASK (0x1 << 14) +#define RT5665_4BTN_IL_NOR (0x1 << 14) +#define RT5665_4BTN_IL_RST (0x0 << 14) + +/* Analog JD Control 1 (0x00f0) */ +#define RT5665_JD1_MODE_MASK (0x3 << 0) +#define RT5665_JD1_MODE_0 (0x0 << 0) +#define RT5665_JD1_MODE_1 (0x1 << 0) +#define RT5665_JD1_MODE_2 (0x2 << 0) + +/* Jack Detect Control 3 (0x00f8) */ +#define RT5665_JD_TRI_HPO_SEL_MASK (0x7) +#define RT5665_JD_TRI_HPO_SEL_SFT (0) +#define RT5665_JD_HPO_GPIO_JD1 (0x0) +#define RT5665_JD_HPO_JD1_1 (0x1) +#define RT5665_JD_HPO_JD1_2 (0x2) +#define RT5665_JD_HPO_JD2 (0x3) +#define RT5665_JD_HPO_GPIO_JD2 (0x4) +#define RT5665_JD_HPO_JD3 (0x5) +#define RT5665_JD_HPO_JD_D (0x6) + +/* Digital Misc Control (0x00fa) */ +#define RT5665_AM_MASK (0x1 << 7) +#define RT5665_AM_EN (0x1 << 7) +#define RT5665_AM_DIS (0x1 << 7) +#define RT5665_DIG_GATE_CTRL 0x1 +#define RT5665_DIG_GATE_CTRL_SFT (0) + +/* Chopper and Clock control for ADC (0x011c)*/ +#define RT5665_M_RF_DIG_MASK (0x1 << 12) +#define RT5665_M_RF_DIG_SFT 12 +#define RT5665_M_RI_DIG (0x1 << 11) + +/* Chopper and Clock control for DAC (0x013a)*/ +#define RT5665_CKXEN_DAC1_MASK (0x1 << 13) +#define RT5665_CKXEN_DAC1_SFT 13 +#define RT5665_CKGEN_DAC1_MASK (0x1 << 12) +#define RT5665_CKGEN_DAC1_SFT 12 +#define RT5665_CKXEN_DAC2_MASK (0x1 << 5) +#define RT5665_CKXEN_DAC2_SFT 5 +#define RT5665_CKGEN_DAC2_MASK (0x1 << 4) +#define RT5665_CKGEN_DAC2_SFT 4 + +/* Chopper and Clock control for ADC (0x013b)*/ +#define RT5665_CKXEN_ADC1_MASK (0x1 << 13) +#define RT5665_CKXEN_ADC1_SFT 13 +#define RT5665_CKGEN_ADC1_MASK (0x1 << 12) +#define RT5665_CKGEN_ADC1_SFT 12 +#define RT5665_CKXEN_ADC2_MASK (0x1 << 5) +#define RT5665_CKXEN_ADC2_SFT 5 +#define RT5665_CKGEN_ADC2_MASK (0x1 << 4) +#define RT5665_CKGEN_ADC2_SFT 4 + +/* Volume test (0x013f)*/ +#define RT5665_SEL_CLK_VOL_MASK (0x1 << 15) +#define RT5665_SEL_CLK_VOL_EN (0x1 << 15) +#define RT5665_SEL_CLK_VOL_DIS (0x0 << 15) + +/* Test Mode Control 1 (0x0145) */ +#define RT5665_AD2DA_LB_MASK (0x1 << 9) +#define RT5665_AD2DA_LB_SFT 9 + +/* Stereo Noise Gate Control 1 (0x0160) */ +#define RT5665_NG2_EN_MASK (0x1 << 15) +#define RT5665_NG2_EN (0x1 << 15) +#define RT5665_NG2_DIS (0x0 << 15) + +/* Stereo1 DAC Silence Detection Control (0x0190) */ +#define RT5665_DEB_STO_DAC_MASK (0x7 << 4) +#define RT5665_DEB_80_MS (0x0 << 4) + +/* SAR ADC Inline Command Control 1 (0x0210) */ +#define RT5665_SAR_BUTT_DET_MASK (0x1 << 15) +#define RT5665_SAR_BUTT_DET_EN (0x1 << 15) +#define RT5665_SAR_BUTT_DET_DIS (0x0 << 15) +#define RT5665_SAR_BUTDET_MODE_MASK (0x1 << 14) +#define RT5665_SAR_BUTDET_POW_SAV (0x1 << 14) +#define RT5665_SAR_BUTDET_POW_NORM (0x0 << 14) +#define RT5665_SAR_BUTDET_RST_MASK (0x1 << 13) +#define RT5665_SAR_BUTDET_RST_NORMAL (0x1 << 13) +#define RT5665_SAR_BUTDET_RST (0x0 << 13) +#define RT5665_SAR_POW_MASK (0x1 << 12) +#define RT5665_SAR_POW_EN (0x1 << 12) +#define RT5665_SAR_POW_DIS (0x0 << 12) +#define RT5665_SAR_RST_MASK (0x1 << 11) +#define RT5665_SAR_RST_NORMAL (0x1 << 11) +#define RT5665_SAR_RST (0x0 << 11) +#define RT5665_SAR_BYPASS_MASK (0x1 << 10) +#define RT5665_SAR_BYPASS_EN (0x1 << 10) +#define RT5665_SAR_BYPASS_DIS (0x0 << 10) +#define RT5665_SAR_SEL_MB1_MASK (0x1 << 9) +#define RT5665_SAR_SEL_MB1_SEL (0x1 << 9) +#define RT5665_SAR_SEL_MB1_NOSEL (0x0 << 9) +#define RT5665_SAR_SEL_MB2_MASK (0x1 << 8) +#define RT5665_SAR_SEL_MB2_SEL (0x1 << 8) +#define RT5665_SAR_SEL_MB2_NOSEL (0x0 << 8) +#define RT5665_SAR_SEL_MODE_MASK (0x1 << 7) +#define RT5665_SAR_SEL_MODE_CMP (0x1 << 7) +#define RT5665_SAR_SEL_MODE_ADC (0x0 << 7) +#define RT5665_SAR_SEL_MB1_MB2_MASK (0x1 << 5) +#define RT5665_SAR_SEL_MB1_MB2_AUTO (0x1 << 5) +#define RT5665_SAR_SEL_MB1_MB2_MANU (0x0 << 5) +#define RT5665_SAR_SEL_SIGNAL_MASK (0x1 << 4) +#define RT5665_SAR_SEL_SIGNAL_AUTO (0x1 << 4) +#define RT5665_SAR_SEL_SIGNAL_MANU (0x0 << 4) + +/* System Clock Source */ +enum { + RT5665_SCLK_S_MCLK, + RT5665_SCLK_S_PLL1, + RT5665_SCLK_S_RCCLK, +}; + +/* PLL1 Source */ +enum { + RT5665_PLL1_S_MCLK, + RT5665_PLL1_S_BCLK1, + RT5665_PLL1_S_BCLK2, + RT5665_PLL1_S_BCLK3, + RT5665_PLL1_S_BCLK4, +}; + +enum { + RT5665_AIF1_1, + RT5665_AIF1_2, + RT5665_AIF2_1, + RT5665_AIF2_2, + RT5665_AIF3, + RT5665_AIFS +}; + +enum { + CODEC_5665, + CODEC_5666, + CODEC_5668, +}; + +/* filter mask */ +enum { + RT5665_DA_STEREO1_FILTER = 0x1, + RT5665_DA_STEREO2_FILTER = (0x1 << 1), + RT5665_DA_MONO_L_FILTER = (0x1 << 2), + RT5665_DA_MONO_R_FILTER = (0x1 << 3), + RT5665_AD_STEREO1_FILTER = (0x1 << 4), + RT5665_AD_STEREO2_FILTER = (0x1 << 5), + RT5665_AD_MONO_L_FILTER = (0x1 << 6), + RT5665_AD_MONO_R_FILTER = (0x1 << 7), +}; + +enum { + RT5665_CLK_SEL_SYS, + RT5665_CLK_SEL_I2S1_ASRC, + RT5665_CLK_SEL_I2S2_ASRC, + RT5665_CLK_SEL_I2S3_ASRC, + RT5665_CLK_SEL_SYS2, + RT5665_CLK_SEL_SYS3, + RT5665_CLK_SEL_SYS4, +}; + +int rt5665_sel_asrc_clk_src(struct snd_soc_codec *codec, + unsigned int filter_mask, unsigned int clk_src); +int rt5665_set_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *hs_jack); + +#endif /* __RT5665_H__ */ diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index 8ef467f64f03..97bafac3bc15 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -2618,7 +2618,7 @@ static int rt5670_set_bias_level(struct snd_soc_codec *codec, RT5670_OSW_L_DIS | RT5670_OSW_R_DIS); snd_soc_update_bits(codec, RT5670_DIG_MISC, 0x1, 0x1); snd_soc_update_bits(codec, RT5670_PWR_ANLG1, - RT5670_LDO_SEL_MASK, 0x3); + RT5670_LDO_SEL_MASK, 0x5); } break; case SND_SOC_BIAS_STANDBY: @@ -2626,7 +2626,7 @@ static int rt5670_set_bias_level(struct snd_soc_codec *codec, RT5670_PWR_VREF1 | RT5670_PWR_VREF2 | RT5670_PWR_FV1 | RT5670_PWR_FV2, 0); snd_soc_update_bits(codec, RT5670_PWR_ANLG1, - RT5670_LDO_SEL_MASK, 0x1); + RT5670_LDO_SEL_MASK, 0x3); break; case SND_SOC_BIAS_OFF: if (rt5670->pdata.jd_mode) @@ -2777,12 +2777,14 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5670 = { .resume = rt5670_resume, .set_bias_level = rt5670_set_bias_level, .idle_bias_off = true, - .controls = rt5670_snd_controls, - .num_controls = ARRAY_SIZE(rt5670_snd_controls), - .dapm_widgets = rt5670_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(rt5670_dapm_widgets), - .dapm_routes = rt5670_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(rt5670_dapm_routes), + .component_driver = { + .controls = rt5670_snd_controls, + .num_controls = ARRAY_SIZE(rt5670_snd_controls), + .dapm_widgets = rt5670_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5670_dapm_widgets), + .dapm_routes = rt5670_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5670_dapm_routes), + }, }; static const struct regmap_config rt5670_regmap = { @@ -2811,6 +2813,7 @@ MODULE_DEVICE_TABLE(i2c, rt5670_i2c_id); #ifdef CONFIG_ACPI static const struct acpi_device_id rt5670_acpi_match[] = { { "10EC5670", 0}, + { "10EC5672", 0}, { }, }; MODULE_DEVICE_TABLE(acpi, rt5670_acpi_match); @@ -2824,6 +2827,13 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = { DMI_MATCH(DMI_BOARD_NAME, "Braswell CRB"), }, }, + { + .ident = "Dell Wyse 3040", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Wyse 3040"), + }, + }, {} }; @@ -2887,6 +2897,9 @@ static int rt5670_i2c_probe(struct i2c_client *i2c, if (ret != 0) dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); + regmap_update_bits(rt5670->regmap, RT5670_DIG_MISC, + RT5670_MCLK_DET, RT5670_MCLK_DET); + if (rt5670->pdata.in2_diff) regmap_update_bits(rt5670->regmap, RT5670_IN2, RT5670_IN_DF2, RT5670_IN_DF2); @@ -2901,7 +2914,6 @@ static int rt5670_i2c_probe(struct i2c_client *i2c, RT5670_GP1_PIN_MASK, RT5670_GP1_PIN_IRQ); regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL2, RT5670_GP1_PF_MASK, RT5670_GP1_PF_OUT); - regmap_update_bits(rt5670->regmap, RT5670_DIG_MISC, 0x8, 0x8); } if (rt5670->pdata.jd_mode) { diff --git a/sound/soc/codecs/rt5670.h b/sound/soc/codecs/rt5670.h index 3f1b0f1df809..5ba485cae4e6 100644 --- a/sound/soc/codecs/rt5670.h +++ b/sound/soc/codecs/rt5670.h @@ -1914,6 +1914,7 @@ enum { #define RT5670_IF1_ADC1_IN2_SFT 11 #define RT5670_IF1_ADC2_IN1_SEL (0x1 << 10) #define RT5670_IF1_ADC2_IN1_SFT 10 +#define RT5670_MCLK_DET (0x1 << 3) /* General Control2 (0xfb) */ #define RT5670_RXDC_SRC_MASK (0x1 << 7) diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c index 91879ea95415..ebd0f7c5ad3b 100644 --- a/sound/soc/codecs/rt5677-spi.c +++ b/sound/soc/codecs/rt5677-spi.c @@ -20,7 +20,6 @@ #include <linux/slab.h> #include <linux/gpio.h> #include <linux/sched.h> -#include <linux/kthread.h> #include <linux/uaccess.h> #include <linux/miscdevice.h> #include <linux/regulator/consumer.h> diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index da9483c1c6fb..abc802a5a479 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -9,6 +9,7 @@ * published by the Free Software Foundation. */ +#include <linux/acpi.h> #include <linux/fs.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -40,6 +41,15 @@ #define RT5677_PR_BASE (RT5677_PR_RANGE_BASE + (0 * RT5677_PR_SPACING)) +/* GPIO indexes defined by ACPI */ +enum { + RT5677_GPIO_PLUG_DET = 0, + RT5677_GPIO_MIC_PRESENT_L = 1, + RT5677_GPIO_HOTWORD_DET_L = 2, + RT5677_GPIO_DSP_INT = 3, + RT5677_GPIO_HP_AMP_SHDN_L = 4, +}; + static const struct regmap_range_cfg rt5677_ranges[] = { { .name = "PR", @@ -4657,7 +4667,7 @@ static int rt5677_to_irq(struct gpio_chip *chip, unsigned offset) return regmap_irq_get_virq(data, irq); } -static struct gpio_chip rt5677_template_chip = { +static const struct gpio_chip rt5677_template_chip = { .label = "rt5677", .owner = THIS_MODULE, .direction_output = rt5677_gpio_direction_out, @@ -4974,12 +4984,14 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5677 = { .resume = rt5677_resume, .set_bias_level = rt5677_set_bias_level, .idle_bias_off = true, - .controls = rt5677_snd_controls, - .num_controls = ARRAY_SIZE(rt5677_snd_controls), - .dapm_widgets = rt5677_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(rt5677_dapm_widgets), - .dapm_routes = rt5677_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(rt5677_dapm_routes), + .component_driver = { + .controls = rt5677_snd_controls, + .num_controls = ARRAY_SIZE(rt5677_snd_controls), + .dapm_widgets = rt5677_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5677_dapm_widgets), + .dapm_routes = rt5677_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5677_dapm_routes), + }, }; static const struct regmap_config rt5677_regmap_physical = { @@ -5018,10 +5030,47 @@ static const struct regmap_config rt5677_regmap = { static const struct i2c_device_id rt5677_i2c_id[] = { { "rt5677", RT5677 }, { "rt5676", RT5676 }, + { "RT5677CE:00", RT5677 }, { } }; MODULE_DEVICE_TABLE(i2c, rt5677_i2c_id); +static const struct acpi_gpio_params plug_det_gpio = { RT5677_GPIO_PLUG_DET, 0, false }; +static const struct acpi_gpio_params mic_present_gpio = { RT5677_GPIO_MIC_PRESENT_L, 0, false }; +static const struct acpi_gpio_params headphone_enable_gpio = { RT5677_GPIO_HP_AMP_SHDN_L, 0, false }; + +static const struct acpi_gpio_mapping bdw_rt5677_gpios[] = { + { "plug-det-gpios", &plug_det_gpio, 1 }, + { "mic-present-gpios", &mic_present_gpio, 1 }, + { "headphone-enable-gpios", &headphone_enable_gpio, 1 }, + { NULL }, +}; + +static void rt5677_read_acpi_properties(struct rt5677_priv *rt5677, + struct device *dev) +{ + int ret; + u32 val; + + ret = acpi_dev_add_driver_gpios(ACPI_COMPANION(dev), + bdw_rt5677_gpios); + if (ret) + dev_warn(dev, "Failed to add driver gpios\n"); + + if (!device_property_read_u32(dev, "DCLK", &val)) + rt5677->pdata.dmic2_clk_pin = val; + + rt5677->pdata.in1_diff = device_property_read_bool(dev, "IN1"); + rt5677->pdata.in2_diff = device_property_read_bool(dev, "IN2"); + rt5677->pdata.lout1_diff = device_property_read_bool(dev, "OUT1"); + rt5677->pdata.lout2_diff = device_property_read_bool(dev, "OUT2"); + rt5677->pdata.lout3_diff = device_property_read_bool(dev, "OUT3"); + + device_property_read_u32(dev, "JD1", &rt5677->pdata.jd1_gpio); + device_property_read_u32(dev, "JD2", &rt5677->pdata.jd2_gpio); + device_property_read_u32(dev, "JD3", &rt5677->pdata.jd3_gpio); +} + static void rt5677_read_device_properties(struct rt5677_priv *rt5677, struct device *dev) { @@ -5127,8 +5176,12 @@ static int rt5677_i2c_probe(struct i2c_client *i2c, if (pdata) rt5677->pdata = *pdata; - else + else if (i2c->dev.of_node) rt5677_read_device_properties(rt5677, &i2c->dev); + else if (ACPI_HANDLE(&i2c->dev)) + rt5677_read_acpi_properties(rt5677, &i2c->dev); + else + return -EINVAL; /* pow-ldo2 and reset are optional. The codec pins may be statically * connected on the board without gpios. If the gpio device property diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 527b759c1562..1589325855bc 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -411,6 +411,8 @@ static const struct snd_kcontrol_new sgtl5000_snd_controls[] = { 0, 8, 0x7f, 1, headphone_volume), + SOC_SINGLE("Headphone Playback Switch", SGTL5000_CHIP_ANA_CTRL, + 4, 1, 1), SOC_SINGLE("Headphone Playback ZC Switch", SGTL5000_CHIP_ANA_CTRL, 5, 1, 0), @@ -423,6 +425,7 @@ static const struct snd_kcontrol_new sgtl5000_snd_controls[] = { SGTL5000_LINE_OUT_VOL_RIGHT_SHIFT, 0x1f, 1, lineout_volume), + SOC_SINGLE("Lineout Playback Switch", SGTL5000_CHIP_ANA_CTRL, 8, 1, 1), }; /* mute the codec used by alsa core */ @@ -1151,12 +1154,14 @@ static struct snd_soc_codec_driver sgtl5000_driver = { .remove = sgtl5000_remove, .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, - .num_dapm_widgets = ARRAY_SIZE(sgtl5000_dapm_widgets), - .dapm_routes = sgtl5000_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(sgtl5000_dapm_routes), + .component_driver = { + .controls = sgtl5000_snd_controls, + .num_controls = ARRAY_SIZE(sgtl5000_snd_controls), + .dapm_widgets = sgtl5000_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sgtl5000_dapm_widgets), + .dapm_routes = sgtl5000_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(sgtl5000_dapm_routes), + }, }; static const struct regmap_config sgtl5000_regmap = { diff --git a/sound/soc/codecs/si476x.c b/sound/soc/codecs/si476x.c index a8402d0af0ea..5344f4aa8fde 100644 --- a/sound/soc/codecs/si476x.c +++ b/sound/soc/codecs/si476x.c @@ -238,10 +238,12 @@ static struct regmap *si476x_get_regmap(struct device *dev) static struct snd_soc_codec_driver soc_codec_dev_si476x = { .get_regmap = si476x_get_regmap, - .dapm_widgets = si476x_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(si476x_dapm_widgets), - .dapm_routes = si476x_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(si476x_dapm_routes), + .component_driver = { + .dapm_widgets = si476x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(si476x_dapm_widgets), + .dapm_routes = si476x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(si476x_dapm_routes), + }, }; static int si476x_platform_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c index 3a7de0159f24..eae54c37cff9 100644 --- a/sound/soc/codecs/sn95031.c +++ b/sound/soc/codecs/sn95031.c @@ -888,12 +888,14 @@ static struct snd_soc_codec_driver sn95031_codec = { .set_bias_level = sn95031_set_vaud_bias, .idle_bias_off = true, - .controls = sn95031_snd_controls, - .num_controls = ARRAY_SIZE(sn95031_snd_controls), - .dapm_widgets = sn95031_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(sn95031_dapm_widgets), - .dapm_routes = sn95031_audio_map, - .num_dapm_routes = ARRAY_SIZE(sn95031_audio_map), + .component_driver = { + .controls = sn95031_snd_controls, + .num_controls = ARRAY_SIZE(sn95031_snd_controls), + .dapm_widgets = sn95031_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sn95031_dapm_widgets), + .dapm_routes = sn95031_audio_map, + .num_dapm_routes = ARRAY_SIZE(sn95031_audio_map), + }, }; static int sn95031_device_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/spdif_receiver.c b/sound/soc/codecs/spdif_receiver.c index 3ec41ccbf4e2..234f87b54838 100644 --- a/sound/soc/codecs/spdif_receiver.c +++ b/sound/soc/codecs/spdif_receiver.c @@ -38,10 +38,12 @@ static const struct snd_soc_dapm_route dir_routes[] = { SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) static struct snd_soc_codec_driver soc_codec_spdif_dir = { - .dapm_widgets = dir_widgets, - .num_dapm_widgets = ARRAY_SIZE(dir_widgets), - .dapm_routes = dir_routes, - .num_dapm_routes = ARRAY_SIZE(dir_routes), + .component_driver = { + .dapm_widgets = dir_widgets, + .num_dapm_widgets = ARRAY_SIZE(dir_widgets), + .dapm_routes = dir_routes, + .num_dapm_routes = ARRAY_SIZE(dir_routes), + }, }; static struct snd_soc_dai_driver dir_stub_dai = { diff --git a/sound/soc/codecs/spdif_transmitter.c b/sound/soc/codecs/spdif_transmitter.c index ef634a9ad673..ee367536a498 100644 --- a/sound/soc/codecs/spdif_transmitter.c +++ b/sound/soc/codecs/spdif_transmitter.c @@ -38,10 +38,12 @@ static const struct snd_soc_dapm_route dit_routes[] = { }; static struct snd_soc_codec_driver soc_codec_spdif_dit = { - .dapm_widgets = dit_widgets, - .num_dapm_widgets = ARRAY_SIZE(dit_widgets), - .dapm_routes = dit_routes, - .num_dapm_routes = ARRAY_SIZE(dit_routes), + .component_driver = { + .dapm_widgets = dit_widgets, + .num_dapm_widgets = ARRAY_SIZE(dit_widgets), + .dapm_routes = dit_routes, + .num_dapm_routes = ARRAY_SIZE(dit_routes), + }, }; static struct snd_soc_dai_driver dit_stub_dai = { diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c index e2e0bfa7ec20..38a85f3adc80 100644 --- a/sound/soc/codecs/ssm2518.c +++ b/sound/soc/codecs/ssm2518.c @@ -715,12 +715,14 @@ static struct snd_soc_codec_driver ssm2518_codec_driver = { .set_sysclk = ssm2518_set_sysclk, .idle_bias_off = true, - .controls = ssm2518_snd_controls, - .num_controls = ARRAY_SIZE(ssm2518_snd_controls), - .dapm_widgets = ssm2518_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(ssm2518_dapm_widgets), - .dapm_routes = ssm2518_routes, - .num_dapm_routes = ARRAY_SIZE(ssm2518_routes), + .component_driver = { + .controls = ssm2518_snd_controls, + .num_controls = ARRAY_SIZE(ssm2518_snd_controls), + .dapm_widgets = ssm2518_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ssm2518_dapm_widgets), + .dapm_routes = ssm2518_routes, + .num_dapm_routes = ARRAY_SIZE(ssm2518_routes), + }, }; static const struct regmap_config ssm2518_regmap_config = { diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index 4452fea0b118..993bde29ca1b 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -597,12 +597,14 @@ static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = { .set_bias_level = ssm2602_set_bias_level, .suspend_bias_off = true, - .controls = ssm260x_snd_controls, - .num_controls = ARRAY_SIZE(ssm260x_snd_controls), - .dapm_widgets = ssm260x_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(ssm260x_dapm_widgets), - .dapm_routes = ssm260x_routes, - .num_dapm_routes = ARRAY_SIZE(ssm260x_routes), + .component_driver = { + .controls = ssm260x_snd_controls, + .num_controls = ARRAY_SIZE(ssm260x_snd_controls), + .dapm_widgets = ssm260x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ssm260x_dapm_widgets), + .dapm_routes = ssm260x_routes, + .num_dapm_routes = ARRAY_SIZE(ssm260x_routes), + }, }; static bool ssm2602_register_volatile(struct device *dev, unsigned int reg) diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c index 080c78e88e10..2bb5a11c9ba1 100644 --- a/sound/soc/codecs/ssm4567.c +++ b/sound/soc/codecs/ssm4567.c @@ -421,12 +421,14 @@ 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), + .component_driver = { + .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 = { diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index a9844b2ac829..0790ae8530d9 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c @@ -991,12 +991,14 @@ static const struct snd_soc_codec_driver sta32x_codec = { .remove = sta32x_remove, .set_bias_level = sta32x_set_bias_level, .suspend_bias_off = true, - .controls = sta32x_snd_controls, - .num_controls = ARRAY_SIZE(sta32x_snd_controls), - .dapm_widgets = sta32x_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(sta32x_dapm_widgets), - .dapm_routes = sta32x_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(sta32x_dapm_routes), + .component_driver = { + .controls = sta32x_snd_controls, + .num_controls = ARRAY_SIZE(sta32x_snd_controls), + .dapm_widgets = sta32x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sta32x_dapm_widgets), + .dapm_routes = sta32x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(sta32x_dapm_routes), + }, }; static const struct regmap_config sta32x_regmap = { diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c index 33a4612f0a07..9644c20f44e3 100644 --- a/sound/soc/codecs/sta350.c +++ b/sound/soc/codecs/sta350.c @@ -1057,12 +1057,14 @@ static const struct snd_soc_codec_driver sta350_codec = { .remove = sta350_remove, .set_bias_level = sta350_set_bias_level, .suspend_bias_off = true, - .controls = sta350_snd_controls, - .num_controls = ARRAY_SIZE(sta350_snd_controls), - .dapm_widgets = sta350_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(sta350_dapm_widgets), - .dapm_routes = sta350_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(sta350_dapm_routes), + .component_driver = { + .controls = sta350_snd_controls, + .num_controls = ARRAY_SIZE(sta350_snd_controls), + .dapm_widgets = sta350_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sta350_dapm_widgets), + .dapm_routes = sta350_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(sta350_dapm_routes), + }, }; static const struct regmap_config sta350_regmap = { diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c index 2cdaca943a8c..d4b384e4b266 100644 --- a/sound/soc/codecs/sta529.c +++ b/sound/soc/codecs/sta529.c @@ -317,8 +317,10 @@ static const struct snd_soc_codec_driver sta529_codec_driver = { .set_bias_level = sta529_set_bias_level, .suspend_bias_off = true, - .controls = sta529_snd_controls, - .num_controls = ARRAY_SIZE(sta529_snd_controls), + .component_driver = { + .controls = sta529_snd_controls, + .num_controls = ARRAY_SIZE(sta529_snd_controls), + }, }; static const struct regmap_config sta529_regmap = { diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c index 0945c51df003..9de7fe8af255 100644 --- a/sound/soc/codecs/stac9766.c +++ b/sound/soc/codecs/stac9766.c @@ -18,6 +18,7 @@ #include <linux/slab.h> #include <linux/module.h> #include <linux/device.h> +#include <linux/regmap.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/ac97_codec.h> @@ -26,31 +27,56 @@ #include <sound/soc.h> #include <sound/tlv.h> -#include "stac9766.h" - #define STAC9766_VENDOR_ID 0x83847666 #define STAC9766_VENDOR_ID_MASK 0xffffffff -/* - * STAC9766 register cache - */ -static const u16 stac9766_reg[] = { - 0x6A90, 0x8000, 0x8000, 0x8000, /* 6 */ - 0x0000, 0x0000, 0x8008, 0x8008, /* e */ - 0x8808, 0x8808, 0x8808, 0x8808, /* 16 */ - 0x8808, 0x0000, 0x8000, 0x0000, /* 1e */ - 0x0000, 0x0000, 0x0000, 0x000f, /* 26 */ - 0x0a05, 0x0400, 0xbb80, 0x0000, /* 2e */ - 0x0000, 0xbb80, 0x0000, 0x0000, /* 36 */ - 0x0000, 0x2000, 0x0000, 0x0100, /* 3e */ - 0x0000, 0x0000, 0x0080, 0x0000, /* 46 */ - 0x0000, 0x0000, 0x0003, 0xffff, /* 4e */ - 0x0000, 0x0000, 0x0000, 0x0000, /* 56 */ - 0x4000, 0x0000, 0x0000, 0x0000, /* 5e */ - 0x1201, 0xFFFF, 0xFFFF, 0x0000, /* 66 */ - 0x0000, 0x0000, 0x0000, 0x0000, /* 6e */ - 0x0000, 0x0000, 0x0000, 0x0006, /* 76 */ - 0x0000, 0x0000, 0x0000, 0x0000, /* 7e */ +#define AC97_STAC_DA_CONTROL 0x6A +#define AC97_STAC_ANALOG_SPECIAL 0x6E +#define AC97_STAC_STEREO_MIC 0x78 + +static const struct reg_default stac9766_reg_defaults[] = { + { 0x02, 0x8000 }, + { 0x04, 0x8000 }, + { 0x06, 0x8000 }, + { 0x0a, 0x0000 }, + { 0x0c, 0x8008 }, + { 0x0e, 0x8008 }, + { 0x10, 0x8808 }, + { 0x12, 0x8808 }, + { 0x14, 0x8808 }, + { 0x16, 0x8808 }, + { 0x18, 0x8808 }, + { 0x1a, 0x0000 }, + { 0x1c, 0x8000 }, + { 0x20, 0x0000 }, + { 0x22, 0x0000 }, + { 0x28, 0x0a05 }, + { 0x2c, 0xbb80 }, + { 0x32, 0xbb80 }, + { 0x3a, 0x2000 }, + { 0x3e, 0x0100 }, + { 0x4c, 0x0300 }, + { 0x4e, 0xffff }, + { 0x50, 0x0000 }, + { 0x52, 0x0000 }, + { 0x54, 0x0000 }, + { 0x6a, 0x0000 }, + { 0x6e, 0x1000 }, + { 0x72, 0x0000 }, + { 0x78, 0x0000 }, +}; + +static const struct regmap_config stac9766_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x78, + .cache_type = REGCACHE_RBTREE, + + .volatile_reg = regmap_ac97_default_volatile, + + .reg_defaults = stac9766_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(stac9766_reg_defaults), }; static const char *stac9766_record_mux[] = {"Mic", "CD", "Video", "AUX", @@ -85,10 +111,10 @@ static SOC_ENUM_SINGLE_DECL(stac9766_boost2_enum, static SOC_ENUM_SINGLE_DECL(stac9766_stereo_mic_enum, AC97_STAC_STEREO_MIC, 2, stac9766_stereo_mic); -static const DECLARE_TLV_DB_LINEAR(master_tlv, -4600, 0); -static const DECLARE_TLV_DB_LINEAR(record_tlv, 0, 2250); -static const DECLARE_TLV_DB_LINEAR(beep_tlv, -4500, 0); -static const DECLARE_TLV_DB_LINEAR(mix_tlv, -3450, 1200); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(master_tlv, -4650, 150, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(record_tlv, 0, 150, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(beep_tlv, -4500, 300, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(mix_tlv, -3450, 150, 0); static const struct snd_kcontrol_new stac9766_snd_ac97_controls[] = { SOC_DOUBLE_TLV("Speaker Volume", AC97_MASTER, 8, 0, 31, 1, master_tlv), @@ -139,71 +165,22 @@ static const struct snd_kcontrol_new stac9766_snd_ac97_controls[] = { SOC_ENUM("Pop Bypass Mux", stac9766_popbypass_enum), }; -static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int val) -{ - struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); - u16 *cache = codec->reg_cache; - - if (reg > AC97_STAC_PAGE0) { - stac9766_ac97_write(codec, AC97_INT_PAGING, 0); - soc_ac97_ops->write(ac97, reg, val); - stac9766_ac97_write(codec, AC97_INT_PAGING, 1); - return 0; - } - if (reg / 2 >= ARRAY_SIZE(stac9766_reg)) - return -EIO; - - soc_ac97_ops->write(ac97, reg, val); - cache[reg / 2] = val; - return 0; -} - -static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); - u16 val = 0, *cache = codec->reg_cache; - - if (reg > AC97_STAC_PAGE0) { - stac9766_ac97_write(codec, AC97_INT_PAGING, 0); - val = soc_ac97_ops->read(ac97, reg - AC97_STAC_PAGE0); - stac9766_ac97_write(codec, AC97_INT_PAGING, 1); - return val; - } - if (reg / 2 >= ARRAY_SIZE(stac9766_reg)) - return -EIO; - - if (reg == AC97_RESET || reg == AC97_GPIO_STATUS || - reg == AC97_INT_PAGING || reg == AC97_VENDOR_ID1 || - reg == AC97_VENDOR_ID2) { - - val = soc_ac97_ops->read(ac97, reg); - return val; - } - return cache[reg / 2]; -} - static int ac97_analog_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct snd_pcm_runtime *runtime = substream->runtime; - unsigned short reg, vra; + unsigned short reg; - vra = stac9766_ac97_read(codec, AC97_EXTENDED_STATUS); - - vra |= 0x1; /* enable variable rate audio */ - vra &= ~0x4; /* disable SPDIF output */ - - stac9766_ac97_write(codec, AC97_EXTENDED_STATUS, vra); + /* enable variable rate audio, disable SPDIF output */ + snd_soc_update_bits(codec, AC97_EXTENDED_STATUS, 0x5, 0x1); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) reg = AC97_PCM_FRONT_DAC_RATE; else reg = AC97_PCM_LR_ADC_RATE; - return stac9766_ac97_write(codec, reg, runtime->rate); + return snd_soc_write(codec, reg, runtime->rate); } static int ac97_digital_prepare(struct snd_pcm_substream *substream, @@ -211,18 +188,16 @@ static int ac97_digital_prepare(struct snd_pcm_substream *substream, { struct snd_soc_codec *codec = dai->codec; struct snd_pcm_runtime *runtime = substream->runtime; - unsigned short reg, vra; + unsigned short reg; - stac9766_ac97_write(codec, AC97_SPDIF, 0x2002); + snd_soc_write(codec, AC97_SPDIF, 0x2002); - vra = stac9766_ac97_read(codec, AC97_EXTENDED_STATUS); - vra |= 0x5; /* Enable VRA and SPDIF out */ - - stac9766_ac97_write(codec, AC97_EXTENDED_STATUS, vra); + /* Enable VRA and SPDIF out */ + snd_soc_update_bits(codec, AC97_EXTENDED_STATUS, 0x5, 0x5); reg = AC97_PCM_FRONT_DAC_RATE; - return stac9766_ac97_write(codec, reg, runtime->rate); + return snd_soc_write(codec, reg, runtime->rate); } static int stac9766_set_bias_level(struct snd_soc_codec *codec, @@ -232,11 +207,11 @@ static int stac9766_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_ON: /* full On */ case SND_SOC_BIAS_PREPARE: /* partial On */ case SND_SOC_BIAS_STANDBY: /* Off, with power */ - stac9766_ac97_write(codec, AC97_POWERDOWN, 0x0000); + snd_soc_write(codec, AC97_POWERDOWN, 0x0000); break; case SND_SOC_BIAS_OFF: /* Off, without power */ /* disable everything including AC link */ - stac9766_ac97_write(codec, AC97_POWERDOWN, 0xffff); + snd_soc_write(codec, AC97_POWERDOWN, 0xffff); break; } return 0; @@ -300,39 +275,48 @@ static struct snd_soc_dai_driver stac9766_dai[] = { static int stac9766_codec_probe(struct snd_soc_codec *codec) { struct snd_ac97 *ac97; + struct regmap *regmap; + int ret; ac97 = snd_soc_new_ac97_codec(codec, STAC9766_VENDOR_ID, STAC9766_VENDOR_ID_MASK); if (IS_ERR(ac97)) return PTR_ERR(ac97); + regmap = regmap_init_ac97(ac97, &stac9766_regmap_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + goto err_free_ac97; + } + + snd_soc_codec_init_regmap(codec, regmap); snd_soc_codec_set_drvdata(codec, ac97); return 0; +err_free_ac97: + snd_soc_free_ac97_codec(ac97); + return ret; } static int stac9766_codec_remove(struct snd_soc_codec *codec) { struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); + snd_soc_codec_exit_regmap(codec); snd_soc_free_ac97_codec(ac97); return 0; } static struct snd_soc_codec_driver soc_codec_dev_stac9766 = { - .controls = stac9766_snd_ac97_controls, - .num_controls = ARRAY_SIZE(stac9766_snd_ac97_controls), - .write = stac9766_ac97_write, - .read = stac9766_ac97_read, + .component_driver = { + .controls = stac9766_snd_ac97_controls, + .num_controls = ARRAY_SIZE(stac9766_snd_ac97_controls), + }, .set_bias_level = stac9766_set_bias_level, .suspend_bias_off = true, .probe = stac9766_codec_probe, .remove = stac9766_codec_remove, .resume = stac9766_codec_resume, - .reg_cache_size = ARRAY_SIZE(stac9766_reg), - .reg_word_size = sizeof(u16), - .reg_cache_step = 2, - .reg_cache_default = stac9766_reg, }; static int stac9766_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/stac9766.h b/sound/soc/codecs/stac9766.h deleted file mode 100644 index c726f907e2c0..000000000000 --- a/sound/soc/codecs/stac9766.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * stac9766.h -- STAC9766 Soc Audio driver - */ - -#ifndef _STAC9766_H -#define _STAC9766_H - -#define AC97_STAC_PAGE0 0x1000 -#define AC97_STAC_DA_CONTROL (AC97_STAC_PAGE0 | 0x6A) -#define AC97_STAC_ANALOG_SPECIAL (AC97_STAC_PAGE0 | 0x6E) -#define AC97_STAC_STEREO_MIC 0x78 - -/* STAC9766 DAI ID's */ -#define STAC9766_DAI_AC97_ANALOG 0 -#define STAC9766_DAI_AC97_DIGITAL 1 - -#endif diff --git a/sound/soc/codecs/sti-sas.c b/sound/soc/codecs/sti-sas.c index 160d61a66204..62c618765224 100644 --- a/sound/soc/codecs/sti-sas.c +++ b/sound/soc/codecs/sti-sas.c @@ -14,28 +14,8 @@ #include <sound/soc.h> #include <sound/soc-dapm.h> -/* chipID supported */ -#define CHIPID_STIH416 0 -#define CHIPID_STIH407 1 - /* DAC definitions */ -/* stih416 DAC registers */ -/* sysconf 2517: Audio-DAC-Control */ -#define STIH416_AUDIO_DAC_CTRL 0x00000814 -/* sysconf 2519: Audio-Gue-Control */ -#define STIH416_AUDIO_GLUE_CTRL 0x0000081C - -#define STIH416_DAC_NOT_STANDBY 0x3 -#define STIH416_DAC_SOFTMUTE 0x4 -#define STIH416_DAC_ANA_NOT_PWR 0x5 -#define STIH416_DAC_NOT_PNDBG 0x6 - -#define STIH416_DAC_NOT_STANDBY_MASK BIT(STIH416_DAC_NOT_STANDBY) -#define STIH416_DAC_SOFTMUTE_MASK BIT(STIH416_DAC_SOFTMUTE) -#define STIH416_DAC_ANA_NOT_PWR_MASK BIT(STIH416_DAC_ANA_NOT_PWR) -#define STIH416_DAC_NOT_PNDBG_MASK BIT(STIH416_DAC_NOT_PNDBG) - /* stih407 DAC registers */ /* sysconf 5041: Audio-Gue-Control */ #define STIH407_AUDIO_GLUE_CTRL 0x000000A4 @@ -63,14 +43,9 @@ enum { STI_SAS_DAI_ANALOG_OUT, }; -static const struct reg_default stih416_sas_reg_defaults[] = { - { STIH407_AUDIO_GLUE_CTRL, 0x00000040 }, - { STIH407_AUDIO_DAC_CTRL, 0x000000000 }, -}; - static const struct reg_default stih407_sas_reg_defaults[] = { - { STIH416_AUDIO_DAC_CTRL, 0x000000000 }, - { STIH416_AUDIO_GLUE_CTRL, 0x00000040 }, + { STIH407_AUDIO_DAC_CTRL, 0x000000000 }, + { STIH407_AUDIO_GLUE_CTRL, 0x00000040 }, }; struct sti_dac_audio { @@ -89,7 +64,6 @@ struct sti_spdif_audio { /* device data structure */ struct sti_sas_dev_data { - const int chipid; /* IC version */ const struct regmap_config *regmap; const struct snd_soc_dai_ops *dac_ops; /* DAC function callbacks */ const struct snd_soc_dapm_widget *dapm_widgets; /* dapms declaration */ @@ -150,51 +124,27 @@ static int sti_sas_init_sas_registers(struct snd_soc_codec *codec, ret = snd_soc_update_bits(codec, STIH407_AUDIO_GLUE_CTRL, SPDIF_BIPHASE_IDLE_MASK, 0); if (ret < 0) { - dev_err(codec->dev, "Failed to update SPDIF registers"); + dev_err(codec->dev, "Failed to update SPDIF registers\n"); return ret; } /* Init DAC configuration */ - switch (data->dev_data->chipid) { - case CHIPID_STIH407: - /* init configuration */ - ret = snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL, - STIH407_DAC_STANDBY_MASK, - STIH407_DAC_STANDBY_MASK); - - if (!ret) - ret = snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL, - STIH407_DAC_STANDBY_ANA_MASK, - STIH407_DAC_STANDBY_ANA_MASK); - if (!ret) - ret = snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL, - STIH407_DAC_SOFTMUTE_MASK, - STIH407_DAC_SOFTMUTE_MASK); - break; - case CHIPID_STIH416: - ret = snd_soc_update_bits(codec, STIH416_AUDIO_DAC_CTRL, - STIH416_DAC_NOT_STANDBY_MASK, 0); - if (!ret) - ret = snd_soc_update_bits(codec, - STIH416_AUDIO_DAC_CTRL, - STIH416_DAC_ANA_NOT_PWR, 0); - if (!ret) - ret = snd_soc_update_bits(codec, - STIH416_AUDIO_DAC_CTRL, - STIH416_DAC_NOT_PNDBG_MASK, - 0); - if (!ret) - ret = snd_soc_update_bits(codec, - STIH416_AUDIO_DAC_CTRL, - STIH416_DAC_SOFTMUTE_MASK, - STIH416_DAC_SOFTMUTE_MASK); - break; - default: - return -EINVAL; - } + /* init configuration */ + ret = snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL, + STIH407_DAC_STANDBY_MASK, + STIH407_DAC_STANDBY_MASK); + + if (!ret) + ret = snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL, + STIH407_DAC_STANDBY_ANA_MASK, + STIH407_DAC_STANDBY_ANA_MASK); + if (!ret) + ret = snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL, + STIH407_DAC_SOFTMUTE_MASK, + STIH407_DAC_SOFTMUTE_MASK); if (ret < 0) { - dev_err(codec->dev, "Failed to update DAC registers"); + dev_err(codec->dev, "Failed to update DAC registers\n"); return ret; } @@ -217,37 +167,6 @@ static int sti_sas_dac_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } -static int stih416_dac_probe(struct snd_soc_dai *dai) -{ - struct snd_soc_codec *codec = dai->codec; - struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev); - struct sti_dac_audio *dac = &drvdata->dac; - - /* Get reset control */ - dac->rst = devm_reset_control_get(codec->dev, "dac_rst"); - if (IS_ERR(dac->rst)) { - dev_err(dai->codec->dev, - "%s: ERROR: DAC reset control not defined !\n", - __func__); - dac->rst = NULL; - return -EFAULT; - } - /* Put the DAC into reset */ - reset_control_assert(dac->rst); - - return 0; -} - -static const struct snd_soc_dapm_widget stih416_sas_dapm_widgets[] = { - SND_SOC_DAPM_PGA("DAC bandgap", STIH416_AUDIO_DAC_CTRL, - STIH416_DAC_NOT_PNDBG_MASK, 0, NULL, 0), - SND_SOC_DAPM_OUT_DRV("DAC standby ana", STIH416_AUDIO_DAC_CTRL, - STIH416_DAC_ANA_NOT_PWR, 0, NULL, 0), - SND_SOC_DAPM_DAC("DAC standby", "dac_p", STIH416_AUDIO_DAC_CTRL, - STIH416_DAC_NOT_STANDBY, 0), - SND_SOC_DAPM_OUTPUT("DAC Output"), -}; - static const struct snd_soc_dapm_widget stih407_sas_dapm_widgets[] = { SND_SOC_DAPM_OUT_DRV("DAC standby ana", STIH407_AUDIO_DAC_CTRL, STIH407_DAC_STANDBY_ANA, 1, NULL, 0), @@ -256,30 +175,11 @@ static const struct snd_soc_dapm_widget stih407_sas_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("DAC Output"), }; -static const struct snd_soc_dapm_route stih416_sas_route[] = { - {"DAC Output", NULL, "DAC bandgap"}, - {"DAC Output", NULL, "DAC standby ana"}, - {"DAC standby ana", NULL, "DAC standby"}, -}; - static const struct snd_soc_dapm_route stih407_sas_route[] = { {"DAC Output", NULL, "DAC standby ana"}, {"DAC standby ana", NULL, "DAC standby"}, }; -static int stih416_sas_dac_mute(struct snd_soc_dai *dai, int mute, int stream) -{ - struct snd_soc_codec *codec = dai->codec; - - if (mute) { - return snd_soc_update_bits(codec, STIH416_AUDIO_DAC_CTRL, - STIH416_DAC_SOFTMUTE_MASK, - STIH416_DAC_SOFTMUTE_MASK); - } else { - return snd_soc_update_bits(codec, STIH416_AUDIO_DAC_CTRL, - STIH416_DAC_SOFTMUTE_MASK, 0); - } -} static int stih407_sas_dac_mute(struct snd_soc_dai *dai, int mute, int stream) { @@ -392,13 +292,13 @@ static int sti_sas_prepare(struct snd_pcm_substream *substream, switch (dai->id) { case STI_SAS_DAI_SPDIF_OUT: if ((drvdata->spdif.mclk / runtime->rate) != 128) { - dev_err(codec->dev, "unexpected mclk-fs ratio"); + dev_err(codec->dev, "unexpected mclk-fs ratio\n"); return -EINVAL; } break; case STI_SAS_DAI_ANALOG_OUT: if ((drvdata->dac.mclk / runtime->rate) != 256) { - dev_err(codec->dev, "unexpected mclk-fs ratio"); + dev_err(codec->dev, "unexpected mclk-fs ratio\n"); return -EINVAL; } break; @@ -407,13 +307,6 @@ static int sti_sas_prepare(struct snd_pcm_substream *substream, return 0; } -static const struct snd_soc_dai_ops stih416_dac_ops = { - .set_fmt = sti_sas_dac_set_fmt, - .mute_stream = stih416_sas_dac_mute, - .prepare = sti_sas_prepare, - .set_sysclk = sti_sas_set_sysclk, -}; - static const struct snd_soc_dai_ops stih407_dac_ops = { .set_fmt = sti_sas_dac_set_fmt, .mute_stream = stih407_sas_dac_mute, @@ -424,7 +317,7 @@ static const struct snd_soc_dai_ops stih407_dac_ops = { static const struct regmap_config stih407_sas_regmap = { .reg_bits = 32, .val_bits = 32, - + .fast_io = true, .max_register = STIH407_AUDIO_DAC_CTRL, .reg_defaults = stih407_sas_reg_defaults, .num_reg_defaults = ARRAY_SIZE(stih407_sas_reg_defaults), @@ -434,31 +327,7 @@ static const struct regmap_config stih407_sas_regmap = { .reg_write = sti_sas_write_reg, }; -static const struct regmap_config stih416_sas_regmap = { - .reg_bits = 32, - .val_bits = 32, - - .max_register = STIH416_AUDIO_DAC_CTRL, - .reg_defaults = stih416_sas_reg_defaults, - .num_reg_defaults = ARRAY_SIZE(stih416_sas_reg_defaults), - .volatile_reg = sti_sas_volatile_register, - .cache_type = REGCACHE_RBTREE, - .reg_read = sti_sas_read_reg, - .reg_write = sti_sas_write_reg, -}; - -static const struct sti_sas_dev_data stih416_data = { - .chipid = CHIPID_STIH416, - .regmap = &stih416_sas_regmap, - .dac_ops = &stih416_dac_ops, - .dapm_widgets = stih416_sas_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(stih416_sas_dapm_widgets), - .dapm_routes = stih416_sas_route, - .num_dapm_routes = ARRAY_SIZE(stih416_sas_route), -}; - static const struct sti_sas_dev_data stih407_data = { - .chipid = CHIPID_STIH407, .regmap = &stih407_sas_regmap, .dac_ops = &stih407_dac_ops, .dapm_widgets = stih407_sas_dapm_widgets, @@ -533,10 +402,6 @@ static struct snd_soc_codec_driver sti_sas_driver = { static const struct of_device_id sti_sas_dev_match[] = { { - .compatible = "st,stih416-sas-codec", - .data = &stih416_data, - }, - { .compatible = "st,stih407-sas-codec", .data = &stih407_data, }, @@ -558,7 +423,7 @@ static int sti_sas_driver_probe(struct platform_device *pdev) /* Populate data structure depending on compatibility */ of_id = of_match_node(sti_sas_dev_match, pnode); if (!of_id->data) { - dev_err(&pdev->dev, "data associated to device is missing"); + dev_err(&pdev->dev, "data associated to device is missing\n"); return -EINVAL; } @@ -584,18 +449,14 @@ static int sti_sas_driver_probe(struct platform_device *pdev) } drvdata->spdif.regmap = drvdata->dac.regmap; - /* Set DAC dai probe */ - if (drvdata->dev_data->chipid == CHIPID_STIH416) - sti_sas_dai[STI_SAS_DAI_ANALOG_OUT].probe = stih416_dac_probe; - sti_sas_dai[STI_SAS_DAI_ANALOG_OUT].ops = drvdata->dev_data->dac_ops; /* Set dapms*/ - sti_sas_driver.dapm_widgets = drvdata->dev_data->dapm_widgets; - sti_sas_driver.num_dapm_widgets = drvdata->dev_data->num_dapm_widgets; + sti_sas_driver.component_driver.dapm_widgets = drvdata->dev_data->dapm_widgets; + sti_sas_driver.component_driver.num_dapm_widgets = drvdata->dev_data->num_dapm_widgets; - sti_sas_driver.dapm_routes = drvdata->dev_data->dapm_routes; - sti_sas_driver.num_dapm_routes = drvdata->dev_data->num_dapm_routes; + sti_sas_driver.component_driver.dapm_routes = drvdata->dev_data->dapm_routes; + sti_sas_driver.component_driver.num_dapm_routes = drvdata->dev_data->num_dapm_routes; /* Store context */ dev_set_drvdata(&pdev->dev, drvdata); diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index cc1d3981fa4b..baf455e8c2f7 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c @@ -667,12 +667,14 @@ static struct snd_soc_codec_driver soc_codec_dev_tas2552 = { .resume = tas2552_resume, .ignore_pmdown_time = true, - .controls = tas2552_snd_controls, - .num_controls = ARRAY_SIZE(tas2552_snd_controls), - .dapm_widgets = tas2552_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(tas2552_dapm_widgets), - .dapm_routes = tas2552_audio_map, - .num_dapm_routes = ARRAY_SIZE(tas2552_audio_map), + .component_driver = { + .controls = tas2552_snd_controls, + .num_controls = ARRAY_SIZE(tas2552_snd_controls), + .dapm_widgets = tas2552_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas2552_dapm_widgets), + .dapm_routes = tas2552_audio_map, + .num_dapm_routes = ARRAY_SIZE(tas2552_audio_map), + }, }; static const struct regmap_config tas2552_regmap_config = { diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c index d49d25d51957..b7de857abb16 100644 --- a/sound/soc/codecs/tas5086.c +++ b/sound/soc/codecs/tas5086.c @@ -387,7 +387,7 @@ static int tas5086_hw_params(struct snd_pcm_substream *substream, val = index_in_array(tas5086_ratios, ARRAY_SIZE(tas5086_ratios), priv->mclk / priv->rate); if (val < 0) { - dev_err(codec->dev, "Inavlid MCLK / Fs ratio\n"); + dev_err(codec->dev, "Invalid MCLK / Fs ratio\n"); return -EINVAL; } @@ -890,12 +890,14 @@ static struct snd_soc_codec_driver soc_codec_dev_tas5086 = { .remove = tas5086_remove, .suspend = tas5086_soc_suspend, .resume = tas5086_soc_resume, - .controls = tas5086_controls, - .num_controls = ARRAY_SIZE(tas5086_controls), - .dapm_widgets = tas5086_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(tas5086_dapm_widgets), - .dapm_routes = tas5086_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(tas5086_dapm_routes), + .component_driver = { + .controls = tas5086_controls, + .num_controls = ARRAY_SIZE(tas5086_controls), + .dapm_widgets = tas5086_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas5086_dapm_widgets), + .dapm_routes = tas5086_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(tas5086_dapm_routes), + }, }; static const struct i2c_device_id tas5086_i2c_id[] = { diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c index d8baca3f8413..810369f687d7 100644 --- a/sound/soc/codecs/tas571x.c +++ b/sound/soc/codecs/tas571x.c @@ -341,20 +341,9 @@ static int tas571x_set_bias_level(struct snd_soc_codec *codec, return ret; } } - - gpiod_set_value(priv->pdn_gpio, 0); - usleep_range(5000, 6000); - - regcache_cache_only(priv->regmap, false); - ret = regcache_sync(priv->regmap); - if (ret) - return ret; } break; case SND_SOC_BIAS_OFF: - regcache_cache_only(priv->regmap, true); - gpiod_set_value(priv->pdn_gpio, 1); - if (!IS_ERR(priv->mclk)) clk_disable_unprepare(priv->mclk); break; @@ -401,16 +390,6 @@ static const struct snd_kcontrol_new tas5711_controls[] = { TAS571X_SOFT_MUTE_REG, TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, 1, 1), - - SOC_DOUBLE_R_RANGE("CH1 Mixer Volume", - TAS5717_CH1_LEFT_CH_MIX_REG, - TAS5717_CH1_RIGHT_CH_MIX_REG, - 16, 0, 0x80, 0), - - SOC_DOUBLE_R_RANGE("CH2 Mixer Volume", - TAS5717_CH2_LEFT_CH_MIX_REG, - TAS5717_CH2_RIGHT_CH_MIX_REG, - 16, 0, 0x80, 0), }; static const struct regmap_range tas571x_readonly_regs_range[] = { @@ -488,6 +467,16 @@ static const struct snd_kcontrol_new tas5717_controls[] = { TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, 1, 1), + SOC_DOUBLE_R_RANGE("CH1 Mixer Volume", + TAS5717_CH1_LEFT_CH_MIX_REG, + TAS5717_CH1_RIGHT_CH_MIX_REG, + 16, 0, 0x80, 0), + + SOC_DOUBLE_R_RANGE("CH2 Mixer Volume", + TAS5717_CH2_LEFT_CH_MIX_REG, + TAS5717_CH2_RIGHT_CH_MIX_REG, + 16, 0, 0x80, 0), + /* * The biquads are named according to the register names. * Please note that TI's TAS57xx Graphical Development Environment @@ -658,10 +647,12 @@ static const struct snd_soc_codec_driver tas571x_codec = { .set_bias_level = tas571x_set_bias_level, .idle_bias_off = true, - .dapm_widgets = tas571x_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(tas571x_dapm_widgets), - .dapm_routes = tas571x_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(tas571x_dapm_routes), + .component_driver = { + .dapm_widgets = tas571x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas571x_dapm_widgets), + .dapm_routes = tas571x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(tas571x_dapm_routes), + }, }; static struct snd_soc_dai_driver tas571x_dai = { @@ -745,17 +736,18 @@ static int tas571x_i2c_probe(struct i2c_client *client, /* pulse the active low reset line for ~100us */ usleep_range(100, 200); gpiod_set_value(priv->reset_gpio, 0); - usleep_range(12000, 20000); + usleep_range(13500, 20000); } ret = regmap_write(priv->regmap, TAS571X_OSC_TRIM_REG, 0); if (ret) return ret; + usleep_range(50000, 60000); memcpy(&priv->codec_driver, &tas571x_codec, sizeof(priv->codec_driver)); - priv->codec_driver.controls = priv->chip->controls; - priv->codec_driver.num_controls = priv->chip->num_controls; + priv->codec_driver.component_driver.controls = priv->chip->controls; + priv->codec_driver.component_driver.num_controls = priv->chip->num_controls; if (priv->chip->vol_reg_size == 2) { /* @@ -768,9 +760,6 @@ static int tas571x_i2c_probe(struct i2c_client *client, return ret; } - regcache_cache_only(priv->regmap, true); - gpiod_set_value(priv->pdn_gpio, 1); - return snd_soc_register_codec(&client->dev, &priv->codec_driver, &tas571x_dai, 1); } diff --git a/sound/soc/codecs/tas5720.c b/sound/soc/codecs/tas5720.c index f54fb46b77c2..c65b917598d2 100644 --- a/sound/soc/codecs/tas5720.c +++ b/sound/soc/codecs/tas5720.c @@ -489,12 +489,14 @@ static struct snd_soc_codec_driver soc_codec_dev_tas5720 = { .suspend = tas5720_suspend, .resume = tas5720_resume, - .controls = tas5720_snd_controls, - .num_controls = ARRAY_SIZE(tas5720_snd_controls), - .dapm_widgets = tas5720_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(tas5720_dapm_widgets), - .dapm_routes = tas5720_audio_map, - .num_dapm_routes = ARRAY_SIZE(tas5720_audio_map), + .component_driver = { + .controls = tas5720_snd_controls, + .num_controls = ARRAY_SIZE(tas5720_snd_controls), + .dapm_widgets = tas5720_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas5720_dapm_widgets), + .dapm_routes = tas5720_audio_map, + .num_dapm_routes = ARRAY_SIZE(tas5720_audio_map), + }, }; /* PCM rates supported by the TAS5720 driver */ diff --git a/sound/soc/codecs/tfa9879.c b/sound/soc/codecs/tfa9879.c index cb5310d89c0f..95e0a7abeb7a 100644 --- a/sound/soc/codecs/tfa9879.c +++ b/sound/soc/codecs/tfa9879.c @@ -231,13 +231,14 @@ static const struct snd_soc_dapm_route tfa9879_dapm_routes[] = { }; static const struct snd_soc_codec_driver tfa9879_codec = { - .controls = tfa9879_controls, - .num_controls = ARRAY_SIZE(tfa9879_controls), - - .dapm_widgets = tfa9879_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(tfa9879_dapm_widgets), - .dapm_routes = tfa9879_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(tfa9879_dapm_routes), + .component_driver = { + .controls = tfa9879_controls, + .num_controls = ARRAY_SIZE(tfa9879_controls), + .dapm_widgets = tfa9879_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tfa9879_dapm_widgets), + .dapm_routes = tfa9879_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(tfa9879_dapm_routes), + }, }; static const struct regmap_config tfa9879_regmap = { diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c index cd8c02b6e4de..410cae0f2060 100644 --- a/sound/soc/codecs/tlv320aic23.c +++ b/sound/soc/codecs/tlv320aic23.c @@ -583,12 +583,14 @@ static struct snd_soc_codec_driver soc_codec_dev_tlv320aic23 = { .set_bias_level = tlv320aic23_set_bias_level, .suspend_bias_off = true, - .controls = tlv320aic23_snd_controls, - .num_controls = ARRAY_SIZE(tlv320aic23_snd_controls), - .dapm_widgets = tlv320aic23_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets), - .dapm_routes = tlv320aic23_intercon, - .num_dapm_routes = ARRAY_SIZE(tlv320aic23_intercon), + .component_driver = { + .controls = tlv320aic23_snd_controls, + .num_controls = ARRAY_SIZE(tlv320aic23_snd_controls), + .dapm_widgets = tlv320aic23_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets), + .dapm_routes = tlv320aic23_intercon, + .num_dapm_routes = ARRAY_SIZE(tlv320aic23_intercon), + }, }; int tlv320aic23_probe(struct device *dev, struct regmap *regmap) diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c index 2c904d7150ad..14aa96d41719 100644 --- a/sound/soc/codecs/tlv320aic26.c +++ b/sound/soc/codecs/tlv320aic26.c @@ -321,12 +321,14 @@ static int aic26_probe(struct snd_soc_codec *codec) static struct snd_soc_codec_driver aic26_soc_codec_dev = { .probe = aic26_probe, - .controls = aic26_snd_controls, - .num_controls = ARRAY_SIZE(aic26_snd_controls), - .dapm_widgets = tlv320aic26_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(tlv320aic26_dapm_widgets), - .dapm_routes = tlv320aic26_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(tlv320aic26_dapm_routes), + .component_driver = { + .controls = aic26_snd_controls, + .num_controls = ARRAY_SIZE(aic26_snd_controls), + .dapm_widgets = tlv320aic26_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tlv320aic26_dapm_widgets), + .dapm_routes = tlv320aic26_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(tlv320aic26_dapm_routes), + }, }; static const struct regmap_config aic26_regmap = { diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index 3c5e1df01c19..f8a90ba8cd71 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -273,10 +273,20 @@ static const DECLARE_TLV_DB_SCALE(sp_vol_tlv, -6350, 50, 0); /* * controls to be exported to the user space */ -static const struct snd_kcontrol_new aic31xx_snd_controls[] = { +static const struct snd_kcontrol_new common31xx_snd_controls[] = { SOC_DOUBLE_R_S_TLV("DAC Playback Volume", AIC31XX_LDACVOL, AIC31XX_RDACVOL, 0, -127, 48, 7, 0, dac_vol_tlv), + SOC_DOUBLE_R("HP Driver Playback Switch", AIC31XX_HPLGAIN, + AIC31XX_HPRGAIN, 2, 1, 0), + SOC_DOUBLE_R_TLV("HP Driver Playback Volume", AIC31XX_HPLGAIN, + AIC31XX_HPRGAIN, 3, 0x09, 0, hp_drv_tlv), + + SOC_DOUBLE_R_TLV("HP Analog Playback Volume", AIC31XX_LANALOGHPL, + AIC31XX_RANALOGHPR, 0, 0x7F, 1, hp_vol_tlv), +}; + +static const struct snd_kcontrol_new aic31xx_snd_controls[] = { SOC_SINGLE_TLV("ADC Fine Capture Volume", AIC31XX_ADCFGA, 4, 4, 1, adc_fgain_tlv), @@ -286,14 +296,6 @@ static const struct snd_kcontrol_new aic31xx_snd_controls[] = { SOC_SINGLE_TLV("Mic PGA Capture Volume", AIC31XX_MICPGA, 0, 119, 0, mic_pga_tlv), - - SOC_DOUBLE_R("HP Driver Playback Switch", AIC31XX_HPLGAIN, - AIC31XX_HPRGAIN, 2, 1, 0), - SOC_DOUBLE_R_TLV("HP Driver Playback Volume", AIC31XX_HPLGAIN, - AIC31XX_HPRGAIN, 3, 0x09, 0, hp_drv_tlv), - - SOC_DOUBLE_R_TLV("HP Analog Playback Volume", AIC31XX_LANALOGHPL, - AIC31XX_RANALOGHPR, 0, 0x7F, 1, hp_vol_tlv), }; static const struct snd_kcontrol_new aic311x_snd_controls[] = { @@ -397,17 +399,28 @@ static int aic31xx_dapm_power_event(struct snd_soc_dapm_widget *w, return 0; } -static const struct snd_kcontrol_new left_output_switches[] = { +static const struct snd_kcontrol_new aic31xx_left_output_switches[] = { SOC_DAPM_SINGLE("From Left DAC", AIC31XX_DACMIXERROUTE, 6, 1, 0), SOC_DAPM_SINGLE("From MIC1LP", AIC31XX_DACMIXERROUTE, 5, 1, 0), SOC_DAPM_SINGLE("From MIC1RP", AIC31XX_DACMIXERROUTE, 4, 1, 0), }; -static const struct snd_kcontrol_new right_output_switches[] = { +static const struct snd_kcontrol_new aic31xx_right_output_switches[] = { SOC_DAPM_SINGLE("From Right DAC", AIC31XX_DACMIXERROUTE, 2, 1, 0), SOC_DAPM_SINGLE("From MIC1RP", AIC31XX_DACMIXERROUTE, 1, 1, 0), }; +static const struct snd_kcontrol_new dac31xx_left_output_switches[] = { + SOC_DAPM_SINGLE("From Left DAC", AIC31XX_DACMIXERROUTE, 6, 1, 0), + SOC_DAPM_SINGLE("From AIN1", AIC31XX_DACMIXERROUTE, 5, 1, 0), + SOC_DAPM_SINGLE("From AIN2", AIC31XX_DACMIXERROUTE, 4, 1, 0), +}; + +static const struct snd_kcontrol_new dac31xx_right_output_switches[] = { + SOC_DAPM_SINGLE("From Right DAC", AIC31XX_DACMIXERROUTE, 2, 1, 0), + SOC_DAPM_SINGLE("From AIN2", AIC31XX_DACMIXERROUTE, 1, 1, 0), +}; + static const struct snd_kcontrol_new p_term_mic1lp = SOC_DAPM_ENUM("MIC1LP P-Terminal", mic1lp_p_enum); @@ -457,7 +470,7 @@ static int mic_bias_event(struct snd_soc_dapm_widget *w, return 0; } -static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = { +static const struct snd_soc_dapm_widget common31xx_dapm_widgets[] = { SND_SOC_DAPM_AIF_IN("DAC IN", "DAC Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_MUX("DAC Left Input", @@ -473,14 +486,7 @@ static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = { AIC31XX_DACSETUP, 6, 0, aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - /* Output Mixers */ - SND_SOC_DAPM_MIXER("Output Left", SND_SOC_NOPM, 0, 0, - left_output_switches, - ARRAY_SIZE(left_output_switches)), - SND_SOC_DAPM_MIXER("Output Right", SND_SOC_NOPM, 0, 0, - right_output_switches, - ARRAY_SIZE(right_output_switches)), - + /* HP */ SND_SOC_DAPM_SWITCH("HP Left", SND_SOC_NOPM, 0, 0, &aic31xx_dapm_hpl_switch), SND_SOC_DAPM_SWITCH("HP Right", SND_SOC_NOPM, 0, 0, @@ -494,10 +500,34 @@ static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = { NULL, 0, aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), - /* ADC */ - SND_SOC_DAPM_ADC_E("ADC", "Capture", AIC31XX_ADCSETUP, 7, 0, - aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_POST_PMD), + /* Mic Bias */ + SND_SOC_DAPM_SUPPLY("MICBIAS", SND_SOC_NOPM, 0, 0, mic_bias_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), +}; + +static const struct snd_soc_dapm_widget dac31xx_dapm_widgets[] = { + /* Inputs */ + SND_SOC_DAPM_INPUT("AIN1"), + SND_SOC_DAPM_INPUT("AIN2"), + + /* Output Mixers */ + SND_SOC_DAPM_MIXER("Output Left", SND_SOC_NOPM, 0, 0, + dac31xx_left_output_switches, + ARRAY_SIZE(dac31xx_left_output_switches)), + SND_SOC_DAPM_MIXER("Output Right", SND_SOC_NOPM, 0, 0, + dac31xx_right_output_switches, + ARRAY_SIZE(dac31xx_right_output_switches)), +}; + +static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = { + /* Inputs */ + SND_SOC_DAPM_INPUT("MIC1LP"), + SND_SOC_DAPM_INPUT("MIC1RP"), + SND_SOC_DAPM_INPUT("MIC1LM"), /* Input Selection to MIC_PGA */ SND_SOC_DAPM_MUX("MIC1LP P-Terminal", SND_SOC_NOPM, 0, 0, @@ -507,24 +537,25 @@ static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = { SND_SOC_DAPM_MUX("MIC1LM P-Terminal", SND_SOC_NOPM, 0, 0, &p_term_mic1lm), + /* ADC */ + SND_SOC_DAPM_ADC_E("ADC", "Capture", AIC31XX_ADCSETUP, 7, 0, + aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("MIC1LM M-Terminal", SND_SOC_NOPM, 0, 0, &m_term_mic1lm), + /* Enabling & Disabling MIC Gain Ctl */ SND_SOC_DAPM_PGA("MIC_GAIN_CTL", AIC31XX_MICPGA, 7, 1, NULL, 0), - /* Mic Bias */ - SND_SOC_DAPM_SUPPLY("MICBIAS", SND_SOC_NOPM, 0, 0, mic_bias_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - - /* Outputs */ - SND_SOC_DAPM_OUTPUT("HPL"), - SND_SOC_DAPM_OUTPUT("HPR"), - - /* Inputs */ - SND_SOC_DAPM_INPUT("MIC1LP"), - SND_SOC_DAPM_INPUT("MIC1RP"), - SND_SOC_DAPM_INPUT("MIC1LM"), + /* Output Mixers */ + SND_SOC_DAPM_MIXER("Output Left", SND_SOC_NOPM, 0, 0, + aic31xx_left_output_switches, + ARRAY_SIZE(aic31xx_left_output_switches)), + SND_SOC_DAPM_MIXER("Output Right", SND_SOC_NOPM, 0, 0, + aic31xx_right_output_switches, + ARRAY_SIZE(aic31xx_right_output_switches)), }; static const struct snd_soc_dapm_widget aic311x_dapm_widgets[] = { @@ -554,7 +585,7 @@ static const struct snd_soc_dapm_widget aic310x_dapm_widgets[] = { }; static const struct snd_soc_dapm_route -aic31xx_audio_map[] = { +common31xx_audio_map[] = { /* DAC Input Routing */ {"DAC Left Input", "Left Data", "DAC IN"}, {"DAC Left Input", "Right Data", "DAC IN"}, @@ -565,6 +596,31 @@ aic31xx_audio_map[] = { {"DAC Left", NULL, "DAC Left Input"}, {"DAC Right", NULL, "DAC Right Input"}, + /* HPL path */ + {"HP Left", "Switch", "Output Left"}, + {"HPL Driver", NULL, "HP Left"}, + {"HPL", NULL, "HPL Driver"}, + + /* HPR path */ + {"HP Right", "Switch", "Output Right"}, + {"HPR Driver", NULL, "HP Right"}, + {"HPR", NULL, "HPR Driver"}, +}; + +static const struct snd_soc_dapm_route +dac31xx_audio_map[] = { + /* Left Output */ + {"Output Left", "From Left DAC", "DAC Left"}, + {"Output Left", "From AIN1", "AIN1"}, + {"Output Left", "From AIN2", "AIN2"}, + + /* Right Output */ + {"Output Right", "From Right DAC", "DAC Right"}, + {"Output Right", "From AIN2", "AIN2"}, +}; + +static const struct snd_soc_dapm_route +aic31xx_audio_map[] = { /* Mic input */ {"MIC1LP P-Terminal", "FFR 10 Ohm", "MIC1LP"}, {"MIC1LP P-Terminal", "FFR 20 Ohm", "MIC1LP"}, @@ -595,16 +651,6 @@ aic31xx_audio_map[] = { /* Right Output */ {"Output Right", "From Right DAC", "DAC Right"}, {"Output Right", "From MIC1RP", "MIC1RP"}, - - /* HPL path */ - {"HP Left", "Switch", "Output Left"}, - {"HPL Driver", NULL, "HP Left"}, - {"HPL", NULL, "HPL Driver"}, - - /* HPR path */ - {"HP Right", "Switch", "Output Right"}, - {"HPR Driver", NULL, "HP Right"}, - {"HPR", NULL, "HPR Driver"}, }; static const struct snd_soc_dapm_route @@ -633,6 +679,13 @@ static int aic31xx_add_controls(struct snd_soc_codec *codec) int ret = 0; struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec); + if (!(aic31xx->pdata.codec_type & DAC31XX_BIT)) + ret = snd_soc_add_codec_controls( + codec, aic31xx_snd_controls, + ARRAY_SIZE(aic31xx_snd_controls)); + if (ret) + return ret; + if (aic31xx->pdata.codec_type & AIC31XX_STEREO_CLASS_D_BIT) ret = snd_soc_add_codec_controls( codec, aic311x_snd_controls, @@ -651,6 +704,30 @@ static int aic31xx_add_widgets(struct snd_soc_codec *codec) struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec); int ret = 0; + if (aic31xx->pdata.codec_type & DAC31XX_BIT) { + ret = snd_soc_dapm_new_controls( + dapm, dac31xx_dapm_widgets, + ARRAY_SIZE(dac31xx_dapm_widgets)); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, dac31xx_audio_map, + ARRAY_SIZE(dac31xx_audio_map)); + if (ret) + return ret; + } else { + ret = snd_soc_dapm_new_controls( + dapm, aic31xx_dapm_widgets, + ARRAY_SIZE(aic31xx_dapm_widgets)); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, aic31xx_audio_map, + ARRAY_SIZE(aic31xx_audio_map)); + if (ret) + return ret; + } + if (aic31xx->pdata.codec_type & AIC31XX_STEREO_CLASS_D_BIT) { ret = snd_soc_dapm_new_controls( dapm, aic311x_dapm_widgets, @@ -1114,12 +1191,14 @@ static struct snd_soc_codec_driver soc_codec_driver_aic31xx = { .set_bias_level = aic31xx_set_bias_level, .suspend_bias_off = true, - .controls = aic31xx_snd_controls, - .num_controls = ARRAY_SIZE(aic31xx_snd_controls), - .dapm_widgets = aic31xx_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(aic31xx_dapm_widgets), - .dapm_routes = aic31xx_audio_map, - .num_dapm_routes = ARRAY_SIZE(aic31xx_audio_map), + .component_driver = { + .controls = common31xx_snd_controls, + .num_controls = ARRAY_SIZE(common31xx_snd_controls), + .dapm_widgets = common31xx_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(common31xx_dapm_widgets), + .dapm_routes = common31xx_audio_map, + .num_dapm_routes = ARRAY_SIZE(common31xx_audio_map), + }, }; static const struct snd_soc_dai_ops aic31xx_dai_ops = { @@ -1129,19 +1208,34 @@ static const struct snd_soc_dai_ops aic31xx_dai_ops = { .digital_mute = aic31xx_dac_mute, }; +static struct snd_soc_dai_driver dac31xx_dai_driver[] = { + { + .name = "tlv32dac31xx-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = AIC31XX_RATES, + .formats = AIC31XX_FORMATS, + }, + .ops = &aic31xx_dai_ops, + .symmetric_rates = 1, + } +}; + static struct snd_soc_dai_driver aic31xx_dai_driver[] = { { .name = "tlv320aic31xx-hifi", .playback = { .stream_name = "Playback", - .channels_min = 1, + .channels_min = 2, .channels_max = 2, .rates = AIC31XX_RATES, .formats = AIC31XX_FORMATS, }, .capture = { .stream_name = "Capture", - .channels_min = 1, + .channels_min = 2, .channels_max = 2, .rates = AIC31XX_RATES, .formats = AIC31XX_FORMATS, @@ -1159,6 +1253,8 @@ static const struct of_device_id tlv320aic31xx_of_match[] = { { .compatible = "ti,tlv320aic3110" }, { .compatible = "ti,tlv320aic3120" }, { .compatible = "ti,tlv320aic3111" }, + { .compatible = "ti,tlv320dac3100" }, + { .compatible = "ti,tlv320dac3101" }, {}, }; MODULE_DEVICE_TABLE(of, tlv320aic31xx_of_match); @@ -1259,9 +1355,16 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c, if (ret) return ret; - return snd_soc_register_codec(&i2c->dev, &soc_codec_driver_aic31xx, - aic31xx_dai_driver, - ARRAY_SIZE(aic31xx_dai_driver)); + if (aic31xx->pdata.codec_type & DAC31XX_BIT) + return snd_soc_register_codec(&i2c->dev, + &soc_codec_driver_aic31xx, + dac31xx_dai_driver, + ARRAY_SIZE(dac31xx_dai_driver)); + else + return snd_soc_register_codec(&i2c->dev, + &soc_codec_driver_aic31xx, + aic31xx_dai_driver, + ARRAY_SIZE(aic31xx_dai_driver)); } static int aic31xx_i2c_remove(struct i2c_client *i2c) @@ -1277,6 +1380,8 @@ static const struct i2c_device_id aic31xx_i2c_id[] = { { "tlv320aic3110", AIC3110 }, { "tlv320aic3120", AIC3120 }, { "tlv320aic3111", AIC3111 }, + { "tlv320dac3100", DAC3100 }, + { "tlv320dac3101", DAC3101 }, { } }; MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id); diff --git a/sound/soc/codecs/tlv320aic31xx.h b/sound/soc/codecs/tlv320aic31xx.h index ac9b146526eb..730fb2058869 100644 --- a/sound/soc/codecs/tlv320aic31xx.h +++ b/sound/soc/codecs/tlv320aic31xx.h @@ -24,12 +24,15 @@ #define AIC31XX_STEREO_CLASS_D_BIT 0x1 #define AIC31XX_MINIDSP_BIT 0x2 +#define DAC31XX_BIT 0x4 enum aic31xx_type { AIC3100 = 0, AIC3110 = AIC31XX_STEREO_CLASS_D_BIT, AIC3120 = AIC31XX_MINIDSP_BIT, AIC3111 = (AIC31XX_STEREO_CLASS_D_BIT | AIC31XX_MINIDSP_BIT), + DAC3100 = DAC31XX_BIT, + DAC3101 = DAC31XX_BIT | AIC31XX_STEREO_CLASS_D_BIT, }; struct aic31xx_pdata { diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 85d4978d0384..28fdfc5ec544 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -797,12 +797,14 @@ static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = { .set_bias_level = aic32x4_set_bias_level, .suspend_bias_off = true, - .controls = aic32x4_snd_controls, - .num_controls = ARRAY_SIZE(aic32x4_snd_controls), - .dapm_widgets = aic32x4_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(aic32x4_dapm_widgets), - .dapm_routes = aic32x4_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(aic32x4_dapm_routes), + .component_driver = { + .controls = aic32x4_snd_controls, + .num_controls = ARRAY_SIZE(aic32x4_snd_controls), + .dapm_widgets = aic32x4_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(aic32x4_dapm_widgets), + .dapm_routes = aic32x4_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(aic32x4_dapm_routes), + }, }; static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4, diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index a564759845f9..bb94d50052d7 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -126,6 +126,16 @@ static const struct reg_default aic3x_reg[] = { { 108, 0x00 }, { 109, 0x00 }, }; +static bool aic3x_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AIC3X_RESET: + return true; + default: + return false; + } +} + static const struct regmap_config aic3x_regmap = { .reg_bits = 8, .val_bits = 8, @@ -133,6 +143,9 @@ static const struct regmap_config aic3x_regmap = { .max_register = DAC_ICC_ADJ, .reg_defaults = aic3x_reg, .num_reg_defaults = ARRAY_SIZE(aic3x_reg), + + .volatile_reg = aic3x_volatile_reg, + .cache_type = REGCACHE_RBTREE, }; @@ -157,7 +170,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; unsigned short val; - struct snd_soc_dapm_update update; + struct snd_soc_dapm_update update = { 0 }; int connect, change; val = (ucontrol->value.integer.value[0] & mask); @@ -1670,12 +1683,14 @@ static struct snd_soc_codec_driver soc_codec_dev_aic3x = { .idle_bias_off = true, .probe = aic3x_probe, .remove = aic3x_remove, - .controls = aic3x_snd_controls, - .num_controls = ARRAY_SIZE(aic3x_snd_controls), - .dapm_widgets = aic3x_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(aic3x_dapm_widgets), - .dapm_routes = intercon, - .num_dapm_routes = ARRAY_SIZE(intercon), + .component_driver = { + .controls = aic3x_snd_controls, + .num_controls = ARRAY_SIZE(aic3x_snd_controls), + .dapm_widgets = aic3x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(aic3x_dapm_widgets), + .dapm_routes = intercon, + .num_dapm_routes = ARRAY_SIZE(intercon), + }, }; /* diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index f7a6ce7e5fb1..7bcf01efdf9a 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -90,7 +90,6 @@ static const char *dac33_supply_names[DAC33_NUM_SUPPLIES] = { struct tlv320dac33_priv { struct mutex mutex; - struct workqueue_struct *dac33_wq; struct work_struct work; struct snd_soc_codec *codec; struct regulator_bulk_data supplies[DAC33_NUM_SUPPLIES]; @@ -771,7 +770,7 @@ static irqreturn_t dac33_interrupt_handler(int irq, void *dev) /* Do not schedule the workqueue in Mode7 */ if (dac33->fifo_mode != DAC33_FIFO_MODE7) - queue_work(dac33->dac33_wq, &dac33->work); + schedule_work(&dac33->work); return IRQ_HANDLED; } @@ -1127,7 +1126,7 @@ static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (dac33->fifo_mode) { dac33->state = DAC33_PREFILL; - queue_work(dac33->dac33_wq, &dac33->work); + schedule_work(&dac33->work); } break; case SNDRV_PCM_TRIGGER_STOP: @@ -1135,7 +1134,7 @@ static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_PAUSE_PUSH: if (dac33->fifo_mode) { dac33->state = DAC33_FLUSH; - queue_work(dac33->dac33_wq, &dac33->work); + schedule_work(&dac33->work); } break; default: @@ -1410,14 +1409,6 @@ static int dac33_soc_probe(struct snd_soc_codec *codec) dac33->irq = -1; } if (dac33->irq != -1) { - /* Setup work queue */ - dac33->dac33_wq = - create_singlethread_workqueue("tlv320dac33"); - if (dac33->dac33_wq == NULL) { - free_irq(dac33->irq, codec); - return -ENOMEM; - } - INIT_WORK(&dac33->work, dac33_work); } } @@ -1437,7 +1428,7 @@ static int dac33_soc_remove(struct snd_soc_codec *codec) if (dac33->irq >= 0) { free_irq(dac33->irq, dac33->codec); - destroy_workqueue(dac33->dac33_wq); + flush_work(&dac33->work); } return 0; } @@ -1453,12 +1444,14 @@ static struct snd_soc_codec_driver soc_codec_dev_tlv320dac33 = { .probe = dac33_soc_probe, .remove = dac33_soc_remove, - .controls = dac33_snd_controls, - .num_controls = ARRAY_SIZE(dac33_snd_controls), - .dapm_widgets = dac33_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(dac33_dapm_widgets), - .dapm_routes = audio_map, - .num_dapm_routes = ARRAY_SIZE(audio_map), + .component_driver = { + .controls = dac33_snd_controls, + .num_controls = ARRAY_SIZE(dac33_snd_controls), + .dapm_widgets = dac33_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(dac33_dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), + }, }; #define DAC33_RATES (SNDRV_PCM_RATE_44100 | \ diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c index f1ea052a822e..2e014c80d113 100644 --- a/sound/soc/codecs/tpa6130a2.c +++ b/sound/soc/codecs/tpa6130a2.c @@ -52,7 +52,7 @@ struct tpa6130a2_data { static int tpa6130a2_power(struct tpa6130a2_data *data, bool enable) { - int ret; + int ret = 0, ret2; if (enable) { ret = regulator_enable(data->supply); @@ -64,7 +64,30 @@ static int tpa6130a2_power(struct tpa6130a2_data *data, bool enable) /* Power on */ if (data->power_gpio >= 0) gpio_set_value(data->power_gpio, 1); + + /* Sync registers */ + regcache_cache_only(data->regmap, false); + ret = regcache_sync(data->regmap); + if (ret != 0) { + dev_err(data->dev, + "Failed to sync registers: %d\n", ret); + regcache_cache_only(data->regmap, true); + if (data->power_gpio >= 0) + gpio_set_value(data->power_gpio, 0); + ret2 = regulator_disable(data->supply); + if (ret2 != 0) + dev_err(data->dev, + "Failed to disable supply: %d\n", ret2); + return ret; + } } else { + /* Powered off device does not retain registers. While device + * is off, any register updates (i.e. volume changes) should + * happen in cache only. + */ + regcache_mark_dirty(data->regmap); + regcache_cache_only(data->regmap, true); + /* Power off */ if (data->power_gpio >= 0) gpio_set_value(data->power_gpio, 0); @@ -75,9 +98,6 @@ static int tpa6130a2_power(struct tpa6130a2_data *data, bool enable) "Failed to disable supply: %d\n", ret); return ret; } - - /* device regs does not match the cache state anymore */ - regcache_mark_dirty(data->regmap); } return ret; @@ -88,25 +108,14 @@ static int tpa6130a2_power_event(struct snd_soc_dapm_widget *w, { struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); struct tpa6130a2_data *data = snd_soc_component_get_drvdata(c); - int ret; - /* before widget power up */ if (SND_SOC_DAPM_EVENT_ON(event)) { - /* Turn on the chip */ - tpa6130a2_power(data, true); - /* Sync the registers */ - ret = regcache_sync(data->regmap); - if (ret < 0) { - dev_err(c->dev, "Failed to initialize chip\n"); - tpa6130a2_power(data, false); - return ret; - } - /* after widget power down */ + /* Before widget power up: turn chip on, sync registers */ + return tpa6130a2_power(data, true); } else { - tpa6130a2_power(data, false); + /* After widget power down: turn chip off */ + return tpa6130a2_power(data, false); } - - return 0; } /* @@ -190,7 +199,7 @@ static const struct snd_soc_dapm_route tpa6130a2_dapm_routes[] = { { "Right PGA", NULL, "Power" }, }; -struct snd_soc_component_driver tpa6130a2_component_driver = { +static const struct snd_soc_component_driver tpa6130a2_component_driver = { .name = "tpa6130a2", .probe = tpa6130a2_component_probe, .dapm_widgets = tpa6130a2_dapm_widgets, diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index a5a4e9f75c57..a2104d68169d 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -2199,12 +2199,14 @@ static struct snd_soc_codec_driver soc_codec_dev_twl4030 = { .set_bias_level = twl4030_set_bias_level, .idle_bias_off = true, - .controls = twl4030_snd_controls, - .num_controls = ARRAY_SIZE(twl4030_snd_controls), - .dapm_widgets = twl4030_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(twl4030_dapm_widgets), - .dapm_routes = intercon, - .num_dapm_routes = ARRAY_SIZE(intercon), + .component_driver = { + .controls = twl4030_snd_controls, + .num_controls = ARRAY_SIZE(twl4030_snd_controls), + .dapm_widgets = twl4030_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(twl4030_dapm_widgets), + .dapm_routes = intercon, + .num_dapm_routes = ARRAY_SIZE(intercon), + }, }; static int twl4030_codec_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 1f7081043566..748036e851ea 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -1156,12 +1156,14 @@ static struct snd_soc_codec_driver soc_codec_dev_twl6040 = { .suspend_bias_off = true, .ignore_pmdown_time = true, - .controls = twl6040_snd_controls, - .num_controls = ARRAY_SIZE(twl6040_snd_controls), - .dapm_widgets = twl6040_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(twl6040_dapm_widgets), - .dapm_routes = intercon, - .num_dapm_routes = ARRAY_SIZE(intercon), + .component_driver = { + .controls = twl6040_snd_controls, + .num_controls = ARRAY_SIZE(twl6040_snd_controls), + .dapm_widgets = twl6040_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(twl6040_dapm_widgets), + .dapm_routes = intercon, + .num_dapm_routes = ARRAY_SIZE(intercon), + }, }; static int twl6040_codec_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c index e4c694c758b8..5fdee874406d 100644 --- a/sound/soc/codecs/uda134x.c +++ b/sound/soc/codecs/uda134x.c @@ -523,10 +523,12 @@ static struct snd_soc_codec_driver soc_codec_dev_uda134x = { .set_bias_level = uda134x_set_bias_level, .suspend_bias_off = true, - .dapm_widgets = uda134x_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(uda134x_dapm_widgets), - .dapm_routes = uda134x_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(uda134x_dapm_routes), + .component_driver = { + .dapm_widgets = uda134x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(uda134x_dapm_widgets), + .dapm_routes = uda134x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(uda134x_dapm_routes), + }, }; static const struct regmap_config uda134x_regmap_config = { @@ -544,6 +546,7 @@ static int uda134x_codec_probe(struct platform_device *pdev) { struct uda134x_platform_data *pd = pdev->dev.platform_data; struct uda134x_priv *uda134x; + int ret; if (!pd) { dev_err(&pdev->dev, "Missing L3 bitbang function\n"); @@ -557,6 +560,12 @@ static int uda134x_codec_probe(struct platform_device *pdev) uda134x->pd = pd; platform_set_drvdata(pdev, uda134x); + if (pd->l3.use_gpios) { + ret = l3_set_gpio_ops(&pdev->dev, &uda134x->pd->l3); + if (ret < 0) + return ret; + } + uda134x->regmap = devm_regmap_init(&pdev->dev, NULL, pd, &uda134x_regmap_config); if (IS_ERR(uda134x->regmap)) diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index 35f0469ebb16..2918fdb95e58 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -698,25 +698,10 @@ static int uda1380_probe(struct snd_soc_codec *codec) codec->hw_write = (hw_write_t)i2c_master_send; codec->control_data = uda1380->control_data; - if (!pdata) - return -EINVAL; - - if (gpio_is_valid(pdata->gpio_reset)) { - ret = gpio_request_one(pdata->gpio_reset, GPIOF_OUT_INIT_LOW, - "uda1380 reset"); - if (ret) - goto err_out; - } - - if (gpio_is_valid(pdata->gpio_power)) { - ret = gpio_request_one(pdata->gpio_power, GPIOF_OUT_INIT_LOW, - "uda1380 power"); - if (ret) - goto err_free_gpio; - } else { + if (!gpio_is_valid(pdata->gpio_power)) { ret = uda1380_reset(codec); if (ret) - goto err_free_gpio; + return ret; } INIT_WORK(&uda1380->work, uda1380_flush_work); @@ -733,28 +718,10 @@ static int uda1380_probe(struct snd_soc_codec *codec) } return 0; - -err_free_gpio: - if (gpio_is_valid(pdata->gpio_reset)) - gpio_free(pdata->gpio_reset); -err_out: - return ret; -} - -/* power down chip */ -static int uda1380_remove(struct snd_soc_codec *codec) -{ - struct uda1380_platform_data *pdata =codec->dev->platform_data; - - gpio_free(pdata->gpio_reset); - gpio_free(pdata->gpio_power); - - return 0; } static struct snd_soc_codec_driver soc_codec_dev_uda1380 = { .probe = uda1380_probe, - .remove = uda1380_remove, .read = uda1380_read_reg_cache, .write = uda1380_write, .set_bias_level = uda1380_set_bias_level, @@ -765,26 +732,45 @@ static struct snd_soc_codec_driver soc_codec_dev_uda1380 = { .reg_cache_default = uda1380_reg, .reg_cache_step = 1, - .controls = uda1380_snd_controls, - .num_controls = ARRAY_SIZE(uda1380_snd_controls), - .dapm_widgets = uda1380_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(uda1380_dapm_widgets), - .dapm_routes = uda1380_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(uda1380_dapm_routes), + .component_driver = { + .controls = uda1380_snd_controls, + .num_controls = ARRAY_SIZE(uda1380_snd_controls), + .dapm_widgets = uda1380_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(uda1380_dapm_widgets), + .dapm_routes = uda1380_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(uda1380_dapm_routes), + }, }; -#if IS_ENABLED(CONFIG_I2C) static int uda1380_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { + struct uda1380_platform_data *pdata = i2c->dev.platform_data; struct uda1380_priv *uda1380; int ret; + if (!pdata) + return -EINVAL; + uda1380 = devm_kzalloc(&i2c->dev, sizeof(struct uda1380_priv), GFP_KERNEL); if (uda1380 == NULL) return -ENOMEM; + if (gpio_is_valid(pdata->gpio_reset)) { + ret = devm_gpio_request_one(&i2c->dev, pdata->gpio_reset, + GPIOF_OUT_INIT_LOW, "uda1380 reset"); + if (ret) + return ret; + } + + if (gpio_is_valid(pdata->gpio_power)) { + ret = devm_gpio_request_one(&i2c->dev, pdata->gpio_power, + GPIOF_OUT_INIT_LOW, "uda1380 power"); + if (ret) + return ret; + } + i2c_set_clientdata(i2c, uda1380); uda1380->control_data = i2c; @@ -813,27 +799,8 @@ static struct i2c_driver uda1380_i2c_driver = { .remove = uda1380_i2c_remove, .id_table = uda1380_i2c_id, }; -#endif -static int __init uda1380_modinit(void) -{ - int ret = 0; -#if IS_ENABLED(CONFIG_I2C) - ret = i2c_add_driver(&uda1380_i2c_driver); - if (ret != 0) - pr_err("Failed to register UDA1380 I2C driver: %d\n", ret); -#endif - return ret; -} -module_init(uda1380_modinit); - -static void __exit uda1380_exit(void) -{ -#if IS_ENABLED(CONFIG_I2C) - i2c_del_driver(&uda1380_i2c_driver); -#endif -} -module_exit(uda1380_exit); +module_i2c_driver(uda1380_i2c_driver); MODULE_AUTHOR("Giorgio Padrin"); MODULE_DESCRIPTION("Audio support for codec Philips UDA1380"); diff --git a/sound/soc/codecs/uda1380.h b/sound/soc/codecs/uda1380.h index 942e3927c72b..69a326ac3c1a 100644 --- a/sound/soc/codecs/uda1380.h +++ b/sound/soc/codecs/uda1380.h @@ -72,8 +72,4 @@ #define R22_SKIP_DCFIL 0x0002 #define R23_AGC_EN 0x0001 -#define UDA1380_DAI_DUPLEX 0 /* playback and capture on single DAI */ -#define UDA1380_DAI_PLAYBACK 1 /* playback DAI */ -#define UDA1380_DAI_CAPTURE 2 /* capture DAI */ - #endif /* _UDA1380_H */ diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c index 1b79778098d2..fcffb6e707d9 100644 --- a/sound/soc/codecs/wl1273.c +++ b/sound/soc/codecs/wl1273.c @@ -484,12 +484,14 @@ static struct snd_soc_codec_driver soc_codec_dev_wl1273 = { .probe = wl1273_probe, .remove = wl1273_remove, - .controls = wl1273_controls, - .num_controls = ARRAY_SIZE(wl1273_controls), - .dapm_widgets = wl1273_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wl1273_dapm_widgets), - .dapm_routes = wl1273_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wl1273_dapm_routes), + .component_driver = { + .controls = wl1273_controls, + .num_controls = ARRAY_SIZE(wl1273_controls), + .dapm_widgets = wl1273_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wl1273_dapm_widgets), + .dapm_routes = wl1273_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wl1273_dapm_routes), + }, }; static int wl1273_platform_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index e3c34bdc2772..0eb5dcf4c29d 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -789,16 +789,18 @@ static int wm0010_set_sysclk(struct snd_soc_codec *codec, int source, static int wm0010_probe(struct snd_soc_codec *codec); -static struct snd_soc_codec_driver soc_codec_dev_wm0010 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm0010 = { .probe = wm0010_probe, .set_bias_level = wm0010_set_bias_level, .set_sysclk = wm0010_set_sysclk, .idle_bias_off = true, - .dapm_widgets = wm0010_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm0010_dapm_widgets), - .dapm_routes = wm0010_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm0010_dapm_routes), + .component_driver = { + .dapm_widgets = wm0010_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm0010_dapm_widgets), + .dapm_routes = wm0010_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm0010_dapm_routes), + }, }; #define WM0010_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) diff --git a/sound/soc/codecs/wm1250-ev1.c b/sound/soc/codecs/wm1250-ev1.c index ec45c5b220b1..cf5f0580df6a 100644 --- a/sound/soc/codecs/wm1250-ev1.c +++ b/sound/soc/codecs/wm1250-ev1.c @@ -141,12 +141,13 @@ static struct snd_soc_dai_driver wm1250_ev1_dai = { .ops = &wm1250_ev1_ops, }; -static struct snd_soc_codec_driver soc_codec_dev_wm1250_ev1 = { - .dapm_widgets = wm1250_ev1_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm1250_ev1_dapm_widgets), - .dapm_routes = wm1250_ev1_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm1250_ev1_dapm_routes), - +static const struct snd_soc_codec_driver soc_codec_dev_wm1250_ev1 = { + .component_driver = { + .dapm_widgets = wm1250_ev1_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm1250_ev1_dapm_widgets), + .dapm_routes = wm1250_ev1_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm1250_ev1_dapm_routes), + }, .set_bias_level = wm1250_ev1_set_bias_level, .idle_bias_off = true, }; diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index f2664396be6f..23cde3a0dc11 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -799,18 +799,20 @@ static int wm2000_remove(struct snd_soc_codec *codec) return wm2000_anc_transition(wm2000, ANC_OFF); } -static struct snd_soc_codec_driver soc_codec_dev_wm2000 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm2000 = { .probe = wm2000_probe, .remove = wm2000_remove, .suspend = wm2000_suspend, .resume = wm2000_resume, - .dapm_widgets = wm2000_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm2000_dapm_widgets), - .dapm_routes = wm2000_audio_map, - .num_dapm_routes = ARRAY_SIZE(wm2000_audio_map), - .controls = wm2000_controls, - .num_controls = ARRAY_SIZE(wm2000_controls), + .component_driver = { + .controls = wm2000_controls, + .num_controls = ARRAY_SIZE(wm2000_controls), + .dapm_widgets = wm2000_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm2000_dapm_widgets), + .dapm_routes = wm2000_audio_map, + .num_dapm_routes = ARRAY_SIZE(wm2000_audio_map), + }, }; static int wm2000_i2c_probe(struct i2c_client *i2c, diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index fd1439ecb50a..d83dab57a1d1 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -999,7 +999,7 @@ static DECLARE_TLV_DB_SCALE(in_tlv, -6300, 100, 0); static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); static DECLARE_TLV_DB_SCALE(out_tlv, -6400, 100, 0); -static const char *wm2200_mixer_texts[] = { +static const char * const wm2200_mixer_texts[] = { "None", "Tone Generator", "AEC Loopback", @@ -1033,7 +1033,7 @@ static const char *wm2200_mixer_texts[] = { "DSP2.6", }; -static int wm2200_mixer_values[] = { +static unsigned int wm2200_mixer_values[] = { 0x00, 0x04, /* Tone */ 0x08, /* AEC */ @@ -2103,7 +2103,7 @@ static struct snd_soc_dai_driver wm2200_dai = { .ops = &wm2200_dai_ops, }; -static struct snd_soc_codec_driver soc_codec_wm2200 = { +static const struct snd_soc_codec_driver soc_codec_wm2200 = { .probe = wm2200_probe, .idle_bias_off = true, @@ -2111,12 +2111,14 @@ static struct snd_soc_codec_driver soc_codec_wm2200 = { .set_sysclk = wm2200_set_sysclk, .set_pll = wm2200_set_fll, - .controls = wm2200_snd_controls, - .num_controls = ARRAY_SIZE(wm2200_snd_controls), - .dapm_widgets = wm2200_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm2200_dapm_widgets), - .dapm_routes = wm2200_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm2200_dapm_routes), + .component_driver = { + .controls = wm2200_snd_controls, + .num_controls = ARRAY_SIZE(wm2200_snd_controls), + .dapm_widgets = wm2200_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm2200_dapm_widgets), + .dapm_routes = wm2200_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm2200_dapm_routes), + }, }; static irqreturn_t wm2200_irq(int irq, void *data) diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index 512a9d25fe6f..560575000cc5 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -2285,7 +2285,7 @@ static int wm5100_gpio_direction_in(struct gpio_chip *chip, unsigned offset) (1 << WM5100_GP1_DIR_SHIFT)); } -static struct gpio_chip wm5100_template_chip = { +static const struct gpio_chip wm5100_template_chip = { .label = "wm5100", .owner = THIS_MODULE, .direction_output = wm5100_gpio_direction_out, @@ -2381,7 +2381,7 @@ static int wm5100_remove(struct snd_soc_codec *codec) return 0; } -static struct snd_soc_codec_driver soc_codec_dev_wm5100 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm5100 = { .probe = wm5100_probe, .remove = wm5100_remove, @@ -2390,12 +2390,14 @@ static struct snd_soc_codec_driver soc_codec_dev_wm5100 = { .idle_bias_off = 1, .seq_notifier = wm5100_seq_notifier, - .controls = wm5100_snd_controls, - .num_controls = ARRAY_SIZE(wm5100_snd_controls), - .dapm_widgets = wm5100_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm5100_dapm_widgets), - .dapm_routes = wm5100_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm5100_dapm_routes), + .component_driver = { + .controls = wm5100_snd_controls, + .num_controls = ARRAY_SIZE(wm5100_snd_controls), + .dapm_widgets = wm5100_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm5100_dapm_widgets), + .dapm_routes = wm5100_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm5100_dapm_routes), + }, }; static const struct regmap_config wm5100_regmap = { diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 846deed6af41..e7ab37d0dd32 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -607,6 +607,9 @@ static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w, break; case SND_SOC_DAPM_PRE_PMD: break; + case SND_SOC_DAPM_PRE_PMU: + case SND_SOC_DAPM_POST_PMD: + return arizona_clk_ev(w, kcontrol, event); default: return 0; } @@ -1077,9 +1080,11 @@ static const struct snd_kcontrol_new wm5102_aec_loopback_mux = static const struct snd_soc_dapm_widget wm5102_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT, 0, wm5102_sysclk_ev, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1, - ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0), + ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK, ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK, @@ -1521,6 +1526,16 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = { { "IN3L", NULL, "SYSCLK" }, { "IN3R", NULL, "SYSCLK" }, + { "ASRC1L", NULL, "SYSCLK" }, + { "ASRC1R", NULL, "SYSCLK" }, + { "ASRC2L", NULL, "SYSCLK" }, + { "ASRC2R", NULL, "SYSCLK" }, + + { "ASRC1L", NULL, "ASYNCCLK" }, + { "ASRC1R", NULL, "ASYNCCLK" }, + { "ASRC2L", NULL, "ASYNCCLK" }, + { "ASRC2R", NULL, "ASYNCCLK" }, + { "MICBIAS1", NULL, "MICVDD" }, { "MICBIAS2", NULL, "MICVDD" }, { "MICBIAS3", NULL, "MICVDD" }, @@ -1600,7 +1615,6 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = { { "Slim3 Capture", NULL, "SYSCLK" }, { "Audio Trace DSP", NULL, "DSP1" }, - { "Audio Trace DSP", NULL, "SYSCLK" }, { "IN1L PGA", NULL, "IN1L" }, { "IN1R PGA", NULL, "IN1R" }, @@ -1894,7 +1908,7 @@ static struct snd_soc_dai_driver wm5102_dai[] = { static int wm5102_open(struct snd_compr_stream *stream) { struct snd_soc_pcm_runtime *rtd = stream->private_data; - struct wm5102_priv *priv = snd_soc_codec_get_drvdata(rtd->codec); + struct wm5102_priv *priv = snd_soc_platform_get_drvdata(rtd->platform); return wm_adsp_compr_open(&priv->core.adsp[0], stream); } @@ -1917,18 +1931,10 @@ static irqreturn_t wm5102_adsp2_irq(int irq, void *data) static int wm5102_codec_probe(struct snd_soc_codec *codec) { struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec); - struct arizona *arizona = priv->core.arizona; int ret; - ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, - "ADSP2 Compressed IRQ", wm5102_adsp2_irq, - priv); - if (ret != 0) { - dev_err(codec->dev, "Failed to request DSP IRQ: %d\n", ret); - return ret; - } - ret = wm_adsp2_codec_probe(&priv->core.adsp[0], codec); if (ret) return ret; @@ -1940,8 +1946,9 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec) arizona_init_spk(codec); arizona_init_gpio(codec); + arizona_init_notifiers(codec); - snd_soc_dapm_disable_pin(dapm, "HAPTICS"); + snd_soc_component_disable_pin(component, "HAPTICS"); priv->core.arizona->dapm = dapm; @@ -1956,16 +1963,11 @@ err_adsp2_codec_probe: static int wm5102_codec_remove(struct snd_soc_codec *codec) { struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec); - struct arizona *arizona = priv->core.arizona; wm_adsp2_codec_remove(&priv->core.adsp[0], codec); priv->core.arizona->dapm = NULL; - arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv); - - arizona_free_spk(codec); - return 0; } @@ -1990,7 +1992,7 @@ static struct regmap *wm5102_get_regmap(struct device *dev) return priv->core.arizona->regmap; } -static struct snd_soc_codec_driver soc_codec_dev_wm5102 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm5102 = { .probe = wm5102_codec_probe, .remove = wm5102_codec_remove, .get_regmap = wm5102_get_regmap, @@ -2000,12 +2002,14 @@ static struct snd_soc_codec_driver soc_codec_dev_wm5102 = { .set_sysclk = arizona_set_sysclk, .set_pll = wm5102_set_fll, - .controls = wm5102_snd_controls, - .num_controls = ARRAY_SIZE(wm5102_snd_controls), - .dapm_widgets = wm5102_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm5102_dapm_widgets), - .dapm_routes = wm5102_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm5102_dapm_routes), + .component_driver = { + .controls = wm5102_snd_controls, + .num_controls = ARRAY_SIZE(wm5102_snd_controls), + .dapm_widgets = wm5102_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm5102_dapm_widgets), + .dapm_routes = wm5102_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm5102_dapm_routes), + }, }; static struct snd_compr_ops wm5102_compr_ops = { @@ -2081,25 +2085,47 @@ static int wm5102_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_idle(&pdev->dev); + ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, + "ADSP2 Compressed IRQ", wm5102_adsp2_irq, + wm5102); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret); + return ret; + } + + ret = arizona_init_spk_irqs(arizona); + if (ret < 0) + goto err_dsp_irq; + ret = snd_soc_register_platform(&pdev->dev, &wm5102_compr_platform); if (ret < 0) { dev_err(&pdev->dev, "Failed to register platform: %d\n", ret); - return ret; + goto err_spk_irqs; } ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5102, wm5102_dai, ARRAY_SIZE(wm5102_dai)); if (ret < 0) { dev_err(&pdev->dev, "Failed to register codec: %d\n", ret); - snd_soc_unregister_platform(&pdev->dev); + goto err_platform; } return ret; + +err_platform: + snd_soc_unregister_platform(&pdev->dev); +err_spk_irqs: + arizona_free_spk_irqs(arizona); +err_dsp_irq: + arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5102); + + return ret; } static int wm5102_remove(struct platform_device *pdev) { struct wm5102_priv *wm5102 = platform_get_drvdata(pdev); + struct arizona *arizona = wm5102->core.arizona; snd_soc_unregister_platform(&pdev->dev); snd_soc_unregister_codec(&pdev->dev); @@ -2107,6 +2133,10 @@ static int wm5102_remove(struct platform_device *pdev) wm_adsp2_remove(&wm5102->core.adsp[0]); + arizona_free_spk_irqs(arizona); + + arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5102); + return 0; } diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 156547026a40..585fc706c1b0 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -183,7 +183,9 @@ static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w, regmap_write_async(regmap, patch[i].reg, patch[i].def); break; - + case SND_SOC_DAPM_PRE_PMU: + case SND_SOC_DAPM_POST_PMD: + return arizona_clk_ev(w, kcontrol, event); default: break; } @@ -1073,9 +1075,11 @@ static const struct snd_kcontrol_new wm5110_output_anc_src[] = { static const struct snd_soc_dapm_widget wm5110_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT, - 0, wm5110_sysclk_ev, SND_SOC_DAPM_POST_PMU), + 0, wm5110_sysclk_ev, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1, - ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0), + ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK, ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK, @@ -1745,6 +1749,16 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = { { "IN4L", NULL, "SYSCLK" }, { "IN4R", NULL, "SYSCLK" }, + { "ASRC1L", NULL, "SYSCLK" }, + { "ASRC1R", NULL, "SYSCLK" }, + { "ASRC2L", NULL, "SYSCLK" }, + { "ASRC2R", NULL, "SYSCLK" }, + + { "ASRC1L", NULL, "ASYNCCLK" }, + { "ASRC1R", NULL, "ASYNCCLK" }, + { "ASRC2L", NULL, "ASYNCCLK" }, + { "ASRC2R", NULL, "ASYNCCLK" }, + { "MICBIAS1", NULL, "MICVDD" }, { "MICBIAS2", NULL, "MICVDD" }, { "MICBIAS3", NULL, "MICVDD" }, @@ -1832,10 +1846,8 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = { { "Slim3 Capture", NULL, "SYSCLK" }, { "Voice Control DSP", NULL, "DSP3" }, - { "Voice Control DSP", NULL, "SYSCLK" }, { "Audio Trace DSP", NULL, "DSP1" }, - { "Audio Trace DSP", NULL, "SYSCLK" }, { "IN1L PGA", NULL, "IN1L" }, { "IN1R PGA", NULL, "IN1R" }, @@ -2212,7 +2224,7 @@ static struct snd_soc_dai_driver wm5110_dai[] = { static int wm5110_open(struct snd_compr_stream *stream) { struct snd_soc_pcm_runtime *rtd = stream->private_data; - struct wm5110_priv *priv = snd_soc_codec_get_drvdata(rtd->codec); + struct wm5110_priv *priv = snd_soc_platform_get_drvdata(rtd->platform); struct arizona *arizona = priv->core.arizona; int n_adsp; @@ -2261,8 +2273,8 @@ static irqreturn_t wm5110_adsp2_irq(int irq, void *data) static int wm5110_codec_probe(struct snd_soc_codec *codec) { struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec); - struct arizona *arizona = priv->core.arizona; int i, ret; priv->core.arizona->dapm = dapm; @@ -2272,14 +2284,6 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec) arizona_init_mono(codec); arizona_init_notifiers(codec); - ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, - "ADSP2 Compressed IRQ", wm5110_adsp2_irq, - priv); - if (ret != 0) { - dev_err(codec->dev, "Failed to request DSP IRQ: %d\n", ret); - return ret; - } - for (i = 0; i < WM5110_NUM_ADSP; ++i) { ret = wm_adsp2_codec_probe(&priv->core.adsp[i], codec); if (ret) @@ -2292,7 +2296,7 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec) if (ret) goto err_adsp2_codec_probe; - snd_soc_dapm_disable_pin(dapm, "HAPTICS"); + snd_soc_component_disable_pin(component, "HAPTICS"); return 0; @@ -2300,15 +2304,12 @@ err_adsp2_codec_probe: for (--i; i >= 0; --i) wm_adsp2_codec_remove(&priv->core.adsp[i], codec); - arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv); - return ret; } static int wm5110_codec_remove(struct snd_soc_codec *codec) { struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec); - struct arizona *arizona = priv->core.arizona; int i; for (i = 0; i < WM5110_NUM_ADSP; ++i) @@ -2316,10 +2317,6 @@ static int wm5110_codec_remove(struct snd_soc_codec *codec) priv->core.arizona->dapm = NULL; - arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv); - - arizona_free_spk(codec); - return 0; } @@ -2347,7 +2344,7 @@ static struct regmap *wm5110_get_regmap(struct device *dev) return priv->core.arizona->regmap; } -static struct snd_soc_codec_driver soc_codec_dev_wm5110 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm5110 = { .probe = wm5110_codec_probe, .remove = wm5110_codec_remove, .get_regmap = wm5110_get_regmap, @@ -2357,12 +2354,14 @@ static struct snd_soc_codec_driver soc_codec_dev_wm5110 = { .set_sysclk = arizona_set_sysclk, .set_pll = wm5110_set_fll, - .controls = wm5110_snd_controls, - .num_controls = ARRAY_SIZE(wm5110_snd_controls), - .dapm_widgets = wm5110_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm5110_dapm_widgets), - .dapm_routes = wm5110_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm5110_dapm_routes), + .component_driver = { + .controls = wm5110_snd_controls, + .num_controls = ARRAY_SIZE(wm5110_snd_controls), + .dapm_widgets = wm5110_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm5110_dapm_widgets), + .dapm_routes = wm5110_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm5110_dapm_routes), + }, }; static struct snd_compr_ops wm5110_compr_ops = { @@ -2439,25 +2438,47 @@ static int wm5110_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_idle(&pdev->dev); + ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, + "ADSP2 Compressed IRQ", wm5110_adsp2_irq, + wm5110); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret); + return ret; + } + + ret = arizona_init_spk_irqs(arizona); + if (ret < 0) + goto err_dsp_irq; + ret = snd_soc_register_platform(&pdev->dev, &wm5110_compr_platform); if (ret < 0) { dev_err(&pdev->dev, "Failed to register platform: %d\n", ret); - return ret; + goto err_spk_irqs; } ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5110, wm5110_dai, ARRAY_SIZE(wm5110_dai)); if (ret < 0) { dev_err(&pdev->dev, "Failed to register codec: %d\n", ret); - snd_soc_unregister_platform(&pdev->dev); + goto err_platform; } return ret; + +err_platform: + snd_soc_unregister_platform(&pdev->dev); +err_spk_irqs: + arizona_free_spk_irqs(arizona); +err_dsp_irq: + arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5110); + + return ret; } static int wm5110_remove(struct platform_device *pdev) { struct wm5110_priv *wm5110 = platform_get_drvdata(pdev); + struct arizona *arizona = wm5110->core.arizona; int i; snd_soc_unregister_platform(&pdev->dev); @@ -2467,6 +2488,10 @@ static int wm5110_remove(struct platform_device *pdev) for (i = 0; i < WM5110_NUM_ADSP; i++) wm_adsp2_remove(&wm5110->core.adsp[i]); + arizona_free_spk_irqs(arizona); + + arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5110); + return 0; } diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index ffbf3df8ae97..2efc5b41ad0f 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -1587,19 +1587,21 @@ static struct regmap *wm8350_get_regmap(struct device *dev) return wm8350->regmap; } -static struct snd_soc_codec_driver soc_codec_dev_wm8350 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8350 = { .probe = wm8350_codec_probe, .remove = wm8350_codec_remove, .get_regmap = wm8350_get_regmap, .set_bias_level = wm8350_set_bias_level, .suspend_bias_off = true, - .controls = wm8350_snd_controls, - .num_controls = ARRAY_SIZE(wm8350_snd_controls), - .dapm_widgets = wm8350_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8350_dapm_widgets), - .dapm_routes = wm8350_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm8350_dapm_routes), + .component_driver = { + .controls = wm8350_snd_controls, + .num_controls = ARRAY_SIZE(wm8350_snd_controls), + .dapm_widgets = wm8350_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8350_dapm_widgets), + .dapm_routes = wm8350_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8350_dapm_routes), + }, }; static int wm8350_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c index b1d346aa4696..6c59fb933bd6 100644 --- a/sound/soc/codecs/wm8400.c +++ b/sound/soc/codecs/wm8400.c @@ -1332,19 +1332,21 @@ static struct regmap *wm8400_get_regmap(struct device *dev) return wm8400->regmap; } -static struct snd_soc_codec_driver soc_codec_dev_wm8400 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8400 = { .probe = wm8400_codec_probe, .remove = wm8400_codec_remove, .get_regmap = wm8400_get_regmap, .set_bias_level = wm8400_set_bias_level, .suspend_bias_off = true, - .controls = wm8400_snd_controls, - .num_controls = ARRAY_SIZE(wm8400_snd_controls), - .dapm_widgets = wm8400_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8400_dapm_widgets), - .dapm_routes = wm8400_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm8400_dapm_routes), + .component_driver = { + .controls = wm8400_snd_controls, + .num_controls = ARRAY_SIZE(wm8400_snd_controls), + .dapm_widgets = wm8400_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8400_dapm_widgets), + .dapm_routes = wm8400_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8400_dapm_routes), + }, }; static int wm8400_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c index 99e40e629cca..119ceac684ae 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c @@ -581,17 +581,19 @@ static int wm8510_probe(struct snd_soc_codec *codec) return 0; } -static struct snd_soc_codec_driver soc_codec_dev_wm8510 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8510 = { .probe = wm8510_probe, .set_bias_level = wm8510_set_bias_level, .suspend_bias_off = true, - .controls = wm8510_snd_controls, - .num_controls = ARRAY_SIZE(wm8510_snd_controls), - .dapm_widgets = wm8510_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8510_dapm_widgets), - .dapm_routes = wm8510_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm8510_dapm_routes), + .component_driver = { + .controls = wm8510_snd_controls, + .num_controls = ARRAY_SIZE(wm8510_snd_controls), + .dapm_widgets = wm8510_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8510_dapm_widgets), + .dapm_routes = wm8510_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8510_dapm_routes), + }, }; static const struct of_device_id wm8510_of_match[] = { diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index aa287a3965e7..6d0a2723bfde 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c @@ -413,17 +413,19 @@ static int wm8523_probe(struct snd_soc_codec *codec) return 0; } -static struct snd_soc_codec_driver soc_codec_dev_wm8523 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8523 = { .probe = wm8523_probe, .set_bias_level = wm8523_set_bias_level, .suspend_bias_off = true, - .controls = wm8523_controls, - .num_controls = ARRAY_SIZE(wm8523_controls), - .dapm_widgets = wm8523_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8523_dapm_widgets), - .dapm_routes = wm8523_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm8523_dapm_routes), + .component_driver = { + .controls = wm8523_controls, + .num_controls = ARRAY_SIZE(wm8523_controls), + .dapm_widgets = wm8523_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8523_dapm_widgets), + .dapm_routes = wm8523_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8523_dapm_routes), + }, }; static const struct of_device_id wm8523_of_match[] = { @@ -444,7 +446,6 @@ static const struct regmap_config wm8523_regmap = { .volatile_reg = wm8523_volatile_register, }; -#if IS_ENABLED(CONFIG_I2C) static int wm8523_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -541,29 +542,8 @@ static struct i2c_driver wm8523_i2c_driver = { .remove = wm8523_i2c_remove, .id_table = wm8523_i2c_id, }; -#endif - -static int __init wm8523_modinit(void) -{ - int ret; -#if IS_ENABLED(CONFIG_I2C) - ret = i2c_add_driver(&wm8523_i2c_driver); - if (ret != 0) { - printk(KERN_ERR "Failed to register WM8523 I2C driver: %d\n", - ret); - } -#endif - return 0; -} -module_init(wm8523_modinit); -static void __exit wm8523_exit(void) -{ -#if IS_ENABLED(CONFIG_I2C) - i2c_del_driver(&wm8523_i2c_driver); -#endif -} -module_exit(wm8523_exit); +module_i2c_driver(wm8523_i2c_driver); MODULE_DESCRIPTION("ASoC WM8523 driver"); MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index 66602bf02f6e..910801dddd64 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -1,5 +1,5 @@ /* - * wm8580.c -- WM8580 ALSA Soc Audio driver + * wm8580.c -- WM8580 and WM8581 ALSA Soc Audio driver * * Copyright 2008-12 Wolfson Microelectronics PLC. * @@ -12,6 +12,9 @@ * The WM8580 is a multichannel codec with S/PDIF support, featuring six * DAC channels and two ADC channels. * + * The WM8581 is a multichannel codec with S/PDIF support, featuring eight + * DAC channels and two ADC channels. + * * Currently only the primary audio interface is supported - S/PDIF and * the secondary audio interfaces are not. */ @@ -65,6 +68,8 @@ #define WM8580_DIGITAL_ATTENUATION_DACR2 0x17 #define WM8580_DIGITAL_ATTENUATION_DACL3 0x18 #define WM8580_DIGITAL_ATTENUATION_DACR3 0x19 +#define WM8581_DIGITAL_ATTENUATION_DACL4 0x1A +#define WM8581_DIGITAL_ATTENUATION_DACR4 0x1B #define WM8580_MASTER_DIGITAL_ATTENUATION 0x1C #define WM8580_ADC_CONTROL1 0x1D #define WM8580_SPDTXCHAN0 0x1E @@ -236,12 +241,17 @@ static const char *wm8580_supply_names[WM8580_NUM_SUPPLIES] = { "PVDD", }; +struct wm8580_driver_data { + int num_dacs; +}; + /* codec private data */ struct wm8580_priv { struct regmap *regmap; struct regulator_bulk_data supplies[WM8580_NUM_SUPPLIES]; struct pll_state a; struct pll_state b; + const struct wm8580_driver_data *drvdata; int sysclk[2]; }; @@ -306,6 +316,19 @@ SOC_DOUBLE("Capture Switch", WM8580_ADC_CONTROL1, 0, 1, 1, 1), SOC_SINGLE("Capture High-Pass Filter Switch", WM8580_ADC_CONTROL1, 4, 1, 0), }; +static const struct snd_kcontrol_new wm8581_snd_controls[] = { +SOC_DOUBLE_R_EXT_TLV("DAC4 Playback Volume", + WM8581_DIGITAL_ATTENUATION_DACL4, + WM8581_DIGITAL_ATTENUATION_DACR4, + 0, 0xff, 0, snd_soc_get_volsw, wm8580_out_vu, dac_tlv), + +SOC_SINGLE("DAC4 Deemphasis Switch", WM8580_DAC_CONTROL3, 3, 1, 0), + +SOC_DOUBLE("DAC4 Invert Switch", WM8580_DAC_CONTROL4, 8, 7, 1, 0), + +SOC_SINGLE("DAC4 Switch", WM8580_DAC_CONTROL5, 3, 1, 1), +}; + static const struct snd_soc_dapm_widget wm8580_dapm_widgets[] = { SND_SOC_DAPM_DAC("DAC1", "Playback", WM8580_PWRDN1, 2, 1), SND_SOC_DAPM_DAC("DAC2", "Playback", WM8580_PWRDN1, 3, 1), @@ -324,6 +347,13 @@ SND_SOC_DAPM_INPUT("AINL"), SND_SOC_DAPM_INPUT("AINR"), }; +static const struct snd_soc_dapm_widget wm8581_dapm_widgets[] = { +SND_SOC_DAPM_DAC("DAC4", "Playback", WM8580_PWRDN1, 5, 1), + +SND_SOC_DAPM_OUTPUT("VOUT4L"), +SND_SOC_DAPM_OUTPUT("VOUT4R"), +}; + static const struct snd_soc_dapm_route wm8580_dapm_routes[] = { { "VOUT1L", NULL, "DAC1" }, { "VOUT1R", NULL, "DAC1" }, @@ -338,6 +368,11 @@ static const struct snd_soc_dapm_route wm8580_dapm_routes[] = { { "ADC", NULL, "AINR" }, }; +static const struct snd_soc_dapm_route wm8581_dapm_routes[] = { + { "VOUT4L", NULL, "DAC4" }, + { "VOUT4R", NULL, "DAC4" }, +}; + /* PLL divisors */ struct _pll_div { u32 prescale:1; @@ -815,10 +850,21 @@ static int wm8580_set_bias_level(struct snd_soc_codec *codec, return 0; } +static int wm8580_playback_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec); + + return snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_CHANNELS, 1, wm8580->drvdata->num_dacs * 2); +} + #define WM8580_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) static const struct snd_soc_dai_ops wm8580_dai_ops_playback = { + .startup = wm8580_playback_startup, .set_sysclk = wm8580_set_sysclk, .hw_params = wm8580_paif_hw_params, .set_fmt = wm8580_set_paif_dai_fmt, @@ -842,7 +888,6 @@ static struct snd_soc_dai_driver wm8580_dai[] = { .playback = { .stream_name = "Playback", .channels_min = 1, - .channels_max = 6, .rates = SNDRV_PCM_RATE_8000_192000, .formats = WM8580_FORMATS, }, @@ -865,8 +910,22 @@ static struct snd_soc_dai_driver wm8580_dai[] = { static int wm8580_probe(struct snd_soc_codec *codec) { struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); int ret = 0; + switch (wm8580->drvdata->num_dacs) { + case 4: + snd_soc_add_codec_controls(codec, wm8581_snd_controls, + ARRAY_SIZE(wm8581_snd_controls)); + snd_soc_dapm_new_controls(dapm, wm8581_dapm_widgets, + ARRAY_SIZE(wm8581_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, wm8581_dapm_routes, + ARRAY_SIZE(wm8581_dapm_routes)); + break; + default: + break; + } + ret = regulator_bulk_enable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies); if (ret != 0) { @@ -899,24 +958,20 @@ static int wm8580_remove(struct snd_soc_codec *codec) return 0; } -static struct snd_soc_codec_driver soc_codec_dev_wm8580 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8580 = { .probe = wm8580_probe, .remove = wm8580_remove, .set_bias_level = wm8580_set_bias_level, - .controls = wm8580_snd_controls, - .num_controls = ARRAY_SIZE(wm8580_snd_controls), - .dapm_widgets = wm8580_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8580_dapm_widgets), - .dapm_routes = wm8580_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm8580_dapm_routes), -}; - -static const struct of_device_id wm8580_of_match[] = { - { .compatible = "wlf,wm8580" }, - { }, + .component_driver = { + .controls = wm8580_snd_controls, + .num_controls = ARRAY_SIZE(wm8580_snd_controls), + .dapm_widgets = wm8580_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8580_dapm_widgets), + .dapm_routes = wm8580_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8580_dapm_routes), + }, }; -MODULE_DEVICE_TABLE(of, wm8580_of_match); static const struct regmap_config wm8580_regmap = { .reg_bits = 7, @@ -930,10 +985,25 @@ static const struct regmap_config wm8580_regmap = { .volatile_reg = wm8580_volatile, }; -#if IS_ENABLED(CONFIG_I2C) +static const struct wm8580_driver_data wm8580_data = { + .num_dacs = 3, +}; + +static const struct wm8580_driver_data wm8581_data = { + .num_dacs = 4, +}; + +static const struct of_device_id wm8580_of_match[] = { + { .compatible = "wlf,wm8580", .data = &wm8580_data }, + { .compatible = "wlf,wm8581", .data = &wm8581_data }, + { }, +}; +MODULE_DEVICE_TABLE(of, wm8580_of_match); + static int wm8580_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { + const struct of_device_id *of_id; struct wm8580_priv *wm8580; int ret, i; @@ -958,6 +1028,15 @@ static int wm8580_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, wm8580); + of_id = of_match_device(wm8580_of_match, &i2c->dev); + if (of_id) + wm8580->drvdata = of_id->data; + + if (!wm8580->drvdata) { + dev_err(&i2c->dev, "failed to find driver data\n"); + return -EINVAL; + } + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm8580, wm8580_dai, ARRAY_SIZE(wm8580_dai)); @@ -971,7 +1050,8 @@ static int wm8580_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id wm8580_i2c_id[] = { - { "wm8580", 0 }, + { "wm8580", (kernel_ulong_t)&wm8580_data }, + { "wm8581", (kernel_ulong_t)&wm8581_data }, { } }; MODULE_DEVICE_TABLE(i2c, wm8580_i2c_id); @@ -985,31 +1065,10 @@ static struct i2c_driver wm8580_i2c_driver = { .remove = wm8580_i2c_remove, .id_table = wm8580_i2c_id, }; -#endif - -static int __init wm8580_modinit(void) -{ - int ret = 0; - -#if IS_ENABLED(CONFIG_I2C) - ret = i2c_add_driver(&wm8580_i2c_driver); - if (ret != 0) { - pr_err("Failed to register WM8580 I2C driver: %d\n", ret); - } -#endif - return ret; -} -module_init(wm8580_modinit); - -static void __exit wm8580_exit(void) -{ -#if IS_ENABLED(CONFIG_I2C) - i2c_del_driver(&wm8580_i2c_driver); -#endif -} -module_exit(wm8580_exit); +module_i2c_driver(wm8580_i2c_driver); MODULE_DESCRIPTION("ASoC WM8580 driver"); MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_AUTHOR("Matt Flax <flatmax@flatmax.org>"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index c759ec068e97..2b376c9c99af 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -367,17 +367,19 @@ static int wm8711_probe(struct snd_soc_codec *codec) } -static struct snd_soc_codec_driver soc_codec_dev_wm8711 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8711 = { .probe = wm8711_probe, .set_bias_level = wm8711_set_bias_level, .suspend_bias_off = true, - .controls = wm8711_snd_controls, - .num_controls = ARRAY_SIZE(wm8711_snd_controls), - .dapm_widgets = wm8711_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8711_dapm_widgets), - .dapm_routes = wm8711_intercon, - .num_dapm_routes = ARRAY_SIZE(wm8711_intercon), + .component_driver = { + .controls = wm8711_snd_controls, + .num_controls = ARRAY_SIZE(wm8711_snd_controls), + .dapm_widgets = wm8711_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8711_dapm_widgets), + .dapm_routes = wm8711_intercon, + .num_dapm_routes = ARRAY_SIZE(wm8711_intercon), + }, }; static const struct of_device_id wm8711_of_match[] = { diff --git a/sound/soc/codecs/wm8727.c b/sound/soc/codecs/wm8727.c index bb25a75f92a2..7fde077a014b 100644 --- a/sound/soc/codecs/wm8727.c +++ b/sound/soc/codecs/wm8727.c @@ -53,11 +53,13 @@ static struct snd_soc_dai_driver wm8727_dai = { }, }; -static struct snd_soc_codec_driver soc_codec_dev_wm8727 = { - .dapm_widgets = wm8727_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8727_dapm_widgets), - .dapm_routes = wm8727_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm8727_dapm_routes), +static const struct snd_soc_codec_driver soc_codec_dev_wm8727 = { + .component_driver = { + .dapm_widgets = wm8727_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8727_dapm_widgets), + .dapm_routes = wm8727_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8727_dapm_routes), + }, }; static int wm8727_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c index 1564e6926527..797cc6e7c70f 100644 --- a/sound/soc/codecs/wm8728.c +++ b/sound/soc/codecs/wm8728.c @@ -211,16 +211,18 @@ static struct snd_soc_dai_driver wm8728_dai = { .ops = &wm8728_dai_ops, }; -static struct snd_soc_codec_driver soc_codec_dev_wm8728 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8728 = { .set_bias_level = wm8728_set_bias_level, .suspend_bias_off = true, - .controls = wm8728_snd_controls, - .num_controls = ARRAY_SIZE(wm8728_snd_controls), - .dapm_widgets = wm8728_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8728_dapm_widgets), - .dapm_routes = wm8728_intercon, - .num_dapm_routes = ARRAY_SIZE(wm8728_intercon), + .component_driver = { + .controls = wm8728_snd_controls, + .num_controls = ARRAY_SIZE(wm8728_snd_controls), + .dapm_widgets = wm8728_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8728_dapm_widgets), + .dapm_routes = wm8728_intercon, + .num_dapm_routes = ARRAY_SIZE(wm8728_intercon), + }, }; static const struct of_device_id wm8728_of_match[] = { diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index d18261a44256..4f9a1eb28120 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -628,16 +628,18 @@ err_regulator_enable: return ret; } -static struct snd_soc_codec_driver soc_codec_dev_wm8731 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8731 = { .set_bias_level = wm8731_set_bias_level, .suspend_bias_off = true, - .dapm_widgets = wm8731_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets), - .dapm_routes = wm8731_intercon, - .num_dapm_routes = ARRAY_SIZE(wm8731_intercon), - .controls = wm8731_snd_controls, - .num_controls = ARRAY_SIZE(wm8731_snd_controls), + .component_driver = { + .controls = wm8731_snd_controls, + .num_controls = ARRAY_SIZE(wm8731_snd_controls), + .dapm_widgets = wm8731_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets), + .dapm_routes = wm8731_intercon, + .num_dapm_routes = ARRAY_SIZE(wm8731_intercon), + }, }; static const struct of_device_id wm8731_of_match[] = { diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c index e7807601e675..f0cb1c4afe3c 100644 --- a/sound/soc/codecs/wm8737.c +++ b/sound/soc/codecs/wm8737.c @@ -573,17 +573,19 @@ err_get: return ret; } -static struct snd_soc_codec_driver soc_codec_dev_wm8737 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8737 = { .probe = wm8737_probe, .set_bias_level = wm8737_set_bias_level, .suspend_bias_off = true, - .controls = wm8737_snd_controls, - .num_controls = ARRAY_SIZE(wm8737_snd_controls), - .dapm_widgets = wm8737_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8737_dapm_widgets), - .dapm_routes = intercon, - .num_dapm_routes = ARRAY_SIZE(intercon), + .component_driver = { + .controls = wm8737_snd_controls, + .num_controls = ARRAY_SIZE(wm8737_snd_controls), + .dapm_widgets = wm8737_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8737_dapm_widgets), + .dapm_routes = intercon, + .num_dapm_routes = ARRAY_SIZE(intercon), + }, }; static const struct of_device_id wm8737_of_match[] = { diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index 36ef91fe0511..565d477cd790 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -497,15 +497,17 @@ static int wm8741_remove(struct snd_soc_codec *codec) return 0; } -static struct snd_soc_codec_driver soc_codec_dev_wm8741 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8741 = { .probe = wm8741_probe, .remove = wm8741_remove, .resume = wm8741_resume, - .dapm_widgets = wm8741_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8741_dapm_widgets), - .dapm_routes = wm8741_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm8741_dapm_routes), + .component_driver = { + .dapm_widgets = wm8741_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8741_dapm_widgets), + .dapm_routes = wm8741_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8741_dapm_routes), + }, }; static const struct of_device_id wm8741_of_match[] = { diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index bd9dcd2161bc..0da2bbaf06d1 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -708,17 +708,19 @@ static int wm8750_probe(struct snd_soc_codec *codec) return ret; } -static struct snd_soc_codec_driver soc_codec_dev_wm8750 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8750 = { .probe = wm8750_probe, .set_bias_level = wm8750_set_bias_level, .suspend_bias_off = true, - .controls = wm8750_snd_controls, - .num_controls = ARRAY_SIZE(wm8750_snd_controls), - .dapm_widgets = wm8750_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets), - .dapm_routes = wm8750_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm8750_dapm_routes), + .component_driver = { + .controls = wm8750_snd_controls, + .num_controls = ARRAY_SIZE(wm8750_snd_controls), + .dapm_widgets = wm8750_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets), + .dapm_routes = wm8750_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8750_dapm_routes), + }, }; static const struct of_device_id wm8750_of_match[] = { diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index cdcc91282e8a..9bdf5447f6f6 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -1478,18 +1478,20 @@ static int wm8753_probe(struct snd_soc_codec *codec) return 0; } -static struct snd_soc_codec_driver soc_codec_dev_wm8753 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8753 = { .probe = wm8753_probe, .resume = wm8753_resume, .set_bias_level = wm8753_set_bias_level, .suspend_bias_off = true, - .controls = wm8753_snd_controls, - .num_controls = ARRAY_SIZE(wm8753_snd_controls), - .dapm_widgets = wm8753_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8753_dapm_widgets), - .dapm_routes = wm8753_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm8753_dapm_routes), + .component_driver = { + .controls = wm8753_snd_controls, + .num_controls = ARRAY_SIZE(wm8753_snd_controls), + .dapm_widgets = wm8753_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8753_dapm_widgets), + .dapm_routes = wm8753_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8753_dapm_routes), + }, }; static const struct of_device_id wm8753_of_match[] = { diff --git a/sound/soc/codecs/wm8753.h b/sound/soc/codecs/wm8753.h index 94edac144bcb..8b39e3677ac8 100644 --- a/sound/soc/codecs/wm8753.h +++ b/sound/soc/codecs/wm8753.h @@ -112,7 +112,4 @@ #define WM8753_VXCLK_DIV_8 (3 << 6) #define WM8753_VXCLK_DIV_16 (4 << 6) -#define WM8753_DAI_HIFI 0 -#define WM8753_DAI_VOICE 1 - #endif diff --git a/sound/soc/codecs/wm8770.c b/sound/soc/codecs/wm8770.c index df6178464b00..d6edcbbdec12 100644 --- a/sound/soc/codecs/wm8770.c +++ b/sound/soc/codecs/wm8770.c @@ -608,17 +608,19 @@ err_reg_enable: return ret; } -static struct snd_soc_codec_driver soc_codec_dev_wm8770 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8770 = { .probe = wm8770_probe, .set_bias_level = wm8770_set_bias_level, .idle_bias_off = true, - .controls = wm8770_snd_controls, - .num_controls = ARRAY_SIZE(wm8770_snd_controls), - .dapm_widgets = wm8770_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8770_dapm_widgets), - .dapm_routes = wm8770_intercon, - .num_dapm_routes = ARRAY_SIZE(wm8770_intercon), + .component_driver = { + .controls = wm8770_snd_controls, + .num_controls = ARRAY_SIZE(wm8770_snd_controls), + .dapm_widgets = wm8770_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8770_dapm_widgets), + .dapm_routes = wm8770_intercon, + .num_dapm_routes = ARRAY_SIZE(wm8770_intercon), + }, }; static const struct of_device_id wm8770_of_match[] = { diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c index 5af44f9a8cf2..ae30480b3976 100644 --- a/sound/soc/codecs/wm8776.c +++ b/sound/soc/codecs/wm8776.c @@ -425,17 +425,19 @@ static int wm8776_probe(struct snd_soc_codec *codec) return ret; } -static struct snd_soc_codec_driver soc_codec_dev_wm8776 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8776 = { .probe = wm8776_probe, .set_bias_level = wm8776_set_bias_level, .suspend_bias_off = true, - .controls = wm8776_snd_controls, - .num_controls = ARRAY_SIZE(wm8776_snd_controls), - .dapm_widgets = wm8776_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8776_dapm_widgets), - .dapm_routes = routes, - .num_dapm_routes = ARRAY_SIZE(routes), + .component_driver = { + .controls = wm8776_snd_controls, + .num_controls = ARRAY_SIZE(wm8776_snd_controls), + .dapm_widgets = wm8776_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8776_dapm_widgets), + .dapm_routes = routes, + .num_dapm_routes = ARRAY_SIZE(routes), + }, }; static const struct of_device_id wm8776_of_match[] = { diff --git a/sound/soc/codecs/wm8782.c b/sound/soc/codecs/wm8782.c index fb55fd845d27..bcda21018505 100644 --- a/sound/soc/codecs/wm8782.c +++ b/sound/soc/codecs/wm8782.c @@ -50,11 +50,13 @@ static struct snd_soc_dai_driver wm8782_dai = { }, }; -static struct snd_soc_codec_driver soc_codec_dev_wm8782 = { - .dapm_widgets = wm8782_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8782_dapm_widgets), - .dapm_routes = wm8782_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm8782_dapm_routes), +static const struct snd_soc_codec_driver soc_codec_dev_wm8782 = { + .component_driver = { + .dapm_widgets = wm8782_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8782_dapm_widgets), + .dapm_routes = wm8782_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8782_dapm_routes), + }, }; static int wm8782_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c index 8d914702cae4..af95d648265b 100644 --- a/sound/soc/codecs/wm8804.c +++ b/sound/soc/codecs/wm8804.c @@ -545,10 +545,12 @@ static struct snd_soc_dai_driver wm8804_dai = { static const struct snd_soc_codec_driver soc_codec_dev_wm8804 = { .idle_bias_off = true, - .dapm_widgets = wm8804_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8804_dapm_widgets), - .dapm_routes = wm8804_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm8804_dapm_routes), + .component_driver = { + .dapm_widgets = wm8804_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8804_dapm_widgets), + .dapm_routes = wm8804_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8804_dapm_routes), + }, }; const struct regmap_config wm8804_regmap_config = { diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index 5d8dca88d612..c77b49a29311 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -1208,18 +1208,20 @@ static int wm8900_probe(struct snd_soc_codec *codec) return 0; } -static struct snd_soc_codec_driver soc_codec_dev_wm8900 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8900 = { .probe = wm8900_probe, .suspend = wm8900_suspend, .resume = wm8900_resume, .set_bias_level = wm8900_set_bias_level, - .controls = wm8900_snd_controls, - .num_controls = ARRAY_SIZE(wm8900_snd_controls), - .dapm_widgets = wm8900_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8900_dapm_widgets), - .dapm_routes = wm8900_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm8900_dapm_routes), + .component_driver = { + .controls = wm8900_snd_controls, + .num_controls = ARRAY_SIZE(wm8900_snd_controls), + .dapm_widgets = wm8900_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8900_dapm_widgets), + .dapm_routes = wm8900_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8900_dapm_routes), + }, }; static const struct regmap_config wm8900_regmap = { diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index a26ca490cf31..6e887c2c42b1 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -1830,7 +1830,7 @@ static void wm8903_gpio_set(struct gpio_chip *chip, unsigned offset, int value) !!value << WM8903_GP1_LVL_SHIFT); } -static struct gpio_chip wm8903_template_chip = { +static const struct gpio_chip wm8903_template_chip = { .label = "wm8903", .owner = THIS_MODULE, .request = wm8903_gpio_request, @@ -1874,18 +1874,20 @@ static void wm8903_free_gpio(struct wm8903_priv *wm8903) } #endif -static struct snd_soc_codec_driver soc_codec_dev_wm8903 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8903 = { .resume = wm8903_resume, .set_bias_level = wm8903_set_bias_level, .seq_notifier = wm8903_seq_notifier, .suspend_bias_off = true, - .controls = wm8903_snd_controls, - .num_controls = ARRAY_SIZE(wm8903_snd_controls), - .dapm_widgets = wm8903_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8903_dapm_widgets), - .dapm_routes = wm8903_intercon, - .num_dapm_routes = ARRAY_SIZE(wm8903_intercon), + .component_driver = { + .controls = wm8903_snd_controls, + .num_controls = ARRAY_SIZE(wm8903_snd_controls), + .dapm_widgets = wm8903_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8903_dapm_widgets), + .dapm_routes = wm8903_intercon, + .num_dapm_routes = ARRAY_SIZE(wm8903_intercon), + }, }; static const struct regmap_config wm8903_regmap = { diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index edd7a7709194..4fd350e8420d 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -2086,7 +2086,7 @@ static int wm8904_remove(struct snd_soc_codec *codec) return 0; } -static struct snd_soc_codec_driver soc_codec_dev_wm8904 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8904 = { .probe = wm8904_probe, .remove = wm8904_remove, .set_bias_level = wm8904_set_bias_level, diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index 1c600819f768..b5935625feeb 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c @@ -723,17 +723,19 @@ static int wm8940_probe(struct snd_soc_codec *codec) return ret; } -static struct snd_soc_codec_driver soc_codec_dev_wm8940 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8940 = { .probe = wm8940_probe, .set_bias_level = wm8940_set_bias_level, .suspend_bias_off = true, - .controls = wm8940_snd_controls, - .num_controls = ARRAY_SIZE(wm8940_snd_controls), - .dapm_widgets = wm8940_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8940_dapm_widgets), - .dapm_routes = wm8940_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm8940_dapm_routes), + .component_driver = { + .controls = wm8940_snd_controls, + .num_controls = ARRAY_SIZE(wm8940_snd_controls), + .dapm_widgets = wm8940_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8940_dapm_widgets), + .dapm_routes = wm8940_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8940_dapm_routes), + }, }; static const struct regmap_config wm8940_regmap = { diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c index 9db00d53abe7..1edc7b1df31d 100644 --- a/sound/soc/codecs/wm8955.c +++ b/sound/soc/codecs/wm8955.c @@ -940,17 +940,19 @@ err_enable: return ret; } -static struct snd_soc_codec_driver soc_codec_dev_wm8955 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8955 = { .probe = wm8955_probe, .set_bias_level = wm8955_set_bias_level, .suspend_bias_off = true, - .controls = wm8955_snd_controls, - .num_controls = ARRAY_SIZE(wm8955_snd_controls), - .dapm_widgets = wm8955_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8955_dapm_widgets), - .dapm_routes = wm8955_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm8955_dapm_routes), + .component_driver = { + .controls = wm8955_snd_controls, + .num_controls = ARRAY_SIZE(wm8955_snd_controls), + .dapm_widgets = wm8955_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8955_dapm_widgets), + .dapm_routes = wm8955_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8955_dapm_routes), + }, }; static const struct regmap_config wm8955_regmap = { diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index d7f444f87460..3bf081a7e450 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -226,11 +226,10 @@ static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0); static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); static const DECLARE_TLV_DB_SCALE(lineinboost_tlv, -1500, 300, 1); -static const unsigned int micboost_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(micboost_tlv, 0, 1, TLV_DB_SCALE_ITEM(0, 1300, 0), 2, 3, TLV_DB_SCALE_ITEM(2000, 900, 0), -}; +); static const struct snd_kcontrol_new wm8960_snd_controls[] = { SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL, @@ -1264,7 +1263,7 @@ static int wm8960_probe(struct snd_soc_codec *codec) return 0; } -static struct snd_soc_codec_driver soc_codec_dev_wm8960 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8960 = { .probe = wm8960_probe, .set_bias_level = wm8960_set_bias_level, .suspend_bias_off = true, diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c index e30446a04740..e23ceac76015 100644 --- a/sound/soc/codecs/wm8961.c +++ b/sound/soc/codecs/wm8961.c @@ -882,18 +882,20 @@ static int wm8961_resume(struct snd_soc_codec *codec) #define wm8961_resume NULL #endif -static struct snd_soc_codec_driver soc_codec_dev_wm8961 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8961 = { .probe = wm8961_probe, .resume = wm8961_resume, .set_bias_level = wm8961_set_bias_level, .suspend_bias_off = true, - .controls = wm8961_snd_controls, - .num_controls = ARRAY_SIZE(wm8961_snd_controls), - .dapm_widgets = wm8961_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8961_dapm_widgets), - .dapm_routes = audio_paths, - .num_dapm_routes = ARRAY_SIZE(audio_paths), + .component_driver = { + .controls = wm8961_snd_controls, + .num_controls = ARRAY_SIZE(wm8961_snd_controls), + .dapm_widgets = wm8961_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8961_dapm_widgets), + .dapm_routes = audio_paths, + .num_dapm_routes = ARRAY_SIZE(audio_paths), + }, }; static const struct regmap_config wm8961_regmap = { diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index f3109da24769..fd2731d171dd 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3357,7 +3357,7 @@ static int wm8962_gpio_direction_out(struct gpio_chip *chip, return 0; } -static struct gpio_chip wm8962_template_chip = { +static const struct gpio_chip wm8962_template_chip = { .label = "wm8962", .owner = THIS_MODULE, .request = wm8962_gpio_request, @@ -3479,7 +3479,7 @@ static int wm8962_remove(struct snd_soc_codec *codec) return 0; } -static struct snd_soc_codec_driver soc_codec_dev_wm8962 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8962 = { .probe = wm8962_probe, .remove = wm8962_remove, .set_bias_level = wm8962_set_bias_level, @@ -3713,7 +3713,7 @@ static int wm8962_i2c_probe(struct i2c_client *i2c, ARRAY_SIZE(wm8962_dc_measure)); if (ret != 0) dev_err(&i2c->dev, - "Failed to configure for DC mesurement: %d\n", + "Failed to configure for DC measurement: %d\n", ret); } diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c index 2cdde32c43c6..887d31cf3945 100644 --- a/sound/soc/codecs/wm8971.c +++ b/sound/soc/codecs/wm8971.c @@ -649,17 +649,19 @@ static int wm8971_probe(struct snd_soc_codec *codec) return 0; } -static struct snd_soc_codec_driver soc_codec_dev_wm8971 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8971 = { .probe = wm8971_probe, .set_bias_level = wm8971_set_bias_level, .suspend_bias_off = true, - .controls = wm8971_snd_controls, - .num_controls = ARRAY_SIZE(wm8971_snd_controls), - .dapm_widgets = wm8971_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8971_dapm_widgets), - .dapm_routes = wm8971_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm8971_dapm_routes), + .component_driver = { + .controls = wm8971_snd_controls, + .num_controls = ARRAY_SIZE(wm8971_snd_controls), + .dapm_widgets = wm8971_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8971_dapm_widgets), + .dapm_routes = wm8971_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8971_dapm_routes), + }, }; static const struct regmap_config wm8971_regmap = { diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c index dc8c3b1ebb6f..d414ddd6e197 100644 --- a/sound/soc/codecs/wm8974.c +++ b/sound/soc/codecs/wm8974.c @@ -676,17 +676,19 @@ static int wm8974_probe(struct snd_soc_codec *codec) return 0; } -static struct snd_soc_codec_driver soc_codec_dev_wm8974 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8974 = { .probe = wm8974_probe, .set_bias_level = wm8974_set_bias_level, .suspend_bias_off = true, - .controls = wm8974_snd_controls, - .num_controls = ARRAY_SIZE(wm8974_snd_controls), - .dapm_widgets = wm8974_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8974_dapm_widgets), - .dapm_routes = wm8974_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm8974_dapm_routes), + .component_driver = { + .controls = wm8974_snd_controls, + .num_controls = ARRAY_SIZE(wm8974_snd_controls), + .dapm_widgets = wm8974_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8974_dapm_widgets), + .dapm_routes = wm8974_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8974_dapm_routes), + }, }; static int wm8974_i2c_probe(struct i2c_client *i2c, diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c index d36d6001fbb7..90b2d418ef60 100644 --- a/sound/soc/codecs/wm8978.c +++ b/sound/soc/codecs/wm8978.c @@ -993,18 +993,20 @@ static int wm8978_probe(struct snd_soc_codec *codec) return 0; } -static struct snd_soc_codec_driver soc_codec_dev_wm8978 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8978 = { .probe = wm8978_probe, .suspend = wm8978_suspend, .resume = wm8978_resume, .set_bias_level = wm8978_set_bias_level, - .controls = wm8978_snd_controls, - .num_controls = ARRAY_SIZE(wm8978_snd_controls), - .dapm_widgets = wm8978_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8978_dapm_widgets), - .dapm_routes = wm8978_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm8978_dapm_routes), + .component_driver = { + .controls = wm8978_snd_controls, + .num_controls = ARRAY_SIZE(wm8978_snd_controls), + .dapm_widgets = wm8978_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8978_dapm_widgets), + .dapm_routes = wm8978_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8978_dapm_routes), + }, }; static const struct regmap_config wm8978_regmap_config = { diff --git a/sound/soc/codecs/wm8978.h b/sound/soc/codecs/wm8978.h index 6ae43495b7cf..0dcf6868dff6 100644 --- a/sound/soc/codecs/wm8978.h +++ b/sound/soc/codecs/wm8978.h @@ -78,8 +78,8 @@ enum wm8978_clk_id { }; enum wm8978_sysclk_src { + WM8978_MCLK = 0, WM8978_PLL, - WM8978_MCLK }; #endif /* __WM8978_H__ */ diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c index 0c002a5712cb..bfdbe72ee687 100644 --- a/sound/soc/codecs/wm8983.c +++ b/sound/soc/codecs/wm8983.c @@ -976,16 +976,18 @@ static struct snd_soc_dai_driver wm8983_dai = { .symmetric_rates = 1 }; -static struct snd_soc_codec_driver soc_codec_dev_wm8983 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8983 = { .probe = wm8983_probe, .set_bias_level = wm8983_set_bias_level, .suspend_bias_off = true, - .controls = wm8983_snd_controls, - .num_controls = ARRAY_SIZE(wm8983_snd_controls), - .dapm_widgets = wm8983_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8983_dapm_widgets), - .dapm_routes = wm8983_audio_map, - .num_dapm_routes = ARRAY_SIZE(wm8983_audio_map), + .component_driver = { + .controls = wm8983_snd_controls, + .num_controls = ARRAY_SIZE(wm8983_snd_controls), + .dapm_widgets = wm8983_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8983_dapm_widgets), + .dapm_routes = wm8983_audio_map, + .num_dapm_routes = ARRAY_SIZE(wm8983_audio_map), + }, }; static const struct regmap_config wm8983_regmap = { diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c index 7347abff4b2c..05344f974ff3 100644 --- a/sound/soc/codecs/wm8985.c +++ b/sound/soc/codecs/wm8985.c @@ -1105,17 +1105,19 @@ static struct snd_soc_dai_driver wm8985_dai = { .symmetric_rates = 1 }; -static struct snd_soc_codec_driver soc_codec_dev_wm8985 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8985 = { .probe = wm8985_probe, .set_bias_level = wm8985_set_bias_level, .suspend_bias_off = true, - .controls = wm8985_common_snd_controls, - .num_controls = ARRAY_SIZE(wm8985_common_snd_controls), - .dapm_widgets = wm8985_common_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8985_common_dapm_widgets), - .dapm_routes = wm8985_common_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm8985_common_dapm_routes), + .component_driver = { + .controls = wm8985_common_snd_controls, + .num_controls = ARRAY_SIZE(wm8985_common_snd_controls), + .dapm_widgets = wm8985_common_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8985_common_dapm_widgets), + .dapm_routes = wm8985_common_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8985_common_dapm_routes), + }, }; static const struct regmap_config wm8985_regmap = { diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c index 895721a256f0..b0d0219532f2 100644 --- a/sound/soc/codecs/wm8988.c +++ b/sound/soc/codecs/wm8988.c @@ -817,12 +817,14 @@ static const struct snd_soc_codec_driver soc_codec_dev_wm8988 = { .set_bias_level = wm8988_set_bias_level, .suspend_bias_off = true, - .controls = wm8988_snd_controls, - .num_controls = ARRAY_SIZE(wm8988_snd_controls), - .dapm_widgets = wm8988_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8988_dapm_widgets), - .dapm_routes = wm8988_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm8988_dapm_routes), + .component_driver = { + .controls = wm8988_snd_controls, + .num_controls = ARRAY_SIZE(wm8988_snd_controls), + .dapm_widgets = wm8988_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8988_dapm_widgets), + .dapm_routes = wm8988_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8988_dapm_routes), + }, }; static const struct regmap_config wm8988_regmap = { diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c index 23ecd30d8bca..a8945001e696 100644 --- a/sound/soc/codecs/wm8990.c +++ b/sound/soc/codecs/wm8990.c @@ -1294,17 +1294,19 @@ static int wm8990_probe(struct snd_soc_codec *codec) return 0; } -static struct snd_soc_codec_driver soc_codec_dev_wm8990 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8990 = { .probe = wm8990_probe, .set_bias_level = wm8990_set_bias_level, .suspend_bias_off = true, - .controls = wm8990_snd_controls, - .num_controls = ARRAY_SIZE(wm8990_snd_controls), - .dapm_widgets = wm8990_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8990_dapm_widgets), - .dapm_routes = wm8990_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm8990_dapm_routes), + .component_driver = { + .controls = wm8990_snd_controls, + .num_controls = ARRAY_SIZE(wm8990_snd_controls), + .dapm_widgets = wm8990_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8990_dapm_widgets), + .dapm_routes = wm8990_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8990_dapm_routes), + }, }; static const struct regmap_config wm8990_regmap = { diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c index c9ee0ac6a654..802c0694e5c3 100644 --- a/sound/soc/codecs/wm8991.c +++ b/sound/soc/codecs/wm8991.c @@ -111,14 +111,24 @@ static bool wm8991_volatile(struct device *dev, unsigned int reg) } } -static const DECLARE_TLV_DB_LINEAR(rec_mix_tlv, -1500, 600); -static const DECLARE_TLV_DB_LINEAR(in_pga_tlv, -1650, 3000); -static const DECLARE_TLV_DB_LINEAR(out_mix_tlv, 0, -2100); -static const DECLARE_TLV_DB_LINEAR(out_pga_tlv, -7300, 600); -static const DECLARE_TLV_DB_LINEAR(out_omix_tlv, -600, 0); -static const DECLARE_TLV_DB_LINEAR(out_dac_tlv, -7163, 0); -static const DECLARE_TLV_DB_LINEAR(in_adc_tlv, -7163, 1763); -static const DECLARE_TLV_DB_LINEAR(out_sidetone_tlv, -3600, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(in_pga_tlv, -1650, 150, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(out_mix_tlv, -2100, 300, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(out_pga_tlv, + 0x00, 0x2f, SNDRV_CTL_TLVD_DB_SCALE_ITEM(SNDRV_CTL_TLVD_DB_GAIN_MUTE, 0, 1), + 0x30, 0x7f, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-7300, 100, 0), +); +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(out_dac_tlv, + 0x00, 0xbf, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-71625, 375, 1), + 0xc0, 0xff, SNDRV_CTL_TLVD_DB_SCALE_ITEM(0, 0, 0), +); +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(in_adc_tlv, + 0x00, 0xef, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-71625, 375, 1), + 0xf0, 0xff, SNDRV_CTL_TLVD_DB_SCALE_ITEM(17625, 0, 0), +); +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(out_sidetone_tlv, + 0x00, 0x0c, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-3600, 300, 0), + 0x0d, 0x0f, SNDRV_CTL_TLVD_DB_SCALE_ITEM(0, 0, 0), +); static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -398,7 +408,7 @@ static int outmixer_event(struct snd_soc_dapm_widget *w, } /* INMIX dB values */ -static const DECLARE_TLV_DB_LINEAR(in_mix_tlv, -1200, 600); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(in_mix_tlv, -1200, 300, 1); /* Left In PGA Connections */ static const struct snd_kcontrol_new wm8991_dapm_lin12_pga_controls[] = { @@ -1232,16 +1242,18 @@ static struct snd_soc_dai_driver wm8991_dai = { .ops = &wm8991_ops }; -static struct snd_soc_codec_driver soc_codec_dev_wm8991 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8991 = { .set_bias_level = wm8991_set_bias_level, .suspend_bias_off = true, - .controls = wm8991_snd_controls, - .num_controls = ARRAY_SIZE(wm8991_snd_controls), - .dapm_widgets = wm8991_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8991_dapm_widgets), - .dapm_routes = wm8991_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm8991_dapm_routes), + .component_driver = { + .controls = wm8991_snd_controls, + .num_controls = ARRAY_SIZE(wm8991_snd_controls), + .dapm_widgets = wm8991_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8991_dapm_widgets), + .dapm_routes = wm8991_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8991_dapm_routes), + }, }; static const struct regmap_config wm8991_regmap = { diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index 8668c4c391b0..195f7bf6eb22 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -1613,7 +1613,7 @@ static const struct regmap_config wm8993_regmap = { .num_reg_defaults = ARRAY_SIZE(wm8993_reg_defaults), }; -static struct snd_soc_codec_driver soc_codec_dev_wm8993 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8993 = { .probe = wm8993_probe, .suspend = wm8993_suspend, .resume = wm8993_resume, diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index a18aecb49935..3896523b71e9 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -4439,7 +4439,7 @@ static struct regmap *wm8994_get_regmap(struct device *dev) return control->regmap; } -static struct snd_soc_codec_driver soc_codec_dev_wm8994 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8994 = { .probe = wm8994_codec_probe, .remove = wm8994_codec_remove, .suspend = wm8994_codec_suspend, diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index 24500bafb0a8..19b08a5cae62 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -2192,12 +2192,14 @@ static const struct snd_soc_codec_driver soc_codec_dev_wm8995 = { .set_bias_level = wm8995_set_bias_level, .idle_bias_off = true, - .controls = wm8995_snd_controls, - .num_controls = ARRAY_SIZE(wm8995_snd_controls), - .dapm_widgets = wm8995_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8995_dapm_widgets), - .dapm_routes = wm8995_intercon, - .num_dapm_routes = ARRAY_SIZE(wm8995_intercon), + .component_driver = { + .controls = wm8995_snd_controls, + .num_controls = ARRAY_SIZE(wm8995_snd_controls), + .dapm_widgets = wm8995_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8995_dapm_widgets), + .dapm_routes = wm8995_intercon, + .num_dapm_routes = ARRAY_SIZE(wm8995_intercon), + }, }; static const struct regmap_config wm8995_regmap = { diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index a73044251218..8affa4969120 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -2184,7 +2184,7 @@ static int wm8996_gpio_direction_in(struct gpio_chip *chip, unsigned offset) (1 << WM8996_GP1_DIR_SHIFT)); } -static struct gpio_chip wm8996_template_chip = { +static const struct gpio_chip wm8996_template_chip = { .label = "wm8996", .owner = THIS_MODULE, .direction_output = wm8996_gpio_direction_out, @@ -2684,18 +2684,20 @@ static int wm8996_remove(struct snd_soc_codec *codec) return 0; } -static struct snd_soc_codec_driver soc_codec_dev_wm8996 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8996 = { .probe = wm8996_probe, .remove = wm8996_remove, .set_bias_level = wm8996_set_bias_level, .idle_bias_off = true, .seq_notifier = wm8996_seq_notifier, - .controls = wm8996_snd_controls, - .num_controls = ARRAY_SIZE(wm8996_snd_controls), - .dapm_widgets = wm8996_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8996_dapm_widgets), - .dapm_routes = wm8996_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm8996_dapm_routes), + .component_driver = { + .controls = wm8996_snd_controls, + .num_controls = ARRAY_SIZE(wm8996_snd_controls), + .dapm_widgets = wm8996_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8996_dapm_widgets), + .dapm_routes = wm8996_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8996_dapm_routes), + }, .set_pll = wm8996_set_fll, }; diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c index 6b0785b5a5c5..ee0c8639c743 100644 --- a/sound/soc/codecs/wm8997.c +++ b/sound/soc/codecs/wm8997.c @@ -108,6 +108,9 @@ static int wm8997_sysclk_ev(struct snd_soc_dapm_widget *w, break; case SND_SOC_DAPM_PRE_PMD: break; + case SND_SOC_DAPM_PRE_PMU: + case SND_SOC_DAPM_POST_PMD: + return arizona_clk_ev(w, kcontrol, event); default: return 0; } @@ -408,9 +411,11 @@ static const struct snd_kcontrol_new wm8997_aec_loopback_mux = static const struct snd_soc_dapm_widget wm8997_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT, 0, wm8997_sysclk_ev, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1, - ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0), + ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK, ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK, @@ -1055,11 +1060,13 @@ static struct snd_soc_dai_driver wm8997_dai[] = { static int wm8997_codec_probe(struct snd_soc_codec *codec) { struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec); arizona_init_spk(codec); + arizona_init_notifiers(codec); - snd_soc_dapm_disable_pin(dapm, "HAPTICS"); + snd_soc_component_disable_pin(component, "HAPTICS"); priv->core.arizona->dapm = dapm; @@ -1072,8 +1079,6 @@ static int wm8997_codec_remove(struct snd_soc_codec *codec) priv->core.arizona->dapm = NULL; - arizona_free_spk(codec); - return 0; } @@ -1095,7 +1100,7 @@ static struct regmap *wm8997_get_regmap(struct device *dev) return priv->core.arizona->regmap; } -static struct snd_soc_codec_driver soc_codec_dev_wm8997 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8997 = { .probe = wm8997_codec_probe, .remove = wm8997_codec_remove, .get_regmap = wm8997_get_regmap, @@ -1105,19 +1110,21 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8997 = { .set_sysclk = arizona_set_sysclk, .set_pll = wm8997_set_fll, - .controls = wm8997_snd_controls, - .num_controls = ARRAY_SIZE(wm8997_snd_controls), - .dapm_widgets = wm8997_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8997_dapm_widgets), - .dapm_routes = wm8997_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm8997_dapm_routes), + .component_driver = { + .controls = wm8997_snd_controls, + .num_controls = ARRAY_SIZE(wm8997_snd_controls), + .dapm_widgets = wm8997_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8997_dapm_widgets), + .dapm_routes = wm8997_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8997_dapm_routes), + }, }; static int wm8997_probe(struct platform_device *pdev) { struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); struct wm8997_priv *wm8997; - int i; + int i, ret; wm8997 = devm_kzalloc(&pdev->dev, sizeof(struct wm8997_priv), GFP_KERNEL); @@ -1157,15 +1164,33 @@ static int wm8997_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_idle(&pdev->dev); - return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8997, - wm8997_dai, ARRAY_SIZE(wm8997_dai)); + ret = arizona_init_spk_irqs(arizona); + if (ret < 0) + return ret; + + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8997, + wm8997_dai, ARRAY_SIZE(wm8997_dai)); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register codec: %d\n", ret); + goto err_spk_irqs; + } + +err_spk_irqs: + arizona_free_spk_irqs(arizona); + + return ret; } static int wm8997_remove(struct platform_device *pdev) { + struct wm8997_priv *wm8997 = platform_get_drvdata(pdev); + struct arizona *arizona = wm8997->core.arizona; + snd_soc_unregister_codec(&pdev->dev); pm_runtime_disable(&pdev->dev); + arizona_free_spk_irqs(arizona); + return 0; } diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c index 3a5c896a2d13..3694f5958d86 100644 --- a/sound/soc/codecs/wm8998.c +++ b/sound/soc/codecs/wm8998.c @@ -541,9 +541,11 @@ static const struct snd_kcontrol_new wm8998_aec_loopback_mux[] = { static const struct snd_soc_dapm_widget wm8998_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, - ARIZONA_SYSCLK_ENA_SHIFT, 0, NULL, 0), + ARIZONA_SYSCLK_ENA_SHIFT, 0, arizona_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1, - ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0), + ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK, ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK, @@ -966,6 +968,16 @@ static const struct snd_soc_dapm_route wm8998_dapm_routes[] = { { "IN2A", NULL, "SYSCLK" }, { "IN2B", NULL, "SYSCLK" }, + { "ASRC1L", NULL, "SYSCLK" }, + { "ASRC1R", NULL, "SYSCLK" }, + { "ASRC2L", NULL, "SYSCLK" }, + { "ASRC2R", NULL, "SYSCLK" }, + + { "ASRC1L", NULL, "ASYNCCLK" }, + { "ASRC1R", NULL, "ASYNCCLK" }, + { "ASRC2L", NULL, "ASYNCCLK" }, + { "ASRC2R", NULL, "ASYNCCLK" }, + { "SPD1", NULL, "SYSCLK" }, { "SPD1", NULL, "SPD1TX1" }, { "SPD1", NULL, "SPD1TX2" }, @@ -1308,13 +1320,15 @@ static int wm8998_codec_probe(struct snd_soc_codec *codec) { struct wm8998_priv *priv = snd_soc_codec_get_drvdata(codec); struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); priv->core.arizona->dapm = dapm; arizona_init_spk(codec); arizona_init_gpio(codec); + arizona_init_notifiers(codec); - snd_soc_dapm_disable_pin(dapm, "HAPTICS"); + snd_soc_component_disable_pin(component, "HAPTICS"); return 0; } @@ -1325,8 +1339,6 @@ static int wm8998_codec_remove(struct snd_soc_codec *codec) priv->core.arizona->dapm = NULL; - arizona_free_spk(codec); - return 0; } @@ -1351,7 +1363,7 @@ static struct regmap *wm8998_get_regmap(struct device *dev) return priv->core.arizona->regmap; } -static struct snd_soc_codec_driver soc_codec_dev_wm8998 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm8998 = { .probe = wm8998_codec_probe, .remove = wm8998_codec_remove, .get_regmap = wm8998_get_regmap, @@ -1361,19 +1373,21 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8998 = { .set_sysclk = arizona_set_sysclk, .set_pll = wm8998_set_fll, - .controls = wm8998_snd_controls, - .num_controls = ARRAY_SIZE(wm8998_snd_controls), - .dapm_widgets = wm8998_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8998_dapm_widgets), - .dapm_routes = wm8998_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wm8998_dapm_routes), + .component_driver = { + .controls = wm8998_snd_controls, + .num_controls = ARRAY_SIZE(wm8998_snd_controls), + .dapm_widgets = wm8998_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8998_dapm_widgets), + .dapm_routes = wm8998_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8998_dapm_routes), + }, }; static int wm8998_probe(struct platform_device *pdev) { struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); struct wm8998_priv *wm8998; - int i; + int i, ret; wm8998 = devm_kzalloc(&pdev->dev, sizeof(struct wm8998_priv), GFP_KERNEL); @@ -1405,15 +1419,35 @@ static int wm8998_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_idle(&pdev->dev); - return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8998, - wm8998_dai, ARRAY_SIZE(wm8998_dai)); + ret = arizona_init_spk_irqs(arizona); + if (ret < 0) + return ret; + + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8998, + wm8998_dai, ARRAY_SIZE(wm8998_dai)); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register codec: %d\n", ret); + goto err_spk_irqs; + } + + return ret; + +err_spk_irqs: + arizona_free_spk_irqs(arizona); + + return ret; } static int wm8998_remove(struct platform_device *pdev) { + struct wm8998_priv *wm8998 = platform_get_drvdata(pdev); + struct arizona *arizona = wm8998->core.arizona; + snd_soc_unregister_codec(&pdev->dev); pm_runtime_disable(&pdev->dev); + arizona_free_spk_irqs(arizona); + return 0; } diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index 363b3b667616..6febef337dd2 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -1274,7 +1274,7 @@ static int wm9081_probe(struct snd_soc_codec *codec) return 0; } -static struct snd_soc_codec_driver soc_codec_dev_wm9081 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm9081 = { .probe = wm9081_probe, .set_sysclk = wm9081_set_sysclk, @@ -1282,12 +1282,14 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9081 = { .idle_bias_off = true, - .controls = wm9081_snd_controls, - .num_controls = ARRAY_SIZE(wm9081_snd_controls), - .dapm_widgets = wm9081_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm9081_dapm_widgets), - .dapm_routes = wm9081_audio_paths, - .num_dapm_routes = ARRAY_SIZE(wm9081_audio_paths), + .component_driver = { + .controls = wm9081_snd_controls, + .num_controls = ARRAY_SIZE(wm9081_snd_controls), + .dapm_widgets = wm9081_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm9081_dapm_widgets), + .dapm_routes = wm9081_audio_paths, + .num_dapm_routes = ARRAY_SIZE(wm9081_audio_paths), + }, }; static const struct regmap_config wm9081_regmap = { @@ -1302,7 +1304,6 @@ static const struct regmap_config wm9081_regmap = { .cache_type = REGCACHE_RBTREE, }; -#if IS_ENABLED(CONFIG_I2C) static int wm9081_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -1382,7 +1383,6 @@ static struct i2c_driver wm9081_i2c_driver = { .remove = wm9081_i2c_remove, .id_table = wm9081_i2c_id, }; -#endif module_i2c_driver(wm9081_i2c_driver); diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c index 5d737290f547..5a131385cb2f 100644 --- a/sound/soc/codecs/wm9090.c +++ b/sound/soc/codecs/wm9090.c @@ -550,7 +550,7 @@ static int wm9090_probe(struct snd_soc_codec *codec) return 0; } -static struct snd_soc_codec_driver soc_codec_dev_wm9090 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm9090 = { .probe = wm9090_probe, .set_bias_level = wm9090_set_bias_level, .suspend_bias_off = true, diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c index 744842c76a60..f6d5c0f2aea5 100644 --- a/sound/soc/codecs/wm9705.c +++ b/sound/soc/codecs/wm9705.c @@ -14,37 +14,58 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/device.h> +#include <linux/regmap.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/ac97_codec.h> #include <sound/initval.h> #include <sound/soc.h> -#include "wm9705.h" - #define WM9705_VENDOR_ID 0x574d4c05 #define WM9705_VENDOR_ID_MASK 0xffffffff -/* - * WM9705 register cache - */ -static const u16 wm9705_reg[] = { - 0x6150, 0x8000, 0x8000, 0x8000, /* 0x0 */ - 0x0000, 0x8000, 0x8008, 0x8008, /* 0x8 */ - 0x8808, 0x8808, 0x8808, 0x8808, /* 0x10 */ - 0x8808, 0x0000, 0x8000, 0x0000, /* 0x18 */ - 0x0000, 0x0000, 0x0000, 0x000f, /* 0x20 */ - 0x0605, 0x0000, 0xbb80, 0x0000, /* 0x28 */ - 0x0000, 0xbb80, 0x0000, 0x0000, /* 0x30 */ - 0x0000, 0x2000, 0x0000, 0x0000, /* 0x38 */ - 0x0000, 0x0000, 0x0000, 0x0000, /* 0x40 */ - 0x0000, 0x0000, 0x0000, 0x0000, /* 0x48 */ - 0x0000, 0x0000, 0x0000, 0x0000, /* 0x50 */ - 0x0000, 0x0000, 0x0000, 0x0000, /* 0x58 */ - 0x0000, 0x0000, 0x0000, 0x0000, /* 0x60 */ - 0x0000, 0x0000, 0x0000, 0x0000, /* 0x68 */ - 0x0000, 0x0808, 0x0000, 0x0006, /* 0x70 */ - 0x0000, 0x0000, 0x574d, 0x4c05, /* 0x78 */ +static const struct reg_default wm9705_reg_defaults[] = { + { 0x02, 0x8000 }, + { 0x04, 0x8000 }, + { 0x06, 0x8000 }, + { 0x0a, 0x8000 }, + { 0x0c, 0x8008 }, + { 0x0e, 0x8008 }, + { 0x10, 0x8808 }, + { 0x12, 0x8808 }, + { 0x14, 0x8808 }, + { 0x16, 0x8808 }, + { 0x18, 0x8808 }, + { 0x1a, 0x0000 }, + { 0x1c, 0x8000 }, + { 0x20, 0x0000 }, + { 0x22, 0x0000 }, + { 0x26, 0x000f }, + { 0x28, 0x0605 }, + { 0x2a, 0x0000 }, + { 0x2c, 0xbb80 }, + { 0x32, 0xbb80 }, + { 0x34, 0x2000 }, + { 0x5a, 0x0000 }, + { 0x5c, 0x0000 }, + { 0x72, 0x0808 }, + { 0x74, 0x0000 }, + { 0x76, 0x0006 }, + { 0x78, 0x0000 }, + { 0x7a, 0x0000 }, +}; + +static const struct regmap_config wm9705_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x7e, + .cache_type = REGCACHE_RBTREE, + + .volatile_reg = regmap_ac97_default_volatile, + + .reg_defaults = wm9705_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm9705_reg_defaults), }; static const struct snd_kcontrol_new wm9705_snd_ac97_controls[] = { @@ -203,57 +224,20 @@ static const struct snd_soc_dapm_route wm9705_audio_map[] = { {"Right ADC", NULL, "ADC PGA"}, }; -/* We use a register cache to enhance read performance. */ -static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg) -{ - struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); - u16 *cache = codec->reg_cache; - - switch (reg) { - case AC97_RESET: - case AC97_VENDOR_ID1: - case AC97_VENDOR_ID2: - return soc_ac97_ops->read(ac97, reg); - default: - reg = reg >> 1; - - if (reg >= (ARRAY_SIZE(wm9705_reg))) - return -EIO; - - return cache[reg]; - } -} - -static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int val) -{ - struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); - u16 *cache = codec->reg_cache; - - soc_ac97_ops->write(ac97, reg, val); - reg = reg >> 1; - if (reg < (ARRAY_SIZE(wm9705_reg))) - cache[reg] = val; - - return 0; -} - static int ac97_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; int reg; - u16 vra; - vra = ac97_read(codec, AC97_EXTENDED_STATUS); - ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1); + snd_soc_update_bits(codec, AC97_EXTENDED_STATUS, 0x1, 0x1); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) reg = AC97_PCM_FRONT_DAC_RATE; else reg = AC97_PCM_LR_ADC_RATE; - return ac97_write(codec, reg, substream->runtime->rate); + return snd_soc_write(codec, reg, substream->runtime->rate); } #define WM9705_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \ @@ -299,9 +283,9 @@ static struct snd_soc_dai_driver wm9705_dai[] = { #ifdef CONFIG_PM static int wm9705_soc_suspend(struct snd_soc_codec *codec) { - struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); - - soc_ac97_ops->write(ac97, AC97_POWERDOWN, 0xffff); + regcache_cache_bypass(codec->component.regmap, true); + snd_soc_write(codec, AC97_POWERDOWN, 0xffff); + regcache_cache_bypass(codec->component.regmap, false); return 0; } @@ -309,17 +293,14 @@ static int wm9705_soc_suspend(struct snd_soc_codec *codec) static int wm9705_soc_resume(struct snd_soc_codec *codec) { struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); - int i, ret; - u16 *cache = codec->reg_cache; + int ret; ret = snd_ac97_reset(ac97, true, WM9705_VENDOR_ID, WM9705_VENDOR_ID_MASK); if (ret < 0) return ret; - for (i = 2; i < ARRAY_SIZE(wm9705_reg) << 1; i += 2) { - soc_ac97_ops->write(ac97, i, cache[i>>1]); - } + regcache_sync(codec->component.regmap); return 0; } @@ -331,6 +312,8 @@ static int wm9705_soc_resume(struct snd_soc_codec *codec) static int wm9705_soc_probe(struct snd_soc_codec *codec) { struct snd_ac97 *ac97; + struct regmap *regmap; + int ret; ac97 = snd_soc_new_ac97_codec(codec, WM9705_VENDOR_ID, WM9705_VENDOR_ID_MASK); @@ -339,37 +322,44 @@ static int wm9705_soc_probe(struct snd_soc_codec *codec) return PTR_ERR(ac97); } + regmap = regmap_init_ac97(ac97, &wm9705_regmap_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + goto err_free_ac97_codec; + } + snd_soc_codec_set_drvdata(codec, ac97); + snd_soc_codec_init_regmap(codec, regmap); return 0; +err_free_ac97_codec: + snd_soc_free_ac97_codec(ac97); + return ret; } static int wm9705_soc_remove(struct snd_soc_codec *codec) { struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); + snd_soc_codec_exit_regmap(codec); snd_soc_free_ac97_codec(ac97); return 0; } -static struct snd_soc_codec_driver soc_codec_dev_wm9705 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm9705 = { .probe = wm9705_soc_probe, .remove = wm9705_soc_remove, .suspend = wm9705_soc_suspend, .resume = wm9705_soc_resume, - .read = ac97_read, - .write = ac97_write, - .reg_cache_size = ARRAY_SIZE(wm9705_reg), - .reg_word_size = sizeof(u16), - .reg_cache_step = 2, - .reg_cache_default = wm9705_reg, - - .controls = wm9705_snd_ac97_controls, - .num_controls = ARRAY_SIZE(wm9705_snd_ac97_controls), - .dapm_widgets = wm9705_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm9705_dapm_widgets), - .dapm_routes = wm9705_audio_map, - .num_dapm_routes = ARRAY_SIZE(wm9705_audio_map), + + .component_driver = { + .controls = wm9705_snd_ac97_controls, + .num_controls = ARRAY_SIZE(wm9705_snd_ac97_controls), + .dapm_widgets = wm9705_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm9705_dapm_widgets), + .dapm_routes = wm9705_audio_map, + .num_dapm_routes = ARRAY_SIZE(wm9705_audio_map), + }, }; static int wm9705_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/wm9705.h b/sound/soc/codecs/wm9705.h deleted file mode 100644 index 23ea9ce47359..000000000000 --- a/sound/soc/codecs/wm9705.h +++ /dev/null @@ -1,11 +0,0 @@ -/* - * wm9705.h -- WM9705 Soc Audio driver - */ - -#ifndef _WM9705_H -#define _WM9705_H - -#define WM9705_DAI_AC97_HIFI 0 -#define WM9705_DAI_AC97_AUX 1 - -#endif diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 488a92224249..1a3e1797994a 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -15,13 +15,13 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/device.h> +#include <linux/regmap.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/ac97_codec.h> #include <sound/initval.h> #include <sound/soc.h> #include <sound/tlv.h> -#include "wm9712.h" #define WM9712_VENDOR_ID 0x574d4c12 #define WM9712_VENDOR_ID_MASK 0xffffffff @@ -32,31 +32,66 @@ struct wm9712_priv { struct mutex lock; }; -static unsigned int ac97_read(struct snd_soc_codec *codec, - unsigned int reg); -static int ac97_write(struct snd_soc_codec *codec, - unsigned int reg, unsigned int val); +static const struct reg_default wm9712_reg_defaults[] = { + { 0x02, 0x8000 }, + { 0x04, 0x8000 }, + { 0x06, 0x8000 }, + { 0x08, 0x0f0f }, + { 0x0a, 0xaaa0 }, + { 0x0c, 0xc008 }, + { 0x0e, 0x6808 }, + { 0x10, 0xe808 }, + { 0x12, 0xaaa0 }, + { 0x14, 0xad00 }, + { 0x16, 0x8000 }, + { 0x18, 0xe808 }, + { 0x1a, 0x3000 }, + { 0x1c, 0x8000 }, + { 0x20, 0x0000 }, + { 0x22, 0x0000 }, + { 0x26, 0x000f }, + { 0x28, 0x0605 }, + { 0x2a, 0x0410 }, + { 0x2c, 0xbb80 }, + { 0x2e, 0xbb80 }, + { 0x32, 0xbb80 }, + { 0x34, 0x2000 }, + { 0x4c, 0xf83e }, + { 0x4e, 0xffff }, + { 0x50, 0x0000 }, + { 0x52, 0x0000 }, + { 0x56, 0xf83e }, + { 0x58, 0x0008 }, + { 0x5c, 0x0000 }, + { 0x60, 0xb032 }, + { 0x62, 0x3e00 }, + { 0x64, 0x0000 }, + { 0x76, 0x0006 }, + { 0x78, 0x0001 }, + { 0x7a, 0x0000 }, +}; -/* - * WM9712 register cache - */ -static const u16 wm9712_reg[] = { - 0x6174, 0x8000, 0x8000, 0x8000, /* 6 */ - 0x0f0f, 0xaaa0, 0xc008, 0x6808, /* e */ - 0xe808, 0xaaa0, 0xad00, 0x8000, /* 16 */ - 0xe808, 0x3000, 0x8000, 0x0000, /* 1e */ - 0x0000, 0x0000, 0x0000, 0x000f, /* 26 */ - 0x0405, 0x0410, 0xbb80, 0xbb80, /* 2e */ - 0x0000, 0xbb80, 0x0000, 0x0000, /* 36 */ - 0x0000, 0x2000, 0x0000, 0x0000, /* 3e */ - 0x0000, 0x0000, 0x0000, 0x0000, /* 46 */ - 0x0000, 0x0000, 0xf83e, 0xffff, /* 4e */ - 0x0000, 0x0000, 0x0000, 0xf83e, /* 56 */ - 0x0008, 0x0000, 0x0000, 0x0000, /* 5e */ - 0xb032, 0x3e00, 0x0000, 0x0000, /* 66 */ - 0x0000, 0x0000, 0x0000, 0x0000, /* 6e */ - 0x0000, 0x0000, 0x0000, 0x0006, /* 76 */ - 0x0001, 0x0000, 0x574d, 0x4c12, /* 7e */ +static bool wm9712_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AC97_REC_GAIN: + return true; + default: + return regmap_ac97_default_volatile(dev, reg); + } +} + +static const struct regmap_config wm9712_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x7e, + .cache_type = REGCACHE_RBTREE, + + .volatile_reg = wm9712_volatile_reg, + + .reg_defaults = wm9712_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm9712_reg_defaults), }; #define HPL_MIXER 0x0 @@ -187,7 +222,7 @@ static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol, struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int mixer, mask, shift, old; - struct snd_soc_dapm_update update; + struct snd_soc_dapm_update update = { 0 }; bool change; mixer = mc->shift >> 8; @@ -485,75 +520,36 @@ static const struct snd_soc_dapm_route wm9712_audio_map[] = { {"ROUT2", NULL, "Speaker PGA"}, }; -static unsigned int ac97_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); - u16 *cache = codec->reg_cache; - - if (reg == AC97_RESET || reg == AC97_GPIO_STATUS || - reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 || - reg == AC97_REC_GAIN) - return soc_ac97_ops->read(wm9712->ac97, reg); - else { - reg = reg >> 1; - - if (reg >= (ARRAY_SIZE(wm9712_reg))) - return -EIO; - - return cache[reg]; - } -} - -static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int val) -{ - struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); - u16 *cache = codec->reg_cache; - - soc_ac97_ops->write(wm9712->ac97, reg, val); - reg = reg >> 1; - if (reg < (ARRAY_SIZE(wm9712_reg))) - cache[reg] = val; - - return 0; -} - static int ac97_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; int reg; - u16 vra; struct snd_pcm_runtime *runtime = substream->runtime; - vra = ac97_read(codec, AC97_EXTENDED_STATUS); - ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1); + snd_soc_update_bits(codec, AC97_EXTENDED_STATUS, 0x1, 0x1); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) reg = AC97_PCM_FRONT_DAC_RATE; else reg = AC97_PCM_LR_ADC_RATE; - return ac97_write(codec, reg, runtime->rate); + return snd_soc_write(codec, reg, runtime->rate); } static int ac97_aux_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; - u16 vra, xsle; struct snd_pcm_runtime *runtime = substream->runtime; - vra = ac97_read(codec, AC97_EXTENDED_STATUS); - ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1); - xsle = ac97_read(codec, AC97_PCI_SID); - ac97_write(codec, AC97_PCI_SID, xsle | 0x8000); + snd_soc_update_bits(codec, AC97_EXTENDED_STATUS, 0x1, 0x1); + snd_soc_update_bits(codec, AC97_PCI_SID, 0x8000, 0x8000); if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) return -ENODEV; - return ac97_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate); + return snd_soc_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate); } #define WM9712_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ @@ -605,12 +601,12 @@ static int wm9712_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - ac97_write(codec, AC97_POWERDOWN, 0x0000); + snd_soc_write(codec, AC97_POWERDOWN, 0x0000); break; case SND_SOC_BIAS_OFF: /* disable everything including AC link */ - ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff); - ac97_write(codec, AC97_POWERDOWN, 0xffff); + snd_soc_write(codec, AC97_EXTENDED_MSTATUS, 0xffff); + snd_soc_write(codec, AC97_POWERDOWN, 0xffff); break; } return 0; @@ -619,8 +615,7 @@ static int wm9712_set_bias_level(struct snd_soc_codec *codec, static int wm9712_soc_resume(struct snd_soc_codec *codec) { struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); - int i, ret; - u16 *cache = codec->reg_cache; + int ret; ret = snd_ac97_reset(wm9712->ac97, true, WM9712_VENDOR_ID, WM9712_VENDOR_ID_MASK); @@ -629,15 +624,8 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec) snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); - if (ret == 0) { - /* Sync reg_cache with the hardware after cold reset */ - for (i = 2; i < ARRAY_SIZE(wm9712_reg) << 1; i += 2) { - if (i == AC97_INT_PAGING || i == AC97_POWERDOWN || - (i > 0x58 && i != 0x5c)) - continue; - soc_ac97_ops->write(wm9712->ac97, i, cache[i>>1]); - } - } + if (ret == 0) + regcache_sync(codec->component.regmap); return ret; } @@ -645,6 +633,7 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec) static int wm9712_soc_probe(struct snd_soc_codec *codec) { struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); + struct regmap *regmap; int ret; wm9712->ac97 = snd_soc_new_ac97_codec(codec, WM9712_VENDOR_ID, @@ -655,39 +644,47 @@ static int wm9712_soc_probe(struct snd_soc_codec *codec) return ret; } + regmap = regmap_init_ac97(wm9712->ac97, &wm9712_regmap_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + goto err_free_ac97_codec; + } + + snd_soc_codec_init_regmap(codec, regmap); + /* set alc mux to none */ - ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000); + snd_soc_update_bits(codec, AC97_VIDEO, 0x3000, 0x3000); return 0; +err_free_ac97_codec: + snd_soc_free_ac97_codec(wm9712->ac97); + return ret; } static int wm9712_soc_remove(struct snd_soc_codec *codec) { struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); + snd_soc_codec_exit_regmap(codec); snd_soc_free_ac97_codec(wm9712->ac97); return 0; } -static struct snd_soc_codec_driver soc_codec_dev_wm9712 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm9712 = { .probe = wm9712_soc_probe, .remove = wm9712_soc_remove, .resume = wm9712_soc_resume, - .read = ac97_read, - .write = ac97_write, .set_bias_level = wm9712_set_bias_level, .suspend_bias_off = true, - .reg_cache_size = ARRAY_SIZE(wm9712_reg), - .reg_word_size = sizeof(u16), - .reg_cache_step = 2, - .reg_cache_default = wm9712_reg, - - .controls = wm9712_snd_ac97_controls, - .num_controls = ARRAY_SIZE(wm9712_snd_ac97_controls), - .dapm_widgets = wm9712_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm9712_dapm_widgets), - .dapm_routes = wm9712_audio_map, - .num_dapm_routes = ARRAY_SIZE(wm9712_audio_map), + + .component_driver = { + .controls = wm9712_snd_ac97_controls, + .num_controls = ARRAY_SIZE(wm9712_snd_ac97_controls), + .dapm_widgets = wm9712_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm9712_dapm_widgets), + .dapm_routes = wm9712_audio_map, + .num_dapm_routes = ARRAY_SIZE(wm9712_audio_map), + }, }; static int wm9712_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/wm9712.h b/sound/soc/codecs/wm9712.h deleted file mode 100644 index fb69c3aa4ed0..000000000000 --- a/sound/soc/codecs/wm9712.h +++ /dev/null @@ -1,11 +0,0 @@ -/* - * wm9712.h -- WM9712 Soc Audio driver - */ - -#ifndef _WM9712_H -#define _WM9712_H - -#define WM9712_DAI_AC97_HIFI 0 -#define WM9712_DAI_AC97_AUX 1 - -#endif diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 9849643ef809..7e4822185feb 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -231,7 +231,7 @@ static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol, struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int mixer, mask, shift, old; - struct snd_soc_dapm_update update; + struct snd_soc_dapm_update update = { 0 }; bool change; mixer = mc->shift >> 8; @@ -1235,19 +1235,21 @@ static int wm9713_soc_remove(struct snd_soc_codec *codec) return 0; } -static struct snd_soc_codec_driver soc_codec_dev_wm9713 = { +static const struct snd_soc_codec_driver soc_codec_dev_wm9713 = { .probe = wm9713_soc_probe, .remove = wm9713_soc_remove, .suspend = wm9713_soc_suspend, .resume = wm9713_soc_resume, .set_bias_level = wm9713_set_bias_level, - .controls = wm9713_snd_ac97_controls, - .num_controls = ARRAY_SIZE(wm9713_snd_ac97_controls), - .dapm_widgets = wm9713_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm9713_dapm_widgets), - .dapm_routes = wm9713_audio_map, - .num_dapm_routes = ARRAY_SIZE(wm9713_audio_map), + .component_driver = { + .controls = wm9713_snd_ac97_controls, + .num_controls = ARRAY_SIZE(wm9713_snd_ac97_controls), + .dapm_widgets = wm9713_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm9713_dapm_widgets), + .dapm_routes = wm9713_audio_map, + .num_dapm_routes = ARRAY_SIZE(wm9713_audio_map), + }, }; static int wm9713_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/wm9713.h b/sound/soc/codecs/wm9713.h index 53df11b1f727..7ecffc563016 100644 --- a/sound/soc/codecs/wm9713.h +++ b/sound/soc/codecs/wm9713.h @@ -41,8 +41,4 @@ #define WM9713_PCMBCLK_DIV_8 (3 << 9) #define WM9713_PCMBCLK_DIV_16 (4 << 9) -#define WM9713_DAI_AC97_HIFI 0 -#define WM9713_DAI_AC97_AUX 1 -#define WM9713_DAI_PCM_VOICE 2 - #endif diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 21fbe7d07063..d72ccef9e238 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -162,6 +162,16 @@ #define ADSP_MAX_STD_CTRL_SIZE 512 +#define WM_ADSP_ACKED_CTL_TIMEOUT_MS 100 +#define WM_ADSP_ACKED_CTL_N_QUICKPOLLS 10 +#define WM_ADSP_ACKED_CTL_MIN_VALUE 0 +#define WM_ADSP_ACKED_CTL_MAX_VALUE 0xFFFFFF + +/* + * Event control messages + */ +#define WM_ADSP_FW_EVENT_SHUTDOWN 0x000001 + struct wm_adsp_buf { struct list_head list; void *buf; @@ -177,7 +187,7 @@ static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len, buf->buf = vmalloc(len); if (!buf->buf) { - vfree(buf); + kfree(buf); return NULL; } memcpy(buf->buf, src, len); @@ -441,11 +451,29 @@ struct wm_coeff_ctl { unsigned int offset; size_t len; unsigned int set:1; - struct snd_kcontrol *kcontrol; struct soc_bytes_ext bytes_ext; unsigned int flags; + unsigned int type; }; +static const char *wm_adsp_mem_region_name(unsigned int type) +{ + switch (type) { + case WMFW_ADSP1_PM: + return "PM"; + case WMFW_ADSP1_DM: + return "DM"; + case WMFW_ADSP2_XM: + return "XM"; + case WMFW_ADSP2_YM: + return "YM"; + case WMFW_ADSP1_ZM: + return "ZM"; + default: + return NULL; + } +} + #ifdef CONFIG_DEBUG_FS static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s) { @@ -480,7 +508,7 @@ static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file, mutex_lock(&dsp->pwr_lock); - if (!dsp->wmfw_file_name || !dsp->running) + if (!dsp->wmfw_file_name || !dsp->booted) ret = 0; else ret = simple_read_from_buffer(user_buf, count, ppos, @@ -500,7 +528,7 @@ static ssize_t wm_adsp_debugfs_bin_read(struct file *file, mutex_lock(&dsp->pwr_lock); - if (!dsp->bin_file_name || !dsp->running) + if (!dsp->bin_file_name || !dsp->booted) ret = 0; else ret = simple_read_from_buffer(user_buf, count, ppos, @@ -554,6 +582,9 @@ static void wm_adsp2_init_debugfs(struct wm_adsp *dsp, if (!root) goto err; + if (!debugfs_create_bool("booted", S_IRUGO, root, &dsp->booted)) + goto err; + if (!debugfs_create_bool("running", S_IRUGO, root, &dsp->running)) goto err; @@ -637,7 +668,7 @@ static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, mutex_lock(&dsp[e->shift_l].pwr_lock); - if (dsp[e->shift_l].running || dsp[e->shift_l].compr) + if (dsp[e->shift_l].booted || dsp[e->shift_l].compr) ret = -EBUSY; else dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0]; @@ -724,6 +755,24 @@ static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext) return container_of(ext, struct wm_coeff_ctl, bytes_ext); } +static int wm_coeff_base_reg(struct wm_coeff_ctl *ctl, unsigned int *reg) +{ + const struct wm_adsp_alg_region *alg_region = &ctl->alg_region; + struct wm_adsp *dsp = ctl->dsp; + const struct wm_adsp_region *mem; + + mem = wm_adsp_find_region(dsp, alg_region->type); + if (!mem) { + adsp_err(dsp, "No base for region %x\n", + alg_region->type); + return -EINVAL; + } + + *reg = wm_adsp_region_to_reg(mem, ctl->alg_region.base + ctl->offset); + + return 0; +} + static int wm_coeff_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) { @@ -731,30 +780,94 @@ static int wm_coeff_info(struct snd_kcontrol *kctl, (struct soc_bytes_ext *)kctl->private_value; struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); - uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; - uinfo->count = ctl->len; + switch (ctl->type) { + case WMFW_CTL_TYPE_ACKED: + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->value.integer.min = WM_ADSP_ACKED_CTL_MIN_VALUE; + uinfo->value.integer.max = WM_ADSP_ACKED_CTL_MAX_VALUE; + uinfo->value.integer.step = 1; + uinfo->count = 1; + break; + default: + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = ctl->len; + break; + } + return 0; } +static int wm_coeff_write_acked_control(struct wm_coeff_ctl *ctl, + unsigned int event_id) +{ + struct wm_adsp *dsp = ctl->dsp; + u32 val = cpu_to_be32(event_id); + unsigned int reg; + int i, ret; + + ret = wm_coeff_base_reg(ctl, ®); + if (ret) + return ret; + + adsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n", + event_id, ctl->alg_region.alg, + wm_adsp_mem_region_name(ctl->alg_region.type), ctl->offset); + + ret = regmap_raw_write(dsp->regmap, reg, &val, sizeof(val)); + if (ret) { + adsp_err(dsp, "Failed to write %x: %d\n", reg, ret); + return ret; + } + + /* + * Poll for ack, we initially poll at ~1ms intervals for firmwares + * that respond quickly, then go to ~10ms polls. A firmware is unlikely + * to ack instantly so we do the first 1ms delay before reading the + * control to avoid a pointless bus transaction + */ + for (i = 0; i < WM_ADSP_ACKED_CTL_TIMEOUT_MS;) { + switch (i) { + case 0 ... WM_ADSP_ACKED_CTL_N_QUICKPOLLS - 1: + usleep_range(1000, 2000); + i++; + break; + default: + usleep_range(10000, 20000); + i += 10; + break; + } + + ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); + if (ret) { + adsp_err(dsp, "Failed to read %x: %d\n", reg, ret); + return ret; + } + + if (val == 0) { + adsp_dbg(dsp, "Acked control ACKED at poll %u\n", i); + return 0; + } + } + + adsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n", + reg, ctl->alg_region.alg, + wm_adsp_mem_region_name(ctl->alg_region.type), + ctl->offset); + + return -ETIMEDOUT; +} + static int wm_coeff_write_control(struct wm_coeff_ctl *ctl, const void *buf, size_t len) { - struct wm_adsp_alg_region *alg_region = &ctl->alg_region; - const struct wm_adsp_region *mem; struct wm_adsp *dsp = ctl->dsp; void *scratch; int ret; unsigned int reg; - mem = wm_adsp_find_region(dsp, alg_region->type); - if (!mem) { - adsp_err(dsp, "No base for region %x\n", - alg_region->type); - return -EINVAL; - } - - reg = ctl->alg_region.base + ctl->offset; - reg = wm_adsp_region_to_reg(mem, reg); + ret = wm_coeff_base_reg(ctl, ®); + if (ret) + return ret; scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA); if (!scratch) @@ -789,7 +902,7 @@ static int wm_coeff_put(struct snd_kcontrol *kctl, memcpy(ctl->cache, p, ctl->len); ctl->set = 1; - if (ctl->enabled) + if (ctl->enabled && ctl->dsp->running) ret = wm_coeff_write_control(ctl, p, ctl->len); mutex_unlock(&ctl->dsp->pwr_lock); @@ -811,7 +924,7 @@ static int wm_coeff_tlv_put(struct snd_kcontrol *kctl, ret = -EFAULT; } else { ctl->set = 1; - if (ctl->enabled) + if (ctl->enabled && ctl->dsp->running) ret = wm_coeff_write_control(ctl, ctl->cache, size); } @@ -820,25 +933,41 @@ static int wm_coeff_tlv_put(struct snd_kcontrol *kctl, return ret; } +static int wm_coeff_put_acked(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *)kctl->private_value; + struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); + unsigned int val = ucontrol->value.integer.value[0]; + int ret; + + if (val == 0) + return 0; /* 0 means no event */ + + mutex_lock(&ctl->dsp->pwr_lock); + + if (ctl->enabled) + ret = wm_coeff_write_acked_control(ctl, val); + else + ret = -EPERM; + + mutex_unlock(&ctl->dsp->pwr_lock); + + return ret; +} + static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, void *buf, size_t len) { - struct wm_adsp_alg_region *alg_region = &ctl->alg_region; - const struct wm_adsp_region *mem; struct wm_adsp *dsp = ctl->dsp; void *scratch; int ret; unsigned int reg; - mem = wm_adsp_find_region(dsp, alg_region->type); - if (!mem) { - adsp_err(dsp, "No base for region %x\n", - alg_region->type); - return -EINVAL; - } - - reg = ctl->alg_region.base + ctl->offset; - reg = wm_adsp_region_to_reg(mem, reg); + ret = wm_coeff_base_reg(ctl, ®); + if (ret) + return ret; scratch = kmalloc(len, GFP_KERNEL | GFP_DMA); if (!scratch) @@ -871,12 +1000,12 @@ static int wm_coeff_get(struct snd_kcontrol *kctl, mutex_lock(&ctl->dsp->pwr_lock); if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { - if (ctl->enabled) + if (ctl->enabled && ctl->dsp->running) ret = wm_coeff_read_control(ctl, p, ctl->len); else ret = -EPERM; } else { - if (!ctl->flags && ctl->enabled) + if (!ctl->flags && ctl->enabled && ctl->dsp->running) ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len); memcpy(p, ctl->cache, ctl->len); @@ -898,12 +1027,12 @@ static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, mutex_lock(&ctl->dsp->pwr_lock); if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { - if (ctl->enabled) + if (ctl->enabled && ctl->dsp->running) ret = wm_coeff_read_control(ctl, ctl->cache, size); else ret = -EPERM; } else { - if (!ctl->flags && ctl->enabled) + if (!ctl->flags && ctl->enabled && ctl->dsp->running) ret = wm_coeff_read_control(ctl, ctl->cache, size); } @@ -915,6 +1044,21 @@ static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, return ret; } +static int wm_coeff_get_acked(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* + * Although it's not useful to read an acked control, we must satisfy + * user-side assumptions that all controls are readable and that a + * write of the same value should be filtered out (it's valid to send + * the same event number again to the firmware). We therefore return 0, + * meaning "no event" so valid event numbers will always be a change + */ + ucontrol->value.integer.value[0] = 0; + + return 0; +} + struct wmfw_ctl_work { struct wm_adsp *dsp; struct wm_coeff_ctl *ctl; @@ -964,30 +1108,35 @@ static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); if (!kcontrol) return -ENOMEM; - kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; kcontrol->name = ctl->name; kcontrol->info = wm_coeff_info; - kcontrol->get = wm_coeff_get; - kcontrol->put = wm_coeff_put; kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; kcontrol->tlv.c = snd_soc_bytes_tlv_callback; kcontrol->private_value = (unsigned long)&ctl->bytes_ext; + kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len); - ctl->bytes_ext.max = ctl->len; - ctl->bytes_ext.get = wm_coeff_tlv_get; - ctl->bytes_ext.put = wm_coeff_tlv_put; + switch (ctl->type) { + case WMFW_CTL_TYPE_ACKED: + kcontrol->get = wm_coeff_get_acked; + kcontrol->put = wm_coeff_put_acked; + break; + default: + kcontrol->get = wm_coeff_get; + kcontrol->put = wm_coeff_put; - kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len); + ctl->bytes_ext.max = ctl->len; + ctl->bytes_ext.get = wm_coeff_tlv_get; + ctl->bytes_ext.put = wm_coeff_tlv_put; + break; + } - ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1); + ret = snd_soc_add_codec_controls(dsp->codec, kcontrol, 1); if (ret < 0) goto err_kcontrol; kfree(kcontrol); - ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card, ctl->name); - return 0; err_kcontrol: @@ -1032,6 +1181,27 @@ static int wm_coeff_sync_controls(struct wm_adsp *dsp) return 0; } +static void wm_adsp_signal_event_controls(struct wm_adsp *dsp, + unsigned int event) +{ + struct wm_coeff_ctl *ctl; + int ret; + + list_for_each_entry(ctl, &dsp->ctl_list, list) { + if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT) + continue; + + if (!ctl->enabled) + continue; + + ret = wm_coeff_write_acked_control(ctl, event); + if (ret) + adsp_warn(dsp, + "Failed to send 0x%x event to alg 0x%x (%d)\n", + event, ctl->alg_region.alg, ret); + } +} + static void wm_adsp_ctl_work(struct work_struct *work) { struct wmfw_ctl_work *ctl_work = container_of(work, @@ -1053,34 +1223,16 @@ static int wm_adsp_create_control(struct wm_adsp *dsp, const struct wm_adsp_alg_region *alg_region, unsigned int offset, unsigned int len, const char *subname, unsigned int subname_len, - unsigned int flags) + unsigned int flags, unsigned int type) { struct wm_coeff_ctl *ctl; struct wmfw_ctl_work *ctl_work; char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - char *region_name; + const char *region_name; int ret; - if (flags & WMFW_CTL_FLAG_SYS) - return 0; - - switch (alg_region->type) { - case WMFW_ADSP1_PM: - region_name = "PM"; - break; - case WMFW_ADSP1_DM: - region_name = "DM"; - break; - case WMFW_ADSP2_XM: - region_name = "XM"; - break; - case WMFW_ADSP2_YM: - region_name = "YM"; - break; - case WMFW_ADSP1_ZM: - region_name = "ZM"; - break; - default: + region_name = wm_adsp_mem_region_name(alg_region->type); + if (!region_name) { adsp_err(dsp, "Unknown region type: %d\n", alg_region->type); return -EINVAL; } @@ -1136,6 +1288,7 @@ static int wm_adsp_create_control(struct wm_adsp *dsp, ctl->dsp = dsp; ctl->flags = flags; + ctl->type = type; ctl->offset = offset; ctl->len = len; ctl->cache = kzalloc(ctl->len, GFP_KERNEL); @@ -1146,6 +1299,9 @@ static int wm_adsp_create_control(struct wm_adsp *dsp, list_add(&ctl->list, &dsp->ctl_list); + if (flags & WMFW_CTL_FLAG_SYS) + return 0; + ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL); if (!ctl_work) { ret = -ENOMEM; @@ -1305,6 +1461,21 @@ static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data, adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len); } +static int wm_adsp_check_coeff_flags(struct wm_adsp *dsp, + const struct wm_coeff_parsed_coeff *coeff_blk, + unsigned int f_required, + unsigned int f_illegal) +{ + if ((coeff_blk->flags & f_illegal) || + ((coeff_blk->flags & f_required) != f_required)) { + adsp_err(dsp, "Illegal flags 0x%x for control type 0x%x\n", + coeff_blk->flags, coeff_blk->ctl_type); + return -EINVAL; + } + + return 0; +} + static int wm_adsp_parse_coeff(struct wm_adsp *dsp, const struct wmfw_region *region) { @@ -1321,6 +1492,28 @@ static int wm_adsp_parse_coeff(struct wm_adsp *dsp, switch (coeff_blk.ctl_type) { case SNDRV_CTL_ELEM_TYPE_BYTES: break; + case WMFW_CTL_TYPE_ACKED: + if (coeff_blk.flags & WMFW_CTL_FLAG_SYS) + continue; /* ignore */ + + ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, + WMFW_CTL_FLAG_VOLATILE | + WMFW_CTL_FLAG_WRITEABLE | + WMFW_CTL_FLAG_READABLE, + 0); + if (ret) + return -EINVAL; + break; + case WMFW_CTL_TYPE_HOSTEVENT: + ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, + WMFW_CTL_FLAG_SYS | + WMFW_CTL_FLAG_VOLATILE | + WMFW_CTL_FLAG_WRITEABLE | + WMFW_CTL_FLAG_READABLE, + 0); + if (ret) + return -EINVAL; + break; default: adsp_err(dsp, "Unknown control type: %d\n", coeff_blk.ctl_type); @@ -1335,7 +1528,8 @@ static int wm_adsp_parse_coeff(struct wm_adsp *dsp, coeff_blk.len, coeff_blk.name, coeff_blk.name_len, - coeff_blk.flags); + coeff_blk.flags, + coeff_blk.ctl_type); if (ret < 0) adsp_err(dsp, "Failed to create control: %.*s, %d\n", coeff_blk.name_len, coeff_blk.name, ret); @@ -1357,7 +1551,7 @@ static int wm_adsp_load(struct wm_adsp *dsp) const struct wmfw_region *region; const struct wm_adsp_region *mem; const char *region_name; - char *file, *text; + char *file, *text = NULL; struct wm_adsp_buf *buf; unsigned int reg; int regions = 0; @@ -1488,23 +1682,11 @@ static int wm_adsp_load(struct wm_adsp *dsp) reg = offset; break; case WMFW_ADSP1_PM: - region_name = "PM"; - reg = wm_adsp_region_to_reg(mem, offset); - break; case WMFW_ADSP1_DM: - region_name = "DM"; - reg = wm_adsp_region_to_reg(mem, offset); - break; case WMFW_ADSP2_XM: - region_name = "XM"; - reg = wm_adsp_region_to_reg(mem, offset); - break; case WMFW_ADSP2_YM: - region_name = "YM"; - reg = wm_adsp_region_to_reg(mem, offset); - break; case WMFW_ADSP1_ZM: - region_name = "ZM"; + region_name = wm_adsp_mem_region_name(type); reg = wm_adsp_region_to_reg(mem, offset); break; default: @@ -1518,10 +1700,21 @@ static int wm_adsp_load(struct wm_adsp *dsp) regions, le32_to_cpu(region->len), offset, region_name); + if ((pos + le32_to_cpu(region->len) + sizeof(*region)) > + firmware->size) { + adsp_err(dsp, + "%s.%d: %s region len %d bytes exceeds file length %zu\n", + file, regions, region_name, + le32_to_cpu(region->len), firmware->size); + ret = -EINVAL; + goto out_fw; + } + if (text) { memcpy(text, region->data, le32_to_cpu(region->len)); adsp_info(dsp, "%s: %s\n", file, text); kfree(text); + text = NULL; } if (reg) { @@ -1566,6 +1759,7 @@ out_fw: regmap_async_complete(regmap); wm_adsp_buf_free(&buf_list); release_firmware(firmware); + kfree(text); out: kfree(file); @@ -1747,7 +1941,8 @@ static int wm_adsp1_setup_algs(struct wm_adsp *dsp) len -= be32_to_cpu(adsp1_alg[i].dm); len *= 4; wm_adsp_create_control(dsp, alg_region, 0, - len, NULL, 0, 0); + len, NULL, 0, 0, + SNDRV_CTL_ELEM_TYPE_BYTES); } else { adsp_warn(dsp, "Missing length info for region DM with ID %x\n", be32_to_cpu(adsp1_alg[i].alg.id)); @@ -1767,7 +1962,8 @@ static int wm_adsp1_setup_algs(struct wm_adsp *dsp) len -= be32_to_cpu(adsp1_alg[i].zm); len *= 4; wm_adsp_create_control(dsp, alg_region, 0, - len, NULL, 0, 0); + len, NULL, 0, 0, + SNDRV_CTL_ELEM_TYPE_BYTES); } else { adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", be32_to_cpu(adsp1_alg[i].alg.id)); @@ -1858,7 +2054,8 @@ static int wm_adsp2_setup_algs(struct wm_adsp *dsp) len -= be32_to_cpu(adsp2_alg[i].xm); len *= 4; wm_adsp_create_control(dsp, alg_region, 0, - len, NULL, 0, 0); + len, NULL, 0, 0, + SNDRV_CTL_ELEM_TYPE_BYTES); } else { adsp_warn(dsp, "Missing length info for region XM with ID %x\n", be32_to_cpu(adsp2_alg[i].alg.id)); @@ -1878,7 +2075,8 @@ static int wm_adsp2_setup_algs(struct wm_adsp *dsp) len -= be32_to_cpu(adsp2_alg[i].ym); len *= 4; wm_adsp_create_control(dsp, alg_region, 0, - len, NULL, 0, 0); + len, NULL, 0, 0, + SNDRV_CTL_ELEM_TYPE_BYTES); } else { adsp_warn(dsp, "Missing length info for region YM with ID %x\n", be32_to_cpu(adsp2_alg[i].alg.id)); @@ -1898,7 +2096,8 @@ static int wm_adsp2_setup_algs(struct wm_adsp *dsp) len -= be32_to_cpu(adsp2_alg[i].zm); len *= 4; wm_adsp_create_control(dsp, alg_region, 0, - len, NULL, 0, 0); + len, NULL, 0, 0, + SNDRV_CTL_ELEM_TYPE_BYTES); } else { adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", be32_to_cpu(adsp2_alg[i].alg.id)); @@ -2046,6 +2245,17 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) } if (reg) { + if ((pos + le32_to_cpu(blk->len) + sizeof(*blk)) > + firmware->size) { + adsp_err(dsp, + "%s.%d: %s region len %d bytes exceeds file length %zu\n", + file, blocks, region_name, + le32_to_cpu(blk->len), + firmware->size); + ret = -EINVAL; + goto out_fw; + } + buf = wm_adsp_buf_alloc(blk->data, le32_to_cpu(blk->len), &buf_list); @@ -2111,7 +2321,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, int ret; unsigned int val; - dsp->card = codec->component.card; + dsp->codec = codec; mutex_lock(&dsp->pwr_lock); @@ -2166,13 +2376,20 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err_ena; + dsp->booted = true; + /* Start the core running */ regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, ADSP1_CORE_ENA | ADSP1_START, ADSP1_CORE_ENA | ADSP1_START); + + dsp->running = true; break; case SND_SOC_DAPM_PRE_PMD: + dsp->running = false; + dsp->booted = false; + /* Halt the core */ regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, ADSP1_CORE_ENA | ADSP1_START, 0); @@ -2227,7 +2444,7 @@ static int wm_adsp2_ena(struct wm_adsp *dsp) if (val & ADSP2_RAM_RDY) break; - msleep(1); + usleep_range(250, 500); } if (!(val & ADSP2_RAM_RDY)) { @@ -2249,6 +2466,11 @@ static void wm_adsp2_boot_work(struct work_struct *work) mutex_lock(&dsp->pwr_lock); + ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, + ADSP2_MEM_ENA, ADSP2_MEM_ENA); + if (ret != 0) + goto err_mutex; + ret = wm_adsp2_ena(dsp); if (ret != 0) goto err_mutex; @@ -2270,13 +2492,14 @@ static void wm_adsp2_boot_work(struct work_struct *work) if (ret != 0) goto err_ena; - /* Sync set controls */ - ret = wm_coeff_sync_controls(dsp); + dsp->booted = true; + + /* Turn DSP back off until we are ready to run */ + ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, + ADSP2_SYS_ENA, 0); if (ret != 0) goto err_ena; - dsp->running = true; - mutex_unlock(&dsp->pwr_lock); return; @@ -2307,14 +2530,31 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); struct wm_adsp *dsp = &dsps[w->shift]; - - dsp->card = codec->component.card; + struct wm_coeff_ctl *ctl; switch (event) { case SND_SOC_DAPM_PRE_PMU: wm_adsp2_set_dspclk(dsp, freq); queue_work(system_unbound_wq, &dsp->boot_work); break; + case SND_SOC_DAPM_PRE_PMD: + wm_adsp_debugfs_clear(dsp); + + dsp->fw_id = 0; + dsp->fw_id_version = 0; + + dsp->booted = false; + + regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, + ADSP2_MEM_ENA, 0); + + list_for_each_entry(ctl, &dsp->ctl_list, list) + ctl->enabled = 0; + + wm_adsp_free_alg_regions(dsp); + + adsp_dbg(dsp, "Shutdown complete\n"); + break; default: break; } @@ -2329,16 +2569,24 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); struct wm_adsp *dsp = &dsps[w->shift]; - struct wm_coeff_ctl *ctl; int ret; switch (event) { case SND_SOC_DAPM_POST_PMU: flush_work(&dsp->boot_work); - if (!dsp->running) + if (!dsp->booted) return -EIO; + ret = wm_adsp2_ena(dsp); + if (ret != 0) + goto err; + + /* Sync set controls */ + ret = wm_coeff_sync_controls(dsp); + if (ret != 0) + goto err; + ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, ADSP2_CORE_ENA | ADSP2_START, @@ -2346,25 +2594,31 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; + dsp->running = true; + mutex_lock(&dsp->pwr_lock); - if (wm_adsp_fw[dsp->fw].num_caps != 0) + if (wm_adsp_fw[dsp->fw].num_caps != 0) { ret = wm_adsp_buffer_init(dsp); + if (ret < 0) { + mutex_unlock(&dsp->pwr_lock); + goto err; + } + } mutex_unlock(&dsp->pwr_lock); break; case SND_SOC_DAPM_PRE_PMD: + /* Tell the firmware to cleanup */ + wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN); + /* Log firmware state, it can be useful for analysis */ wm_adsp2_show_fw_status(dsp); mutex_lock(&dsp->pwr_lock); - wm_adsp_debugfs_clear(dsp); - - dsp->fw_id = 0; - dsp->fw_id_version = 0; dsp->running = false; regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, @@ -2378,17 +2632,12 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, ADSP2_SYS_ENA, 0); - list_for_each_entry(ctl, &dsp->ctl_list, list) - ctl->enabled = 0; - - wm_adsp_free_alg_regions(dsp); - if (wm_adsp_fw[dsp->fw].num_caps != 0) wm_adsp_buffer_free(dsp); mutex_unlock(&dsp->pwr_lock); - adsp_dbg(dsp, "Shutdown complete\n"); + adsp_dbg(dsp, "Execution stopped\n"); break; default: @@ -2405,6 +2654,8 @@ EXPORT_SYMBOL_GPL(wm_adsp2_event); int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec) { + dsp->codec = codec; + wm_adsp2_init_debugfs(dsp, codec); return snd_soc_add_codec_controls(codec, diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index be3b5bcb7f17..411d062c13f2 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -44,7 +44,7 @@ struct wm_adsp { int type; struct device *dev; struct regmap *regmap; - struct snd_soc_card *card; + struct snd_soc_codec *codec; int base; int sysclk_reg; @@ -61,6 +61,8 @@ struct wm_adsp { int fw; int fw_ver; + + bool booted; bool running; struct list_head ctl_list; @@ -85,9 +87,10 @@ struct wm_adsp { wm_adsp1_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD) #define WM_ADSP2(wname, num, event_fn) \ -{ .id = snd_soc_dapm_dai_link, .name = wname " Preloader", \ +{ .id = snd_soc_dapm_supply, .name = wname " Preloader", \ .reg = SND_SOC_NOPM, .shift = num, .event = event_fn, \ - .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD }, \ + .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD, \ + .subseq = 100, /* Ensure we run after SYSCLK supply widget */ }, \ { .id = snd_soc_dapm_out_drv, .name = wname, \ .reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_event, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD } @@ -107,18 +110,17 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, int wm_adsp2_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); -extern int wm_adsp_compr_open(struct wm_adsp *dsp, - struct snd_compr_stream *stream); -extern int wm_adsp_compr_free(struct snd_compr_stream *stream); -extern int wm_adsp_compr_set_params(struct snd_compr_stream *stream, - struct snd_compr_params *params); -extern int wm_adsp_compr_get_caps(struct snd_compr_stream *stream, - struct snd_compr_caps *caps); -extern int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd); -extern int wm_adsp_compr_handle_irq(struct wm_adsp *dsp); -extern int wm_adsp_compr_pointer(struct snd_compr_stream *stream, - struct snd_compr_tstamp *tstamp); -extern int wm_adsp_compr_copy(struct snd_compr_stream *stream, - char __user *buf, size_t count); +int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream); +int wm_adsp_compr_free(struct snd_compr_stream *stream); +int wm_adsp_compr_set_params(struct snd_compr_stream *stream, + struct snd_compr_params *params); +int wm_adsp_compr_get_caps(struct snd_compr_stream *stream, + struct snd_compr_caps *caps); +int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd); +int wm_adsp_compr_handle_irq(struct wm_adsp *dsp); +int wm_adsp_compr_pointer(struct snd_compr_stream *stream, + struct snd_compr_tstamp *tstamp); +int wm_adsp_compr_copy(struct snd_compr_stream *stream, + char __user *buf, size_t count); #endif diff --git a/sound/soc/codecs/wmfw.h b/sound/soc/codecs/wmfw.h index 7613d60d62ea..ec78b9da020f 100644 --- a/sound/soc/codecs/wmfw.h +++ b/sound/soc/codecs/wmfw.h @@ -26,6 +26,10 @@ #define WMFW_CTL_FLAG_WRITEABLE 0x0002 #define WMFW_CTL_FLAG_READABLE 0x0001 +/* Non-ALSA coefficient types start at 0x1000 */ +#define WMFW_CTL_TYPE_ACKED 0x1000 /* acked control */ +#define WMFW_CTL_TYPE_HOSTEVENT 0x1001 /* event control */ + struct wmfw_header { char magic[4]; __le32 len; diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 05c2d33aa74d..3c5a9804d3f5 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -1218,7 +1218,7 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params, snd_mask_none(&nfmt); - for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) { + for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) { if (snd_mask_test(fmt, i)) { uint sbits = snd_pcm_format_width(i); int ppm; diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index dc97f4349e66..bdf8398cbc81 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -577,7 +577,6 @@ static int dw_configure_dai_by_dt(struct dw_i2s_dev *dev, dev->capability |= DWC_I2S_PLAY; dev->play_dma_data.dt.addr = res->start + I2S_TXDMA; dev->play_dma_data.dt.addr_width = bus_widths[idx]; - dev->play_dma_data.dt.chan_name = "TX"; dev->play_dma_data.dt.fifo_size = fifo_depth * (fifo_width[idx2]) >> 8; dev->play_dma_data.dt.maxburst = 16; @@ -588,7 +587,6 @@ static int dw_configure_dai_by_dt(struct dw_i2s_dev *dev, dev->capability |= DWC_I2S_RECORD; dev->capture_dma_data.dt.addr = res->start + I2S_RXDMA; dev->capture_dma_data.dt.addr_width = bus_widths[idx]; - dev->capture_dma_data.dt.chan_name = "RX"; dev->capture_dma_data.dt.fifo_size = fifo_depth * (fifo_width[idx2] >> 8); dev->capture_dma_data.dt.maxburst = 16; @@ -683,22 +681,19 @@ static int dw_i2s_probe(struct platform_device *pdev) } if (!pdata) { - ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); - if (ret == -EPROBE_DEFER) { - dev_err(&pdev->dev, - "failed to register PCM, deferring probe\n"); - return ret; - } else if (ret) { - dev_err(&pdev->dev, - "Could not register DMA PCM: %d\n" - "falling back to PIO mode\n", ret); + if (irq >= 0) { ret = dw_pcm_register(pdev); - if (ret) { - dev_err(&pdev->dev, - "Could not register PIO PCM: %d\n", + dev->use_pio = true; + } else { + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, + 0); + dev->use_pio = false; + } + + if (ret) { + dev_err(&pdev->dev, "could not register pcm: %d\n", ret); - goto err_clk_disable; - } + goto err_clk_disable; } } diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 19bdcac71775..37f9b6201918 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -40,6 +40,7 @@ config SND_SOC_FSL_SPDIF select REGMAP_MMIO select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n select SND_SOC_IMX_PCM_FIQ if SND_IMX_SOC != n && (MXC_TZIC || MXC_AVIC) + select BITREVERSE help Say Y if you want to add Sony/Philips Digital Interface (SPDIF) support for the Freescale CPUs. diff --git a/sound/soc/fsl/efika-audio-fabric.c b/sound/soc/fsl/efika-audio-fabric.c index b2acd3293ea8..f200d1cfc4bd 100644 --- a/sound/soc/fsl/efika-audio-fabric.c +++ b/sound/soc/fsl/efika-audio-fabric.c @@ -27,7 +27,6 @@ #include "mpc5200_dma.h" #include "mpc5200_psc_ac97.h" -#include "../codecs/stac9766.h" #define DRV_NAME "efika-audio-fabric" diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index dffd549a0e2a..9998aea23597 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -183,7 +183,7 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, return 0; } -static struct snd_soc_ops fsl_asoc_card_ops = { +static const struct snd_soc_ops fsl_asoc_card_ops = { .hw_params = fsl_asoc_card_hw_params, }; diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index c1a0e01cb8e7..1d82f68305c3 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -726,7 +726,7 @@ static const struct regmap_config fsl_asrc_regmap_config = { .readable_reg = fsl_asrc_readable_reg, .volatile_reg = fsl_asrc_volatile_reg, .writeable_reg = fsl_asrc_writeable_reg, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_FLAT, }; /** @@ -879,7 +879,7 @@ static int fsl_asrc_probe(struct platform_device *pdev) } } - if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx35-asrc")) { + if (of_device_is_compatible(np, "fsl,imx35-asrc")) { asrc_priv->channel_bits = 3; clk_map[IN] = input_clk_map_imx35; clk_map[OUT] = output_clk_map_imx35; @@ -892,7 +892,7 @@ static int fsl_asrc_probe(struct platform_device *pdev) ret = fsl_asrc_init(asrc_priv); if (ret) { dev_err(&pdev->dev, "failed to init asrc %d\n", ret); - return -EINVAL; + return ret; } asrc_priv->channel_avail = 10; @@ -901,14 +901,14 @@ static int fsl_asrc_probe(struct platform_device *pdev) &asrc_priv->asrc_rate); if (ret) { dev_err(&pdev->dev, "failed to get output rate\n"); - return -EINVAL; + return ret; } ret = of_property_read_u32(np, "fsl,asrc-width", &asrc_priv->asrc_width); if (ret) { dev_err(&pdev->dev, "failed to get output width\n"); - return -EINVAL; + return ret; } if (asrc_priv->asrc_width != 16 && asrc_priv->asrc_width != 24) { @@ -933,8 +933,6 @@ static int fsl_asrc_probe(struct platform_device *pdev) return ret; } - dev_info(&pdev->dev, "driver registered\n"); - return 0; } diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c index ffc000bc1f15..dc30d780f874 100644 --- a/sound/soc/fsl/fsl_asrc_dma.c +++ b/sound/soc/fsl/fsl_asrc_dma.c @@ -322,7 +322,7 @@ static snd_pcm_uframes_t fsl_asrc_dma_pcm_pointer(struct snd_pcm_substream *subs return bytes_to_frames(substream->runtime, pair->pos); } -static struct snd_pcm_ops fsl_asrc_dma_pcm_ops = { +static const struct snd_pcm_ops fsl_asrc_dma_pcm_ops = { .ioctl = snd_pcm_lib_ioctl, .hw_params = fsl_asrc_dma_hw_params, .hw_free = fsl_asrc_dma_hw_free, diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index 26a90e12ede4..38bfd46f4ad8 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -77,19 +77,19 @@ static irqreturn_t esai_isr(int irq, void *devid) regmap_read(esai_priv->regmap, REG_ESAI_ESR, &esr); if (esr & ESAI_ESR_TINIT_MASK) - dev_dbg(&pdev->dev, "isr: Transmition Initialized\n"); + dev_dbg(&pdev->dev, "isr: Transmission Initialized\n"); if (esr & ESAI_ESR_RFF_MASK) dev_warn(&pdev->dev, "isr: Receiving overrun\n"); if (esr & ESAI_ESR_TFE_MASK) - dev_warn(&pdev->dev, "isr: Transmition underrun\n"); + dev_warn(&pdev->dev, "isr: Transmission underrun\n"); if (esr & ESAI_ESR_TLS_MASK) dev_dbg(&pdev->dev, "isr: Just transmitted the last slot\n"); if (esr & ESAI_ESR_TDE_MASK) - dev_dbg(&pdev->dev, "isr: Transmition data exception\n"); + dev_dbg(&pdev->dev, "isr: Transmission data exception\n"); if (esr & ESAI_ESR_TED_MASK) dev_dbg(&pdev->dev, "isr: Transmitting even slots\n"); @@ -781,7 +781,7 @@ static const struct regmap_config fsl_esai_regmap_config = { .readable_reg = fsl_esai_readable_reg, .volatile_reg = fsl_esai_volatile_reg, .writeable_reg = fsl_esai_writeable_reg, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_FLAT, }; static int fsl_esai_probe(struct platform_device *pdev) diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 2147994ab46f..9fadf7e31c5f 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -801,8 +801,8 @@ static int fsl_sai_probe(struct platform_device *pdev) sai->pdev = pdev; - if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx6sx-sai") || - of_device_is_compatible(pdev->dev.of_node, "fsl,imx6ul-sai")) + if (of_device_is_compatible(np, "fsl,imx6sx-sai") || + of_device_is_compatible(np, "fsl,imx6ul-sai")) sai->sai_on_imx = true; sai->is_lsb_first = of_property_read_bool(np, "lsb-first"); @@ -883,7 +883,7 @@ static int fsl_sai_probe(struct platform_device *pdev) } if (of_find_property(np, "fsl,sai-mclk-direction-output", NULL) && - of_device_is_compatible(pdev->dev.of_node, "fsl,imx6ul-sai")) { + of_device_is_compatible(np, "fsl,imx6ul-sai")) { gpr = syscon_regmap_lookup_by_compatible("fsl,imx6ul-iomuxc-gpr"); if (IS_ERR(gpr)) { dev_err(&pdev->dev, "cannot find iomuxc registers\n"); diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index beec7934a265..1ff467c9598a 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -1103,7 +1103,7 @@ static const struct regmap_config fsl_spdif_regmap_config = { .readable_reg = fsl_spdif_readable_reg, .volatile_reg = fsl_spdif_volatile_reg, .writeable_reg = fsl_spdif_writeable_reg, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_FLAT, }; static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index bedec4a32581..fde08660b63b 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -182,7 +182,7 @@ static const struct regmap_config fsl_ssi_regconfig = { .volatile_reg = fsl_ssi_volatile_reg, .precious_reg = fsl_ssi_precious_reg, .writeable_reg = fsl_ssi_writeable_reg, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_FLAT, }; struct fsl_ssi_soc_data { @@ -224,6 +224,12 @@ struct fsl_ssi_soc_data { * @dbg_stats: Debugging statistics * * @soc: SoC specific data + * + * @fifo_watermark: the FIFO watermark setting. Notifies DMA when + * there are @fifo_watermark or fewer words in TX fifo or + * @fifo_watermark or more empty words in RX fifo. + * @dma_maxburst: max number of words to transfer in one go. So far, + * this is always the same as fifo_watermark. */ struct fsl_ssi_private { struct regmap *regs; @@ -263,6 +269,9 @@ struct fsl_ssi_private { const struct fsl_ssi_soc_data *soc; struct device *dev; + + u32 fifo_watermark; + u32 dma_maxburst; }; /* @@ -1051,21 +1060,7 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev, regmap_write(regs, CCSR_SSI_SRCR, srcr); regmap_write(regs, CCSR_SSI_SCR, scr); - /* - * Set the watermark for transmit FIFI 0 and receive FIFO 0. We don't - * use FIFO 1. We program the transmit water to signal a DMA transfer - * if there are only two (or fewer) elements left in the FIFO. Two - * elements equals one frame (left channel, right channel). This value, - * however, depends on the depth of the transmit buffer. - * - * We set the watermark on the same level as the DMA burstsize. For - * fiq it is probably better to use the biggest possible watermark - * size. - */ - if (ssi_private->use_dma) - wm = ssi_private->fifo_depth - 2; - else - wm = ssi_private->fifo_depth; + wm = ssi_private->fifo_watermark; regmap_write(regs, CCSR_SSI_SFCSR, CCSR_SSI_SFCSR_TFWM0(wm) | CCSR_SSI_SFCSR_RFWM0(wm) | @@ -1373,12 +1368,8 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev, dev_dbg(&pdev->dev, "could not get baud clock: %ld\n", PTR_ERR(ssi_private->baudclk)); - /* - * We have burstsize be "fifo_depth - 2" to match the SSI - * watermark setting in fsl_ssi_startup(). - */ - ssi_private->dma_params_tx.maxburst = ssi_private->fifo_depth - 2; - ssi_private->dma_params_rx.maxburst = ssi_private->fifo_depth - 2; + ssi_private->dma_params_tx.maxburst = ssi_private->dma_maxburst; + ssi_private->dma_params_rx.maxburst = ssi_private->dma_maxburst; ssi_private->dma_params_tx.addr = ssi_private->ssi_phys + CCSR_SSI_STX0; ssi_private->dma_params_rx.addr = ssi_private->ssi_phys + CCSR_SSI_SRX0; @@ -1543,6 +1534,47 @@ static int fsl_ssi_probe(struct platform_device *pdev) /* Older 8610 DTs didn't have the fifo-depth property */ ssi_private->fifo_depth = 8; + /* + * Set the watermark for transmit FIFO 0 and receive FIFO 0. We don't + * use FIFO 1 but set the watermark appropriately nontheless. + * We program the transmit water to signal a DMA transfer + * if there are N elements left in the FIFO. For chips with 15-deep + * FIFOs, set watermark to 8. This allows the SSI to operate at a + * high data rate without channel slipping. Behavior is unchanged + * for the older chips with a fifo depth of only 8. A value of 4 + * might be appropriate for the older chips, but is left at + * fifo_depth-2 until sombody has a chance to test. + * + * We set the watermark on the same level as the DMA burstsize. For + * fiq it is probably better to use the biggest possible watermark + * size. + */ + switch (ssi_private->fifo_depth) { + case 15: + /* + * 2 samples is not enough when running at high data + * rates (like 48kHz @ 16 bits/channel, 16 channels) + * 8 seems to split things evenly and leave enough time + * for the DMA to fill the FIFO before it's over/under + * run. + */ + ssi_private->fifo_watermark = 8; + ssi_private->dma_maxburst = 8; + break; + case 8: + default: + /* + * maintain old behavior for older chips. + * Keeping it the same because I don't have an older + * board to test with. + * I suspect this could be changed to be something to + * leave some more space in the fifo. + */ + ssi_private->fifo_watermark = ssi_private->fifo_depth - 2; + ssi_private->dma_maxburst = ssi_private->fifo_depth - 2; + break; + } + dev_set_drvdata(&pdev->dev, ssi_private); if (ssi_private->soc->imx) { diff --git a/sound/soc/fsl/imx-wm8962.c b/sound/soc/fsl/imx-wm8962.c index 201a70d1027a..1b60958e2080 100644 --- a/sound/soc/fsl/imx-wm8962.c +++ b/sound/soc/fsl/imx-wm8962.c @@ -61,7 +61,7 @@ static int imx_hifi_hw_params(struct snd_pcm_substream *substream, return 0; } -static struct snd_soc_ops imx_hifi_ops = { +static const struct snd_soc_ops imx_hifi_ops = { .hw_params = imx_hifi_hw_params, }; diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig index c01c5dd68601..d023959b8cd6 100644 --- a/sound/soc/generic/Kconfig +++ b/sound/soc/generic/Kconfig @@ -6,3 +6,11 @@ config SND_SIMPLE_CARD select SND_SIMPLE_CARD_UTILS help This option enables generic simple sound card support + +config SND_SIMPLE_SCU_CARD + tristate "ASoC Simple SCU sound card support" + depends on OF + select SND_SIMPLE_CARD_UTILS + help + This option enables generic simple SCU sound card support. + It supports DPCM of multi CPU single Codec system. diff --git a/sound/soc/generic/Makefile b/sound/soc/generic/Makefile index 2d53c8d70705..ee750f3023ba 100644 --- a/sound/soc/generic/Makefile +++ b/sound/soc/generic/Makefile @@ -1,5 +1,7 @@ snd-soc-simple-card-utils-objs := simple-card-utils.o snd-soc-simple-card-objs := simple-card.o +snd-soc-simple-scu-card-objs := simple-scu-card.o obj-$(CONFIG_SND_SIMPLE_CARD_UTILS) += snd-soc-simple-card-utils.o obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o +obj-$(CONFIG_SND_SIMPLE_SCU_CARD) += snd-soc-simple-scu-card.o diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 9599de69a880..cf026252cd4a 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -1,5 +1,5 @@ /* - * simple-card-core.c + * simple-card-utils.c * * Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> * @@ -7,6 +7,7 @@ * 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/module.h> #include <linux/of.h> #include <sound/simple_card_utils.h> @@ -97,6 +98,143 @@ int asoc_simple_card_parse_card_name(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name); +int asoc_simple_card_parse_clk(struct device_node *node, + struct device_node *dai_of_node, + struct asoc_simple_dai *simple_dai) +{ + struct clk *clk; + u32 val; + + /* + * Parse dai->sysclk come from "clocks = <&xxx>" + * (if system has common clock) + * or "system-clock-frequency = <xxx>" + * or device's module clock. + */ + clk = of_clk_get(node, 0); + if (!IS_ERR(clk)) { + simple_dai->sysclk = clk_get_rate(clk); + simple_dai->clk = clk; + } else if (!of_property_read_u32(node, "system-clock-frequency", &val)) { + simple_dai->sysclk = val; + } else { + clk = of_clk_get(dai_of_node, 0); + if (!IS_ERR(clk)) + simple_dai->sysclk = clk_get_rate(clk); + } + + return 0; +} +EXPORT_SYMBOL_GPL(asoc_simple_card_parse_clk); + +int asoc_simple_card_parse_dai(struct device_node *node, + struct device_node **dai_of_node, + const char **dai_name, + const char *list_name, + const char *cells_name, + int *is_single_link) +{ + struct of_phandle_args args; + int ret; + + if (!node) + return 0; + + /* + * Get node via "sound-dai = <&phandle port>" + * it will be used as xxx_of_node on soc_bind_dai_link() + */ + ret = of_parse_phandle_with_args(node, list_name, cells_name, 0, &args); + if (ret) + return ret; + + /* Get dai->name */ + if (dai_name) { + ret = snd_soc_of_get_dai_name(node, dai_name); + if (ret < 0) + return ret; + } + + *dai_of_node = args.np; + + if (is_single_link) + *is_single_link = !args.args_count; + + return 0; +} +EXPORT_SYMBOL_GPL(asoc_simple_card_parse_dai); + +int asoc_simple_card_init_dai(struct snd_soc_dai *dai, + struct asoc_simple_dai *simple_dai) +{ + int ret; + + if (simple_dai->sysclk) { + ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk, 0); + if (ret && ret != -ENOTSUPP) { + dev_err(dai->dev, "simple-card: set_sysclk error\n"); + return ret; + } + } + + if (simple_dai->slots) { + ret = snd_soc_dai_set_tdm_slot(dai, + simple_dai->tx_slot_mask, + simple_dai->rx_slot_mask, + simple_dai->slots, + simple_dai->slot_width); + if (ret && ret != -ENOTSUPP) { + dev_err(dai->dev, "simple-card: set_tdm_slot error\n"); + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(asoc_simple_card_init_dai); + +int asoc_simple_card_canonicalize_dailink(struct snd_soc_dai_link *dai_link) +{ + /* Assumes platform == cpu */ + if (!dai_link->platform_of_node) + dai_link->platform_of_node = dai_link->cpu_of_node; + + return 0; +} +EXPORT_SYMBOL_GPL(asoc_simple_card_canonicalize_dailink); + +void asoc_simple_card_canonicalize_cpu(struct snd_soc_dai_link *dai_link, + int is_single_links) +{ + /* + * 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 (is_single_links) + dai_link->cpu_dai_name = NULL; +} +EXPORT_SYMBOL_GPL(asoc_simple_card_canonicalize_cpu); + +int asoc_simple_card_clean_reference(struct snd_soc_card *card) +{ + struct snd_soc_dai_link *dai_link; + int num_links; + + for (num_links = 0, dai_link = card->dai_link; + num_links < card->num_links; + num_links++, dai_link++) { + of_node_put(dai_link->cpu_of_node); + of_node_put(dai_link->codec_of_node); + } + return 0; +} +EXPORT_SYMBOL_GPL(asoc_simple_card_clean_reference); + /* Module information */ MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); MODULE_DESCRIPTION("ALSA SoC Simple Card Utils"); diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 43295f024982..a385ff6bfa4b 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -37,13 +37,15 @@ struct simple_card_data { unsigned int mclk_fs; struct asoc_simple_jack hp_jack; struct asoc_simple_jack mic_jack; - struct snd_soc_dai_link dai_link[]; /* dynamically allocated */ + struct snd_soc_dai_link *dai_link; }; #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) +#define simple_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i)) +#define simple_priv_to_props(priv, i) ((priv)->dai_props + (i)) +#define DAI "sound-dai" +#define CELL "#sound-dai-cells" #define PREFIX "simple-audio-card," #define asoc_simple_card_init_hp(card, sjack, prefix)\ @@ -112,13 +114,13 @@ static int asoc_simple_card_startup(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); struct simple_dai_props *dai_props = - &priv->dai_props[rtd->num]; + simple_priv_to_props(priv, rtd->num); int ret; ret = clk_prepare_enable(dai_props->cpu_dai.clk); if (ret) return ret; - + ret = clk_prepare_enable(dai_props->codec_dai.clk); if (ret) clk_disable_unprepare(dai_props->cpu_dai.clk); @@ -131,7 +133,7 @@ static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); struct simple_dai_props *dai_props = - &priv->dai_props[rtd->num]; + simple_priv_to_props(priv, rtd->num); clk_disable_unprepare(dai_props->cpu_dai.clk); @@ -145,7 +147,8 @@ static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct simple_dai_props *dai_props = &priv->dai_props[rtd->num]; + struct simple_dai_props *dai_props = + simple_priv_to_props(priv, rtd->num); unsigned int mclk, mclk_fs = 0; int ret = 0; @@ -171,57 +174,26 @@ err: return ret; } -static struct snd_soc_ops asoc_simple_card_ops = { +static const struct snd_soc_ops asoc_simple_card_ops = { .startup = asoc_simple_card_startup, .shutdown = asoc_simple_card_shutdown, .hw_params = asoc_simple_card_hw_params, }; -static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai, - struct asoc_simple_dai *set) -{ - int ret; - - if (set->sysclk) { - ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0); - if (ret && ret != -ENOTSUPP) { - dev_err(dai->dev, "simple-card: set_sysclk error\n"); - goto err; - } - } - - if (set->slots) { - ret = snd_soc_dai_set_tdm_slot(dai, - set->tx_slot_mask, - set->rx_slot_mask, - set->slots, - set->slot_width); - if (ret && ret != -ENOTSUPP) { - dev_err(dai->dev, "simple-card: set_tdm_slot error\n"); - goto err; - } - } - - ret = 0; - -err: - return ret; -} - static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) { struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_dai *codec = rtd->codec_dai; struct snd_soc_dai *cpu = rtd->cpu_dai; - struct simple_dai_props *dai_props; + struct simple_dai_props *dai_props = + simple_priv_to_props(priv, rtd->num); int ret; - dai_props = &priv->dai_props[rtd->num]; - ret = __asoc_simple_card_dai_init(codec, &dai_props->codec_dai); + ret = asoc_simple_card_init_dai(codec, &dai_props->codec_dai); if (ret < 0) return ret; - ret = __asoc_simple_card_dai_init(cpu, &dai_props->cpu_dai); + ret = asoc_simple_card_init_dai(cpu, &dai_props->cpu_dai); if (ret < 0) return ret; @@ -236,78 +208,6 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) return 0; } -static int -asoc_simple_card_sub_parse_of(struct device_node *np, - struct asoc_simple_dai *dai, - struct device_node **p_node, - const char **name, - int *args_count) -{ - struct of_phandle_args args; - struct clk *clk; - u32 val; - int ret; - - if (!np) - return 0; - - /* - * Get node via "sound-dai = <&phandle port>" - * it will be used as xxx_of_node on soc_bind_dai_link() - */ - 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 */ - if (name) { - ret = snd_soc_of_get_dai_name(np, name); - if (ret < 0) - return ret; - } - - if (!dai) - return 0; - - /* Parse TDM slot */ - ret = snd_soc_of_parse_tdm_slot(np, &dai->tx_slot_mask, - &dai->rx_slot_mask, - &dai->slots, &dai->slot_width); - if (ret) - return ret; - - /* - * Parse dai->sysclk come from "clocks = <&xxx>" - * (if system has common clock) - * or "system-clock-frequency = <xxx>" - * or device's module clock. - */ - if (of_property_read_bool(np, "clocks")) { - clk = of_clk_get(np, 0); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - return ret; - } - - dai->sysclk = clk_get_rate(clk); - dai->clk = clk; - } else if (!of_property_read_u32(np, "system-clock-frequency", &val)) { - dai->sysclk = val; - } else { - clk = of_clk_get(args.np, 0); - if (!IS_ERR(clk)) - dai->sysclk = clk_get_rate(clk); - } - - return 0; -} - static int asoc_simple_card_dai_link_of(struct device_node *node, struct simple_card_data *priv, int idx, @@ -316,13 +216,14 @@ static int asoc_simple_card_dai_link_of(struct device_node *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 asoc_simple_dai *cpu_dai = &dai_props->cpu_dai; + struct asoc_simple_dai *codec_dai = &dai_props->codec_dai; struct device_node *cpu = NULL; struct device_node *plat = NULL; struct device_node *codec = NULL; char prop[128]; char *prefix = ""; - int ret, cpu_args; - u32 val; + int ret, single_cpu; /* For single DAI link & old style of DT node */ if (is_top_level_node) @@ -348,36 +249,46 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, if (ret < 0) goto dai_link_of_err; - if (!of_property_read_u32(node, "mclk-fs", &val)) - dai_props->mclk_fs = val; + of_property_read_u32(node, "mclk-fs", &dai_props->mclk_fs); - ret = asoc_simple_card_sub_parse_of(cpu, &dai_props->cpu_dai, - &dai_link->cpu_of_node, - &dai_link->cpu_dai_name, - &cpu_args); + ret = asoc_simple_card_parse_cpu(cpu, dai_link, + DAI, CELL, &single_cpu); if (ret < 0) goto dai_link_of_err; - ret = asoc_simple_card_sub_parse_of(codec, &dai_props->codec_dai, - &dai_link->codec_of_node, - &dai_link->codec_dai_name, NULL); + ret = asoc_simple_card_parse_codec(codec, dai_link, DAI, CELL); if (ret < 0) goto dai_link_of_err; - ret = asoc_simple_card_sub_parse_of(plat, NULL, - &dai_link->platform_of_node, - NULL, NULL); + ret = asoc_simple_card_parse_platform(plat, dai_link, DAI, CELL); if (ret < 0) goto dai_link_of_err; - if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) { - ret = -EINVAL; + ret = snd_soc_of_parse_tdm_slot(cpu, &cpu_dai->tx_slot_mask, + &cpu_dai->rx_slot_mask, + &cpu_dai->slots, + &cpu_dai->slot_width); + if (ret < 0) + goto dai_link_of_err; + + ret = snd_soc_of_parse_tdm_slot(codec, &codec_dai->tx_slot_mask, + &codec_dai->rx_slot_mask, + &codec_dai->slots, + &codec_dai->slot_width); + if (ret < 0) + goto dai_link_of_err; + + ret = asoc_simple_card_parse_clk_cpu(cpu, dai_link, cpu_dai); + if (ret < 0) goto dai_link_of_err; - } - /* Assumes platform == cpu */ - if (!dai_link->platform_of_node) - dai_link->platform_of_node = dai_link->cpu_of_node; + ret = asoc_simple_card_parse_clk_codec(codec, dai_link, codec_dai); + if (ret < 0) + goto dai_link_of_err; + + ret = asoc_simple_card_canonicalize_dailink(dai_link); + if (ret < 0) + goto dai_link_of_err; ret = asoc_simple_card_set_dailink_name(dev, dai_link, "%s-%s", @@ -398,17 +309,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, dai_link->codec_dai_name, 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; + asoc_simple_card_canonicalize_cpu(dai_link, single_cpu); dai_link_of_err: of_node_put(cpu); @@ -417,22 +318,54 @@ dai_link_of_err: return ret; } +static int asoc_simple_card_parse_aux_devs(struct device_node *node, + struct simple_card_data *priv) +{ + struct device *dev = simple_priv_to_dev(priv); + struct device_node *aux_node; + int i, n, len; + + if (!of_find_property(node, PREFIX "aux-devs", &len)) + return 0; /* Ok to have no aux-devs */ + + n = len / sizeof(__be32); + if (n <= 0) + return -EINVAL; + + priv->snd_card.aux_dev = devm_kzalloc(dev, + n * sizeof(*priv->snd_card.aux_dev), GFP_KERNEL); + if (!priv->snd_card.aux_dev) + return -ENOMEM; + + for (i = 0; i < n; i++) { + aux_node = of_parse_phandle(node, PREFIX "aux-devs", i); + if (!aux_node) + return -EINVAL; + priv->snd_card.aux_dev[i].codec_of_node = aux_node; + } + + priv->snd_card.num_aux_devs = n; + return 0; +} + static int asoc_simple_card_parse_of(struct device_node *node, struct simple_card_data *priv) { struct device *dev = simple_priv_to_dev(priv); - u32 val; + struct device_node *dai_link; int ret; if (!node) return -EINVAL; + dai_link = of_get_child_by_name(node, PREFIX "dai-link"); + /* The off-codec widgets */ if (of_property_read_bool(node, PREFIX "widgets")) { ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card, PREFIX "widgets"); if (ret) - return ret; + goto card_parse_end; } /* DAPM routes */ @@ -440,16 +373,14 @@ static int asoc_simple_card_parse_of(struct device_node *node, ret = snd_soc_of_parse_audio_routing(&priv->snd_card, PREFIX "routing"); if (ret) - return ret; + goto card_parse_end; } /* Factor to mclk, used in hw_params() */ - ret = of_property_read_u32(node, PREFIX "mclk-fs", &val); - if (ret == 0) - priv->mclk_fs = val; + of_property_read_u32(node, PREFIX "mclk-fs", &priv->mclk_fs); /* Single/Muti DAI link(s) & New style of DT node */ - if (of_get_child_by_name(node, PREFIX "dai-link")) { + if (dai_link) { struct device_node *np = NULL; int i = 0; @@ -459,7 +390,7 @@ static int asoc_simple_card_parse_of(struct device_node *node, i, false); if (ret < 0) { of_node_put(np); - return ret; + goto card_parse_end; } i++; } @@ -467,66 +398,55 @@ static int asoc_simple_card_parse_of(struct device_node *node, /* 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; + goto card_parse_end; } ret = asoc_simple_card_parse_card_name(&priv->snd_card, PREFIX); - if (ret) - return ret; + if (ret < 0) + goto card_parse_end; - return 0; -} + ret = asoc_simple_card_parse_aux_devs(node, priv); -/* Decrease the reference count of the device nodes */ -static int asoc_simple_card_unref(struct snd_soc_card *card) -{ - struct snd_soc_dai_link *dai_link; - int num_links; +card_parse_end: + of_node_put(dai_link); - for (num_links = 0, dai_link = card->dai_link; - num_links < card->num_links; - num_links++, dai_link++) { - of_node_put(dai_link->cpu_of_node); - of_node_put(dai_link->codec_of_node); - } - return 0; + return ret; } static int asoc_simple_card_probe(struct platform_device *pdev) { struct simple_card_data *priv; struct snd_soc_dai_link *dai_link; + struct simple_dai_props *dai_props; struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; - int num_links, ret; + int num, ret; /* Get the number of DAI links */ if (np && of_get_child_by_name(np, PREFIX "dai-link")) - num_links = of_get_child_count(np); + num = of_get_child_count(np); else - num_links = 1; + num = 1; /* Allocate the private data and the DAI link array */ - priv = devm_kzalloc(dev, - sizeof(*priv) + sizeof(*dai_link) * num_links, - GFP_KERNEL); + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - /* 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->dai_props = devm_kzalloc(dev, - sizeof(*priv->dai_props) * num_links, - GFP_KERNEL); - if (!priv->dai_props) + dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL); + dai_link = devm_kzalloc(dev, sizeof(*dai_link) * num, GFP_KERNEL); + if (!dai_props || !dai_link) return -ENOMEM; + priv->dai_props = dai_props; + priv->dai_link = dai_link; + + /* Init snd_soc_card */ + priv->snd_card.owner = THIS_MODULE; + priv->snd_card.dev = dev; + priv->snd_card.dai_link = priv->dai_link; + priv->snd_card.num_links = num; + if (np && of_device_is_available(np)) { ret = asoc_simple_card_parse_of(np, priv); @@ -567,7 +487,6 @@ static int asoc_simple_card_probe(struct platform_device *pdev) sizeof(priv->dai_props->cpu_dai)); memcpy(&priv->dai_props->codec_dai, &cinfo->codec_dai, sizeof(priv->dai_props->codec_dai)); - } snd_soc_card_set_drvdata(&priv->snd_card, priv); @@ -575,9 +494,9 @@ static int asoc_simple_card_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card); if (ret >= 0) return ret; - err: - asoc_simple_card_unref(&priv->snd_card); + asoc_simple_card_clean_reference(&priv->snd_card); + return ret; } @@ -589,7 +508,7 @@ static int asoc_simple_card_remove(struct platform_device *pdev) asoc_simple_card_remove_jack(&priv->hp_jack); asoc_simple_card_remove_jack(&priv->mic_jack); - return asoc_simple_card_unref(card); + return asoc_simple_card_clean_reference(card); } static const struct of_device_id asoc_simple_of_match[] = { @@ -611,6 +530,6 @@ static struct platform_driver asoc_simple_card = { module_platform_driver(asoc_simple_card); MODULE_ALIAS("platform:asoc-simple-card"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("ASoC Simple Sound Card"); MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); diff --git a/sound/soc/generic/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c new file mode 100644 index 000000000000..bb86ee042490 --- /dev/null +++ b/sound/soc/generic/simple-scu-card.c @@ -0,0 +1,332 @@ +/* + * ASoC simple SCU sound card support + * + * Copyright (C) 2015 Renesas Solutions Corp. + * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * based on ${LINUX}/sound/soc/generic/simple-card.c + * + * 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/device.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/string.h> +#include <sound/jack.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> +#include <sound/simple_card_utils.h> + +struct simple_card_data { + struct snd_soc_card snd_card; + struct snd_soc_codec_conf codec_conf; + struct asoc_simple_dai *dai_props; + struct snd_soc_dai_link *dai_link; + u32 convert_rate; + u32 convert_channels; +}; + +#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)) + +#define DAI "sound-dai" +#define CELL "#sound-dai-cells" +#define PREFIX "simple-audio-card," + +static int asoc_simple_card_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct asoc_simple_dai *dai_props = + simple_priv_to_props(priv, rtd->num); + + return clk_prepare_enable(dai_props->clk); +} + +static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct asoc_simple_dai *dai_props = + simple_priv_to_props(priv, rtd->num); + + clk_disable_unprepare(dai_props->clk); +} + +static const struct snd_soc_ops asoc_simple_card_ops = { + .startup = asoc_simple_card_startup, + .shutdown = asoc_simple_card_shutdown, +}; + +static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai; + struct snd_soc_dai_link *dai_link; + struct asoc_simple_dai *dai_props; + int num = rtd->num; + + dai_link = simple_priv_to_link(priv, num); + dai_props = simple_priv_to_props(priv, num); + dai = dai_link->dynamic ? + rtd->cpu_dai : + rtd->codec_dai; + + return asoc_simple_card_init_dai(dai, dai_props); +} + +static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + if (priv->convert_rate) + rate->min = + rate->max = priv->convert_rate; + + if (priv->convert_channels) + channels->min = + channels->max = priv->convert_channels; + + return 0; +} + +static int asoc_simple_card_dai_link_of(struct device_node *np, + struct simple_card_data *priv, + unsigned int daifmt, + int idx, bool is_fe) +{ + struct device *dev = simple_priv_to_dev(priv); + struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx); + struct asoc_simple_dai *dai_props = simple_priv_to_props(priv, idx); + int ret; + + if (is_fe) { + int is_single_links = 0; + + /* BE is dummy */ + dai_link->codec_of_node = NULL; + dai_link->codec_dai_name = "snd-soc-dummy-dai"; + dai_link->codec_name = "snd-soc-dummy"; + + /* FE settings */ + dai_link->dynamic = 1; + dai_link->dpcm_merged_format = 1; + + ret = asoc_simple_card_parse_cpu(np, dai_link, DAI, CELL, + &is_single_links); + if (ret) + return ret; + + ret = asoc_simple_card_parse_clk_cpu(np, dai_link, dai_props); + if (ret < 0) + return ret; + + ret = asoc_simple_card_set_dailink_name(dev, dai_link, + "fe.%s", + dai_link->cpu_dai_name); + if (ret < 0) + return ret; + + asoc_simple_card_canonicalize_cpu(dai_link, is_single_links); + } else { + /* FE is dummy */ + dai_link->cpu_of_node = NULL; + dai_link->cpu_dai_name = "snd-soc-dummy-dai"; + dai_link->cpu_name = "snd-soc-dummy"; + + /* BE settings */ + dai_link->no_pcm = 1; + dai_link->be_hw_params_fixup = asoc_simple_card_be_hw_params_fixup; + + ret = asoc_simple_card_parse_codec(np, dai_link, DAI, CELL); + if (ret < 0) + return ret; + + ret = asoc_simple_card_parse_clk_codec(np, dai_link, dai_props); + if (ret < 0) + return ret; + + ret = asoc_simple_card_set_dailink_name(dev, dai_link, + "be.%s", + dai_link->codec_dai_name); + if (ret < 0) + return ret; + + snd_soc_of_parse_audio_prefix(&priv->snd_card, + &priv->codec_conf, + dai_link->codec_of_node, + PREFIX "prefix"); + } + + ret = snd_soc_of_parse_tdm_slot(np, + &dai_props->tx_slot_mask, + &dai_props->rx_slot_mask, + &dai_props->slots, + &dai_props->slot_width); + if (ret) + return ret; + + ret = asoc_simple_card_canonicalize_dailink(dai_link); + if (ret < 0) + return ret; + + dai_link->dai_fmt = daifmt; + dai_link->dpcm_playback = 1; + dai_link->dpcm_capture = 1; + dai_link->ops = &asoc_simple_card_ops; + dai_link->init = asoc_simple_card_dai_init; + + dev_dbg(dev, "\t%s / %04x / %d\n", + dai_link->name, + dai_link->dai_fmt, + dai_props->sysclk); + + return 0; +} + +static int asoc_simple_card_parse_of(struct device_node *node, + struct simple_card_data *priv) + +{ + struct device *dev = simple_priv_to_dev(priv); + struct device_node *np; + unsigned int daifmt = 0; + bool is_fe; + int ret, i; + + if (!node) + return -EINVAL; + + ret = snd_soc_of_parse_audio_routing(&priv->snd_card, PREFIX "routing"); + if (ret < 0) + return ret; + + /* sampling rate convert */ + of_property_read_u32(node, PREFIX "convert-rate", &priv->convert_rate); + + /* channels transfer */ + of_property_read_u32(node, PREFIX "convert-channels", &priv->convert_channels); + + /* find 1st codec */ + np = of_get_child_by_name(node, PREFIX "codec"); + if (!np) + return -ENODEV; + + ret = asoc_simple_card_parse_daifmt(dev, node, np, PREFIX, &daifmt); + if (ret < 0) + return ret; + + i = 0; + for_each_child_of_node(node, np) { + is_fe = false; + if (strcmp(np->name, PREFIX "cpu") == 0) + is_fe = true; + + ret = asoc_simple_card_dai_link_of(np, priv, daifmt, i, is_fe); + if (ret < 0) + return ret; + i++; + } + + ret = asoc_simple_card_parse_card_name(&priv->snd_card, PREFIX); + if (ret < 0) + return ret; + + dev_dbg(dev, "New card: %s\n", + priv->snd_card.name ? priv->snd_card.name : ""); + dev_dbg(dev, "convert_rate %d\n", priv->convert_rate); + dev_dbg(dev, "convert_channels %d\n", priv->convert_channels); + + return 0; +} + +static int asoc_simple_card_probe(struct platform_device *pdev) +{ + struct simple_card_data *priv; + struct snd_soc_dai_link *dai_link; + struct asoc_simple_dai *dai_props; + struct device *dev = &pdev->dev; + struct device_node *np = pdev->dev.of_node; + int num, ret; + + /* Allocate the private data */ + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + num = of_get_child_count(np); + + dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL); + dai_link = devm_kzalloc(dev, sizeof(*dai_link) * num, GFP_KERNEL); + if (!dai_props || !dai_link) + return -ENOMEM; + + priv->dai_props = dai_props; + priv->dai_link = dai_link; + + /* Init snd_soc_card */ + priv->snd_card.owner = THIS_MODULE; + priv->snd_card.dev = dev; + priv->snd_card.dai_link = priv->dai_link; + priv->snd_card.num_links = num; + priv->snd_card.codec_conf = &priv->codec_conf; + priv->snd_card.num_configs = 1; + + 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; + } + + snd_soc_card_set_drvdata(&priv->snd_card, priv); + + ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card); + if (ret >= 0) + return ret; +err: + asoc_simple_card_clean_reference(&priv->snd_card); + + return ret; +} + +static int asoc_simple_card_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + return asoc_simple_card_clean_reference(card); +} + +static const struct of_device_id asoc_simple_of_match[] = { + { .compatible = "renesas,rsrc-card", }, + { .compatible = "simple-scu-audio-card", }, + {}, +}; +MODULE_DEVICE_TABLE(of, asoc_simple_of_match); + +static struct platform_driver asoc_simple_card = { + .driver = { + .name = "simple-scu-audio-card", + .of_match_table = asoc_simple_of_match, + }, + .probe = asoc_simple_card_probe, + .remove = asoc_simple_card_remove, +}; + +module_platform_driver(asoc_simple_card); + +MODULE_ALIAS("platform:asoc-simple-scu-card"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ASoC Simple SCU Sound Card"); +MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); diff --git a/sound/soc/img/pistachio-internal-dac.c b/sound/soc/img/pistachio-internal-dac.c index 162a0fd68c7b..53e11c6d4e22 100644 --- a/sound/soc/img/pistachio-internal-dac.c +++ b/sound/soc/img/pistachio-internal-dac.c @@ -134,12 +134,14 @@ static int pistachio_internal_dac_codec_probe(struct snd_soc_codec *codec) static const struct snd_soc_codec_driver pistachio_internal_dac_driver = { .probe = pistachio_internal_dac_codec_probe, .idle_bias_off = true, - .controls = pistachio_internal_dac_snd_controls, - .num_controls = ARRAY_SIZE(pistachio_internal_dac_snd_controls), - .dapm_widgets = pistachio_internal_dac_widgets, - .num_dapm_widgets = ARRAY_SIZE(pistachio_internal_dac_widgets), - .dapm_routes = pistachio_internal_dac_routes, - .num_dapm_routes = ARRAY_SIZE(pistachio_internal_dac_routes), + .component_driver = { + .controls = pistachio_internal_dac_snd_controls, + .num_controls = ARRAY_SIZE(pistachio_internal_dac_snd_controls), + .dapm_widgets = pistachio_internal_dac_widgets, + .num_dapm_widgets = ARRAY_SIZE(pistachio_internal_dac_widgets), + .dapm_routes = pistachio_internal_dac_routes, + .num_dapm_routes = ARRAY_SIZE(pistachio_internal_dac_routes), + }, }; static int pistachio_internal_dac_probe(struct platform_device *pdev) diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index a20c3dfbcb5d..fd5d1e091038 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -25,6 +25,7 @@ config SND_SST_IPC_ACPI tristate select SND_SST_IPC select SND_SOC_INTEL_SST + select IOSF_MBI config SND_SOC_INTEL_SST tristate @@ -46,6 +47,7 @@ config SND_SOC_INTEL_SST_MATCH config SND_SOC_INTEL_HASWELL tristate + select SND_SOC_INTEL_SST_FIRMWARE config SND_SOC_INTEL_BAYTRAIL tristate @@ -55,7 +57,6 @@ config SND_SOC_INTEL_HASWELL_MACH depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM depends on DW_DMAC_CORE select SND_SOC_INTEL_SST - select SND_SOC_INTEL_SST_FIRMWARE select SND_SOC_INTEL_HASWELL select SND_SOC_RT5640 help @@ -120,13 +121,23 @@ config SND_SOC_INTEL_BYT_MAX98090_MACH This adds audio driver for Intel Baytrail platform based boards with the MAX98090 audio codec. +config SND_SOC_INTEL_BDW_RT5677_MACH + tristate "ASoC Audio driver for Intel Broadwell with RT5677 codec" + depends on X86_INTEL_LPSS && GPIOLIB && I2C && DW_DMAC + depends on DW_DMAC_CORE=y + select SND_SOC_INTEL_SST + select SND_SOC_INTEL_HASWELL + select SND_SOC_RT5677 + help + This adds support for Intel Broadwell platform based boards with + the RT5677 audio codec. + config SND_SOC_INTEL_BROADWELL_MACH tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint" depends on X86_INTEL_LPSS && I2C && DW_DMAC && \ I2C_DESIGNWARE_PLATFORM depends on DW_DMAC_CORE select SND_SOC_INTEL_SST - select SND_SOC_INTEL_SST_FIRMWARE select SND_SOC_INTEL_HASWELL select SND_SOC_RT286 help diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c index 98720a93de8a..c7b3cbf92faf 100644 --- a/sound/soc/intel/atom/sst-atom-controls.c +++ b/sound/soc/intel/atom/sst-atom-controls.c @@ -1,4 +1,4 @@ -/* + /* * sst-atom-controls.c - Intel MID Platform driver DPCM ALSA controls for Mrfld * * Copyright (C) 2013-14 Intel Corp @@ -534,6 +534,7 @@ static const DECLARE_TLV_DB_SCALE(sst_gain_tlv_common, SST_GAIN_MIN_VALUE * 10, /* Look up table to convert MIXER SW bit regs to SWM inputs */ static const uint swm_mixer_input_ids[SST_SWM_INPUT_COUNT] = { + [SST_IP_MODEM] = SST_SWM_IN_MODEM, [SST_IP_CODEC0] = SST_SWM_IN_CODEC0, [SST_IP_CODEC1] = SST_SWM_IN_CODEC1, [SST_IP_LOOP0] = SST_SWM_IN_SPROT_LOOP, @@ -674,6 +675,7 @@ static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w, /* SBA mixers - 16 inputs */ #define SST_SBA_DECLARE_MIX_CONTROLS(kctl_name) \ static const struct snd_kcontrol_new kctl_name[] = { \ + SOC_DAPM_SINGLE("modem_in Switch", SND_SOC_NOPM, SST_IP_MODEM, 1, 0), \ SOC_DAPM_SINGLE("codec_in0 Switch", SND_SOC_NOPM, SST_IP_CODEC0, 1, 0), \ SOC_DAPM_SINGLE("codec_in1 Switch", SND_SOC_NOPM, SST_IP_CODEC1, 1, 0), \ SOC_DAPM_SINGLE("sprot_loop_in Switch", SND_SOC_NOPM, SST_IP_LOOP0, 1, 0), \ @@ -684,6 +686,7 @@ static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w, } #define SST_SBA_MIXER_GRAPH_MAP(mix_name) \ + { mix_name, "modem_in Switch", "modem_in" }, \ { mix_name, "codec_in0 Switch", "codec_in0" }, \ { mix_name, "codec_in1 Switch", "codec_in1" }, \ { mix_name, "sprot_loop_in Switch", "sprot_loop_in" }, \ @@ -713,6 +716,7 @@ SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l2_controls); SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_voip_controls); SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec0_controls); SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec1_controls); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_modem_controls); /* * sst_handle_vb_timer - Start/Stop the DSP scheduler @@ -931,17 +935,26 @@ void sst_fill_ssp_defaults(struct snd_soc_dai *dai) int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable) { struct sst_data *drv = snd_soc_dai_get_drvdata(dai); - const struct sst_ssp_config *config; + int ssp_id; - dev_info(dai->dev, "Enter: enable=%d port_name=%s\n", enable, id); + dev_dbg(dai->dev, "Enter: enable=%d port_name=%s\n", enable, id); + + if (strcmp(id, "ssp0-port") == 0) + ssp_id = SSP_MODEM; + else if (strcmp(id, "ssp2-port") == 0) + ssp_id = SSP_CODEC; + else { + dev_dbg(dai->dev, "port %s is not supported\n", id); + return -1; + } SST_FILL_DEFAULT_DESTINATION(drv->ssp_cmd.header.dst); drv->ssp_cmd.header.command_id = SBA_HW_SET_SSP; drv->ssp_cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp) - sizeof(struct sst_dsp_header); - config = &sst_ssp_configs; - dev_dbg(dai->dev, "ssp_id: %u\n", config->ssp_id); + drv->ssp_cmd.selection = ssp_id; + dev_dbg(dai->dev, "ssp_id: %u\n", ssp_id); if (enable) drv->ssp_cmd.switch_state = SST_SWITCH_ON; @@ -1047,8 +1060,10 @@ static int sst_set_media_loop(struct snd_soc_dapm_widget *w, } static const struct snd_soc_dapm_widget sst_dapm_widgets[] = { + SST_AIF_IN("modem_in", sst_set_be_modules), SST_AIF_IN("codec_in0", sst_set_be_modules), SST_AIF_IN("codec_in1", sst_set_be_modules), + SST_AIF_OUT("modem_out", sst_set_be_modules), SST_AIF_OUT("codec_out0", sst_set_be_modules), SST_AIF_OUT("codec_out1", sst_set_be_modules), @@ -1103,6 +1118,9 @@ static const struct snd_soc_dapm_widget sst_dapm_widgets[] = { sst_mix_codec0_controls, sst_swm_mixer_event), SST_SWM_MIXER("codec_out1 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC1, sst_mix_codec1_controls, sst_swm_mixer_event), + SST_SWM_MIXER("modem_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MODEM, + sst_mix_modem_controls, sst_swm_mixer_event), + }; static const struct snd_soc_dapm_route intercon[] = { @@ -1148,6 +1166,9 @@ static const struct snd_soc_dapm_route intercon[] = { SST_SBA_MIXER_GRAPH_MAP("codec_out0 mix 0"), {"codec_out1", NULL, "codec_out1 mix 0"}, SST_SBA_MIXER_GRAPH_MAP("codec_out1 mix 0"), + {"modem_out", NULL, "modem_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("modem_out mix 0"), + }; static const char * const slot_names[] = { @@ -1217,6 +1238,9 @@ static const struct snd_kcontrol_new sst_gain_controls[] = { SST_GAIN("media_loop2_out", SST_PATH_INDEX_MEDIA_LOOP2_OUT, SST_TASK_SBA, 0, &sst_gains[13]), SST_GAIN("sprot_loop_out", SST_PATH_INDEX_SPROT_LOOP_OUT, SST_TASK_SBA, 0, &sst_gains[14]), SST_VOLUME("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[15]), + SST_GAIN("modem_in", SST_PATH_INDEX_MODEM_IN, SST_TASK_SBA, 0, &sst_gains[16]), + SST_GAIN("modem_out", SST_PATH_INDEX_MODEM_OUT, SST_TASK_SBA, 0, &sst_gains[17]), + }; #define SST_GAIN_NUM_CONTROLS 3 diff --git a/sound/soc/intel/atom/sst-atom-controls.h b/sound/soc/intel/atom/sst-atom-controls.h index e0113112f668..351d81469685 100644 --- a/sound/soc/intel/atom/sst-atom-controls.h +++ b/sound/soc/intel/atom/sst-atom-controls.h @@ -35,6 +35,8 @@ enum { /* define a bit for each mixer input */ #define SST_MIX_IP(x) (x) +#define SST_IP_MODEM SST_MIX_IP(0) +#define SST_IP_BT SST_MIX_IP(1) #define SST_IP_CODEC0 SST_MIX_IP(2) #define SST_IP_CODEC1 SST_MIX_IP(3) #define SST_IP_LOOP0 SST_MIX_IP(4) @@ -63,6 +65,7 @@ enum { * Audio DSP Path Ids. Specified by the audio DSP FW */ enum sst_path_index { + SST_PATH_INDEX_MODEM_OUT = (0x00 << SST_PATH_ID_SHIFT), SST_PATH_INDEX_CODEC_OUT0 = (0x02 << SST_PATH_ID_SHIFT), SST_PATH_INDEX_CODEC_OUT1 = (0x03 << SST_PATH_ID_SHIFT), @@ -80,6 +83,7 @@ enum sst_path_index { /* Start of input paths */ + SST_PATH_INDEX_MODEM_IN = (0x80 << SST_PATH_ID_SHIFT), SST_PATH_INDEX_CODEC_IN0 = (0x82 << SST_PATH_ID_SHIFT), SST_PATH_INDEX_CODEC_IN1 = (0x83 << SST_PATH_ID_SHIFT), @@ -105,6 +109,7 @@ enum sst_path_index { * path IDs */ enum sst_swm_inputs { + SST_SWM_IN_MODEM = (SST_PATH_INDEX_MODEM_IN | SST_DEFAULT_CELL_NBR), 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), @@ -124,6 +129,7 @@ enum sst_swm_inputs { * path IDs */ enum sst_swm_outputs { + SST_SWM_OUT_MODEM = (SST_PATH_INDEX_MODEM_OUT | SST_DEFAULT_CELL_NBR), 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), diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 52ed434cbca6..f5a8050351b5 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -670,7 +670,7 @@ static snd_pcm_uframes_t sst_platform_pcm_pointer return str_info->buffer_ptr; } -static struct snd_pcm_ops sst_platform_ops = { +static const struct snd_pcm_ops sst_platform_ops = { .open = sst_platform_open, .ioctl = snd_pcm_lib_ioctl, .trigger = sst_platform_pcm_trigger, @@ -771,6 +771,9 @@ static int sst_soc_prepare(struct device *dev) struct sst_data *drv = dev_get_drvdata(dev); struct snd_soc_pcm_runtime *rtd; + if (!drv->soc_card) + return 0; + /* suspend all pcms first */ snd_soc_suspend(drv->soc_card->dev); snd_soc_poweroff(drv->soc_card->dev); @@ -793,6 +796,9 @@ static void sst_soc_complete(struct device *dev) struct sst_data *drv = dev_get_drvdata(dev); struct snd_soc_pcm_runtime *rtd; + if (!drv->soc_card) + return; + /* restart SSPs */ list_for_each_entry(rtd, &drv->soc_card->rtd_list, list) { struct snd_soc_dai *dai = rtd->cpu_dai; diff --git a/sound/soc/intel/atom/sst/sst.c b/sound/soc/intel/atom/sst/sst.c index a4b458e77089..f9ba71315e33 100644 --- a/sound/soc/intel/atom/sst/sst.c +++ b/sound/soc/intel/atom/sst/sst.c @@ -27,6 +27,7 @@ #include <linux/pm_qos.h> #include <linux/async.h> #include <linux/acpi.h> +#include <linux/sysfs.h> #include <sound/core.h> #include <sound/soc.h> #include <asm/platform_sst_audio.h> @@ -190,7 +191,8 @@ int sst_driver_ops(struct intel_sst_drv *sst) default: dev_err(sst->dev, - "SST Driver capablities missing for dev_id: %x", sst->dev_id); + "SST Driver capabilities missing for dev_id: %x", + sst->dev_id); return -EINVAL; }; } @@ -241,6 +243,32 @@ int sst_alloc_drv_context(struct intel_sst_drv **ctx, } EXPORT_SYMBOL_GPL(sst_alloc_drv_context); +static ssize_t firmware_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + if (ctx->fw_version.type == 0 && ctx->fw_version.major == 0 && + ctx->fw_version.minor == 0 && ctx->fw_version.build == 0) + return sprintf(buf, "FW not yet loaded\n"); + else + return sprintf(buf, "v%02x.%02x.%02x.%02x\n", + ctx->fw_version.type, ctx->fw_version.major, + ctx->fw_version.minor, ctx->fw_version.build); + +} + +DEVICE_ATTR_RO(firmware_version); + +static const struct attribute *sst_fw_version_attrs[] = { + &dev_attr_firmware_version.attr, + NULL, +}; + +static const struct attribute_group sst_fw_version_attr_group = { + .attrs = (struct attribute **)sst_fw_version_attrs, +}; + int sst_context_init(struct intel_sst_drv *ctx) { int ret = 0, i; @@ -314,8 +342,19 @@ int sst_context_init(struct intel_sst_drv *ctx) dev_err(ctx->dev, "Firmware download failed:%d\n", ret); goto do_free_mem; } + + ret = sysfs_create_group(&ctx->dev->kobj, + &sst_fw_version_attr_group); + if (ret) { + dev_err(ctx->dev, + "Unable to create sysfs\n"); + goto err_sysfs; + } + sst_register(ctx->dev); return 0; +err_sysfs: + sysfs_remove_group(&ctx->dev->kobj, &sst_fw_version_attr_group); do_free_mem: destroy_workqueue(ctx->post_msg_wq); @@ -329,6 +368,7 @@ void sst_context_cleanup(struct intel_sst_drv *ctx) pm_runtime_disable(ctx->dev); sst_unregister(ctx->dev); sst_set_fw_state_locked(ctx, SST_SHUTDOWN); + sysfs_remove_group(&ctx->dev->kobj, &sst_fw_version_attr_group); flush_scheduled_work(); destroy_workqueue(ctx->post_msg_wq); pm_qos_remove_request(ctx->qos); @@ -441,7 +481,7 @@ static int intel_sst_suspend(struct device *dev) struct stream_info *stream = &ctx->streams[i]; if (stream->status == STREAM_RUNNING) { - dev_err(dev, "stream %d is running, cant susupend, abort\n", i); + dev_err(dev, "stream %d is running, can't suspend, abort\n", i); return -EBUSY; } } diff --git a/sound/soc/intel/atom/sst/sst.h b/sound/soc/intel/atom/sst/sst.h index 3f493862e98d..5c9a51cc77aa 100644 --- a/sound/soc/intel/atom/sst/sst.h +++ b/sound/soc/intel/atom/sst/sst.h @@ -436,6 +436,7 @@ struct intel_sst_drv { */ char firmware_name[FW_NAME_SIZE]; + struct snd_sst_fw_version fw_version; struct sst_fw_save *fw_save; }; diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c index 4d3184971227..f4d92bbc5373 100644 --- a/sound/soc/intel/atom/sst/sst_acpi.c +++ b/sound/soc/intel/atom/sst/sst_acpi.c @@ -39,6 +39,8 @@ #include <acpi/platform/aclinux.h> #include <acpi/actypes.h> #include <acpi/acpi_bus.h> +#include <asm/cpu_device_id.h> +#include <asm/iosf_mbi.h> #include "../sst-mfld-platform.h" #include "../../common/sst-dsp.h" #include "../../common/sst-acpi.h" @@ -113,6 +115,28 @@ static const struct sst_res_info byt_rvp_res_info = { .acpi_ipc_irq_index = 5, }; +/* BYTCR has different BIOS from BYT */ +static const struct sst_res_info bytcr_res_info = { + .shim_offset = 0x140000, + .shim_size = 0x000100, + .shim_phy_addr = SST_BYT_SHIM_PHY_ADDR, + .ssp0_offset = 0xa0000, + .ssp0_size = 0x1000, + .dma0_offset = 0x98000, + .dma0_size = 0x4000, + .dma1_offset = 0x9c000, + .dma1_size = 0x4000, + .iram_offset = 0x0c0000, + .iram_size = 0x14000, + .dram_offset = 0x100000, + .dram_size = 0x28000, + .mbox_offset = 0x144000, + .mbox_size = 0x1000, + .acpi_lpe_res_index = 0, + .acpi_ddr_index = 2, + .acpi_ipc_irq_index = 0 +}; + static struct sst_platform_info byt_rvp_platform_data = { .probe_data = &byt_fwparse_info, .ipc_info = &byt_ipc_info, @@ -142,7 +166,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) rsrc = platform_get_resource(pdev, IORESOURCE_MEM, ctx->pdata->res_info->acpi_lpe_res_index); if (!rsrc) { - dev_err(ctx->dev, "Invalid SHIM base from IFWI"); + dev_err(ctx->dev, "Invalid SHIM base from IFWI\n"); return -EIO; } dev_info(ctx->dev, "LPE base: %#x size:%#x", (unsigned int) rsrc->start, @@ -154,7 +178,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) ctx->iram = devm_ioremap_nocache(ctx->dev, ctx->iram_base, ctx->pdata->res_info->iram_size); if (!ctx->iram) { - dev_err(ctx->dev, "unable to map IRAM"); + dev_err(ctx->dev, "unable to map IRAM\n"); return -EIO; } @@ -164,7 +188,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) ctx->dram = devm_ioremap_nocache(ctx->dev, ctx->dram_base, ctx->pdata->res_info->dram_size); if (!ctx->dram) { - dev_err(ctx->dev, "unable to map DRAM"); + dev_err(ctx->dev, "unable to map DRAM\n"); return -EIO; } @@ -173,7 +197,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) ctx->shim = devm_ioremap_nocache(ctx->dev, ctx->shim_phy_add, ctx->pdata->res_info->shim_size); if (!ctx->shim) { - dev_err(ctx->dev, "unable to map SHIM"); + dev_err(ctx->dev, "unable to map SHIM\n"); return -EIO; } @@ -186,7 +210,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) ctx->mailbox = devm_ioremap_nocache(ctx->dev, ctx->mailbox_add, ctx->pdata->res_info->mbox_size); if (!ctx->mailbox) { - dev_err(ctx->dev, "unable to map mailbox"); + dev_err(ctx->dev, "unable to map mailbox\n"); return -EIO; } @@ -196,7 +220,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) rsrc = platform_get_resource(pdev, IORESOURCE_MEM, ctx->pdata->res_info->acpi_ddr_index); if (!rsrc) { - dev_err(ctx->dev, "Invalid DDR base from IFWI"); + dev_err(ctx->dev, "Invalid DDR base from IFWI\n"); return -EIO; } ctx->ddr_base = rsrc->start; @@ -205,7 +229,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) ctx->ddr = devm_ioremap_nocache(ctx->dev, ctx->ddr_base, resource_size(rsrc)); if (!ctx->ddr) { - dev_err(ctx->dev, "unable to map DDR"); + dev_err(ctx->dev, "unable to map DDR\n"); return -EIO; } @@ -215,6 +239,46 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) return 0; } + +static int is_byt_cr(struct device *dev, bool *bytcr) +{ + int status = 0; + + if (IS_ENABLED(CONFIG_IOSF_MBI)) { + static const struct x86_cpu_id cpu_ids[] = { + { X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */ + {} + }; + u32 bios_status; + + if (!x86_match_cpu(cpu_ids) || !iosf_mbi_available()) { + /* bail silently */ + return status; + } + + status = iosf_mbi_read(BT_MBI_UNIT_PMC, /* 0x04 PUNIT */ + MBI_REG_READ, /* 0x10 */ + 0x006, /* BIOS_CONFIG */ + &bios_status); + + if (status) { + dev_err(dev, "could not read PUNIT BIOS_CONFIG\n"); + } else { + /* bits 26:27 mirror PMIC options */ + bios_status = (bios_status >> 26) & 3; + + if ((bios_status == 1) || (bios_status == 3)) + *bytcr = true; + else + dev_info(dev, "BYT-CR not detected\n"); + } + } else { + dev_info(dev, "IOSF_MBI not enabled, no BYT-CR detection\n"); + } + return status; +} + + static int sst_acpi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -226,11 +290,12 @@ static int sst_acpi_probe(struct platform_device *pdev) struct platform_device *plat_dev; struct sst_platform_info *pdata; unsigned int dev_id; + bool bytcr = false; id = acpi_match_device(dev->driver->acpi_match_table, dev); if (!id) return -ENODEV; - dev_dbg(dev, "for %s", id->id); + dev_dbg(dev, "for %s\n", id->id); mach = (struct sst_acpi_mach *)id->driver_data; mach = sst_acpi_find_machine(mach); @@ -251,6 +316,18 @@ static int sst_acpi_probe(struct platform_device *pdev) dev_dbg(dev, "ACPI device id: %x\n", dev_id); + ret = sst_alloc_drv_context(&ctx, dev, dev_id); + if (ret < 0) + return ret; + + ret = is_byt_cr(dev, &bytcr); + if (!((ret < 0) || (bytcr == false))) { + dev_info(dev, "Detected Baytrail-CR platform\n"); + + /* override resource info */ + byt_rvp_platform_data.res_info = &bytcr_res_info; + } + plat_dev = platform_device_register_data(dev, pdata->platform, -1, NULL, 0); if (IS_ERR(plat_dev)) { @@ -271,10 +348,6 @@ static int sst_acpi_probe(struct platform_device *pdev) return PTR_ERR(mdev); } - ret = sst_alloc_drv_context(&ctx, dev, dev_id); - if (ret < 0) - return ret; - /* Fill sst platform data */ ctx->pdata = pdata; strcpy(ctx->firmware_name, mach->fw_filename); @@ -343,6 +416,7 @@ static const struct dmi_system_id cht_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"), }, }, + { } }; @@ -378,6 +452,8 @@ static struct sst_acpi_mach sst_acpi_bytcr[] = { static struct sst_acpi_mach sst_acpi_chv[] = { {"10EC5670", "cht-bsw-rt5672", "intel/fw_sst_22a8.bin", "cht-bsw", NULL, &chv_platform_data }, + {"10EC5672", "cht-bsw-rt5672", "intel/fw_sst_22a8.bin", "cht-bsw", NULL, + &chv_platform_data }, {"10EC5645", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL, &chv_platform_data }, {"10EC5650", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL, diff --git a/sound/soc/intel/atom/sst/sst_ipc.c b/sound/soc/intel/atom/sst/sst_ipc.c index 8afa6fe7b0b0..374bb61c596d 100644 --- a/sound/soc/intel/atom/sst/sst_ipc.c +++ b/sound/soc/intel/atom/sst/sst_ipc.c @@ -236,6 +236,17 @@ static void process_fw_init(struct intel_sst_drv *sst_drv_ctx, retval = init->result; goto ret; } + dev_info(sst_drv_ctx->dev, "FW Version %02x.%02x.%02x.%02x\n", + init->fw_version.type, init->fw_version.major, + init->fw_version.minor, init->fw_version.build); + dev_dbg(sst_drv_ctx->dev, "Build date %s Time %s\n", + init->build_info.date, init->build_info.time); + + /* Save FW version */ + sst_drv_ctx->fw_version.type = init->fw_version.type; + sst_drv_ctx->fw_version.major = init->fw_version.major; + sst_drv_ctx->fw_version.minor = init->fw_version.minor; + sst_drv_ctx->fw_version.build = init->fw_version.build; ret: sst_wake_up_block(sst_drv_ctx, retval, FW_DWNL_ID, 0 , NULL, 0); @@ -267,6 +278,9 @@ static void process_fw_async_msg(struct intel_sst_drv *sst_drv_ctx, "Period elapsed rcvd for pipe id 0x%x\n", pipe_id); stream = &sst_drv_ctx->streams[str_id]; + /* If stream is dropped, skip processing this message*/ + if (stream->status == STREAM_INIT) + break; if (stream->period_elapsed) stream->period_elapsed(stream->pcm_substream); if (stream->compr_cb) diff --git a/sound/soc/intel/atom/sst/sst_pvt.c b/sound/soc/intel/atom/sst/sst_pvt.c index adb32fefd693..b1e6b8f34a6a 100644 --- a/sound/soc/intel/atom/sst/sst_pvt.c +++ b/sound/soc/intel/atom/sst/sst_pvt.c @@ -279,17 +279,15 @@ int sst_prepare_and_post_msg(struct intel_sst_drv *sst, if (response) { ret = sst_wait_timeout(sst, block); - if (ret < 0) { + if (ret < 0) goto out; - } else if(block->data) { - if (!data) - goto out; - *data = kzalloc(block->size, GFP_KERNEL); - if (!(*data)) { + + if (data && block->data) { + *data = kmemdup(block->data, block->size, GFP_KERNEL); + if (!*data) { ret = -ENOMEM; goto out; - } else - memcpy(data, (void *) block->data, block->size); + } } } out: diff --git a/sound/soc/intel/atom/sst/sst_stream.c b/sound/soc/intel/atom/sst/sst_stream.c index 4ccc80e5e8cc..51bdeeecb7c8 100644 --- a/sound/soc/intel/atom/sst/sst_stream.c +++ b/sound/soc/intel/atom/sst/sst_stream.c @@ -104,7 +104,7 @@ int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params) sst_init_stream(&sst_drv_ctx->streams[str_id], alloc_param.codec_type, str_id, alloc_param.operation, 0); - dev_info(sst_drv_ctx->dev, "Alloc for str %d pipe %#x\n", + dev_dbg(sst_drv_ctx->dev, "Alloc for str %d pipe %#x\n", str_id, pipe_id); ret = sst_prepare_and_post_msg(sst_drv_ctx, task_id, IPC_CMD, IPC_IA_ALLOC_STREAM_MRFLD, pipe_id, sizeof(alloc_param), @@ -415,7 +415,7 @@ int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int str_id) str_info->status = STREAM_UN_INIT; mutex_unlock(&str_info->lock); - dev_info(sst_drv_ctx->dev, "Free for str %d pipe %#x\n", + dev_dbg(sst_drv_ctx->dev, "Free for str %d pipe %#x\n", str_id, str_info->pipe_id); retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD, IPC_IA_FREE_STREAM_MRFLD, str_info->pipe_id, 0, diff --git a/sound/soc/intel/baytrail/sst-baytrail-ipc.c b/sound/soc/intel/baytrail/sst-baytrail-ipc.c index 5bbaa667bec1..260447da32b8 100644 --- a/sound/soc/intel/baytrail/sst-baytrail-ipc.c +++ b/sound/soc/intel/baytrail/sst-baytrail-ipc.c @@ -23,7 +23,6 @@ #include <linux/slab.h> #include <linux/delay.h> #include <linux/platform_device.h> -#include <linux/kthread.h> #include <linux/firmware.h> #include <linux/io.h> #include <asm/div64.h> @@ -184,15 +183,9 @@ struct sst_byt { static inline u64 sst_byt_header(int msg_id, int data, bool large, int str_id) { - u64 header; - - header = IPC_HEADER_MSG_ID(msg_id) | - IPC_HEADER_STR_ID(str_id) | - IPC_HEADER_LARGE(large) | - IPC_HEADER_DATA(data) | - SST_BYT_IPCX_BUSY; - - return header; + return IPC_HEADER_MSG_ID(msg_id) | IPC_HEADER_STR_ID(str_id) | + IPC_HEADER_LARGE(large) | IPC_HEADER_DATA(data) | + SST_BYT_IPCX_BUSY; } static inline u16 sst_byt_header_msg_id(u64 header) @@ -344,7 +337,7 @@ static irqreturn_t sst_byt_irq_thread(int irq, void *context) spin_unlock_irqrestore(&sst->spinlock, flags); /* continue to send any remaining messages... */ - queue_kthread_work(&ipc->kworker, &ipc->kwork); + schedule_work(&ipc->kwork); return IRQ_HANDLED; } diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index dac03a06bfd8..5639f10774e6 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -1,6 +1,7 @@ snd-soc-sst-haswell-objs := haswell.o snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o +snd-soc-sst-bdw-rt5677-mach-objs := bdw-rt5677.o snd-soc-sst-broadwell-objs := broadwell.o snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o snd-soc-sst-bxt-rt298-objs := bxt_rt298.o @@ -19,6 +20,7 @@ obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH) += snd-soc-sst-bxt-da7219_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o +obj-$(CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH) += snd-soc-sst-bdw-rt5677-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5651_MACH) += snd-soc-sst-bytcr-rt5651.o obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o diff --git a/sound/soc/intel/boards/bdw-rt5677.c b/sound/soc/intel/boards/bdw-rt5677.c new file mode 100644 index 000000000000..53c6b4cbb1e1 --- /dev/null +++ b/sound/soc/intel/boards/bdw-rt5677.c @@ -0,0 +1,347 @@ +/* + * ASoC machine driver for Intel Broadwell platforms with RT5677 codec + * + * Copyright (c) 2014, The Chromium OS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/gpio/consumer.h> +#include <linux/delay.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/pcm_params.h> +#include <sound/jack.h> + +#include "../common/sst-dsp.h" +#include "../haswell/sst-haswell-ipc.h" + +#include "../../codecs/rt5677.h" + +struct bdw_rt5677_priv { + struct gpio_desc *gpio_hp_en; + struct snd_soc_codec *codec; +}; + +static int bdw_rt5677_event_hp(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct bdw_rt5677_priv *bdw_rt5677 = snd_soc_card_get_drvdata(card); + + if (SND_SOC_DAPM_EVENT_ON(event)) + msleep(70); + + gpiod_set_value_cansleep(bdw_rt5677->gpio_hp_en, + SND_SOC_DAPM_EVENT_ON(event)); + + return 0; +} + +static const struct snd_soc_dapm_widget bdw_rt5677_widgets[] = { + SND_SOC_DAPM_HP("Headphone", bdw_rt5677_event_hp), + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Local DMICs", NULL), + SND_SOC_DAPM_MIC("Remote DMICs", NULL), +}; + +static const struct snd_soc_dapm_route bdw_rt5677_map[] = { + /* Speakers */ + {"Speaker", NULL, "PDM1L"}, + {"Speaker", NULL, "PDM1R"}, + + /* Headset jack connectors */ + {"Headphone", NULL, "LOUT1"}, + {"Headphone", NULL, "LOUT2"}, + {"IN1P", NULL, "Headset Mic"}, + {"IN1N", NULL, "Headset Mic"}, + + /* Digital MICs + * Local DMICs: the two DMICs on the mainboard + * Remote DMICs: the two DMICs on the camera module + */ + {"DMIC L1", NULL, "Remote DMICs"}, + {"DMIC R1", NULL, "Remote DMICs"}, + {"DMIC L2", NULL, "Local DMICs"}, + {"DMIC R2", NULL, "Local DMICs"}, + + /* CODEC BE connections */ + {"SSP0 CODEC IN", NULL, "AIF1 Capture"}, + {"AIF1 Playback", NULL, "SSP0 CODEC OUT"}, +}; + +static const struct snd_kcontrol_new bdw_rt5677_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Local DMICs"), + SOC_DAPM_PIN_SWITCH("Remote DMICs"), +}; + + +static struct snd_soc_jack headphone_jack; +static struct snd_soc_jack mic_jack; + +static struct snd_soc_jack_pin headphone_jack_pin = { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, +}; + +static struct snd_soc_jack_pin mic_jack_pin = { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, +}; + +static struct snd_soc_jack_gpio headphone_jack_gpio = { + .name = "plug-det", + .report = SND_JACK_HEADPHONE, + .debounce_time = 200, +}; + +static struct snd_soc_jack_gpio mic_jack_gpio = { + .name = "mic-present", + .report = SND_JACK_MICROPHONE, + .debounce_time = 200, + .invert = 1, +}; + +static int broadwell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + /* The ADSP will covert the FE rate to 48k, stereo */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + /* set SSP0 to 16 bit */ + snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - + SNDRV_PCM_HW_PARAM_FIRST_MASK], + SNDRV_PCM_FORMAT_S16_LE); + return 0; +} + +static int bdw_rt5677_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, RT5677_SCLK_S_MCLK, 24576000, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(rtd->dev, "can't set codec sysclk configuration\n"); + return ret; + } + + return ret; +} + +static const struct snd_soc_ops bdw_rt5677_ops = { + .hw_params = bdw_rt5677_hw_params, +}; + +static int bdw_rt5677_rtd_init(struct snd_soc_pcm_runtime *rtd) +{ + struct sst_pdata *pdata = dev_get_platdata(rtd->platform->dev); + struct sst_hsw *broadwell = pdata->dsp; + int ret; + + /* Set ADSP SSP port settings */ + ret = sst_hsw_device_set_config(broadwell, SST_HSW_DEVICE_SSP_0, + SST_HSW_DEVICE_MCLK_FREQ_24_MHZ, + SST_HSW_DEVICE_CLOCK_MASTER, 9); + if (ret < 0) { + dev_err(rtd->dev, "error: failed to set device config\n"); + return ret; + } + + return 0; +} + +static int bdw_rt5677_init(struct snd_soc_pcm_runtime *rtd) +{ + struct bdw_rt5677_priv *bdw_rt5677 = + snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + /* Enable codec ASRC function for Stereo DAC/Stereo1 ADC/DMIC/I2S1. + * The ASRC clock source is clk_i2s1_asrc. + */ + rt5677_sel_asrc_clk_src(codec, RT5677_DA_STEREO_FILTER | + RT5677_AD_STEREO1_FILTER | RT5677_I2S1_SOURCE, + RT5677_CLK_SEL_I2S1_ASRC); + + /* Request rt5677 GPIO for headphone amp control */ + bdw_rt5677->gpio_hp_en = devm_gpiod_get_index(codec->dev, + "headphone-enable", 0, 0); + if (IS_ERR(bdw_rt5677->gpio_hp_en)) { + dev_err(codec->dev, "Can't find HP_AMP_SHDN_L gpio\n"); + return PTR_ERR(bdw_rt5677->gpio_hp_en); + } + gpiod_direction_output(bdw_rt5677->gpio_hp_en, 0); + + /* Create and initialize headphone jack */ + if (!snd_soc_card_jack_new(rtd->card, "Headphone Jack", + SND_JACK_HEADPHONE, &headphone_jack, + &headphone_jack_pin, 1)) { + headphone_jack_gpio.gpiod_dev = codec->dev; + if (snd_soc_jack_add_gpios(&headphone_jack, 1, + &headphone_jack_gpio)) + dev_err(codec->dev, "Can't add headphone jack gpio\n"); + } else { + dev_err(codec->dev, "Can't create headphone jack\n"); + } + + /* Create and initialize mic jack */ + if (!snd_soc_card_jack_new(rtd->card, "Mic Jack", + SND_JACK_MICROPHONE, &mic_jack, + &mic_jack_pin, 1)) { + mic_jack_gpio.gpiod_dev = codec->dev; + if (snd_soc_jack_add_gpios(&mic_jack, 1, &mic_jack_gpio)) + dev_err(codec->dev, "Can't add mic jack gpio\n"); + } else { + dev_err(codec->dev, "Can't create mic jack\n"); + } + bdw_rt5677->codec = codec; + + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1"); + return 0; +} + +/* broadwell digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link bdw_rt5677_dais[] = { + /* Front End DAI links */ + { + .name = "System PCM", + .stream_name = "System Playback/Capture", + .cpu_dai_name = "System Pin", + .platform_name = "haswell-pcm-audio", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = bdw_rt5677_rtd_init, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_capture = 1, + .dpcm_playback = 1, + }, + + /* Back End DAI links */ + { + /* SSP0 - Codec */ + .name = "Codec", + .id = 0, + .cpu_dai_name = "snd-soc-dummy-dai", + .platform_name = "snd-soc-dummy", + .no_pcm = 1, + .codec_name = "i2c-RT5677CE:00", + .codec_dai_name = "rt5677-aif1", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = broadwell_ssp0_fixup, + .ops = &bdw_rt5677_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + .init = bdw_rt5677_init, + }, +}; + +static int bdw_rt5677_suspend_pre(struct snd_soc_card *card) +{ + struct bdw_rt5677_priv *bdw_rt5677 = snd_soc_card_get_drvdata(card); + struct snd_soc_dapm_context *dapm; + + if (bdw_rt5677->codec) { + dapm = snd_soc_codec_get_dapm(bdw_rt5677->codec); + snd_soc_dapm_disable_pin(dapm, "MICBIAS1"); + } + return 0; +} + +static int bdw_rt5677_resume_post(struct snd_soc_card *card) +{ + struct bdw_rt5677_priv *bdw_rt5677 = snd_soc_card_get_drvdata(card); + struct snd_soc_dapm_context *dapm; + + if (bdw_rt5677->codec) { + dapm = snd_soc_codec_get_dapm(bdw_rt5677->codec); + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1"); + } + return 0; +} + +/* ASoC machine driver for Broadwell DSP + RT5677 */ +static struct snd_soc_card bdw_rt5677_card = { + .name = "bdw-rt5677", + .owner = THIS_MODULE, + .dai_link = bdw_rt5677_dais, + .num_links = ARRAY_SIZE(bdw_rt5677_dais), + .dapm_widgets = bdw_rt5677_widgets, + .num_dapm_widgets = ARRAY_SIZE(bdw_rt5677_widgets), + .dapm_routes = bdw_rt5677_map, + .num_dapm_routes = ARRAY_SIZE(bdw_rt5677_map), + .controls = bdw_rt5677_controls, + .num_controls = ARRAY_SIZE(bdw_rt5677_controls), + .fully_routed = true, + .suspend_pre = bdw_rt5677_suspend_pre, + .resume_post = bdw_rt5677_resume_post, +}; + +static int bdw_rt5677_probe(struct platform_device *pdev) +{ + struct bdw_rt5677_priv *bdw_rt5677; + + bdw_rt5677_card.dev = &pdev->dev; + + /* Allocate driver private struct */ + bdw_rt5677 = devm_kzalloc(&pdev->dev, sizeof(struct bdw_rt5677_priv), + GFP_KERNEL); + if (!bdw_rt5677) { + dev_err(&pdev->dev, "Can't allocate bdw_rt5677\n"); + return -ENOMEM; + } + + snd_soc_card_set_drvdata(&bdw_rt5677_card, bdw_rt5677); + + return devm_snd_soc_register_card(&pdev->dev, &bdw_rt5677_card); +} + +static struct platform_driver bdw_rt5677_audio = { + .probe = bdw_rt5677_probe, + .driver = { + .name = "bdw-rt5677", + }, +}; + +module_platform_driver(bdw_rt5677_audio) + +/* Module information */ +MODULE_AUTHOR("Ben Zhang"); +MODULE_DESCRIPTION("Intel Broadwell RT5677 machine driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bdw-rt5677"); diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c index 7486a0022fde..4d7e9decfa92 100644 --- a/sound/soc/intel/boards/broadwell.c +++ b/sound/soc/intel/boards/broadwell.c @@ -126,7 +126,7 @@ static int broadwell_rt286_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops broadwell_rt286_ops = { +static const struct snd_soc_ops broadwell_rt286_ops = { .hw_params = broadwell_rt286_hw_params, }; @@ -220,10 +220,12 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = { }; static int broadwell_suspend(struct snd_soc_card *card){ - struct snd_soc_codec *codec; + struct snd_soc_component *component; + + list_for_each_entry(component, &card->component_dev_list, card_list) { + if (!strcmp(component->name, "i2c-INT343A:00")) { + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); - list_for_each_entry(codec, &card->codec_dev_list, card_list) { - if (!strcmp(codec->component.name, "i2c-INT343A:00")) { dev_dbg(codec->dev, "disabling jack detect before going to suspend.\n"); rt286_mic_detect(codec, NULL); break; @@ -233,10 +235,12 @@ static int broadwell_suspend(struct snd_soc_card *card){ } static int broadwell_resume(struct snd_soc_card *card){ - struct snd_soc_codec *codec; + struct snd_soc_component *component; + + list_for_each_entry(component, &card->component_dev_list, card_list) { + if (!strcmp(component->name, "i2c-INT343A:00")) { + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); - list_for_each_entry(codec, &card->codec_dev_list, card_list) { - if (!strcmp(codec->component.name, "i2c-INT343A:00")) { dev_dbg(codec->dev, "enabling jack detect for resume.\n"); rt286_mic_detect(codec, &broadwell_headset); break; diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c index 3774b117d365..1b4330cd2739 100644 --- a/sound/soc/intel/boards/bxt_da7219_max98357a.c +++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c @@ -30,6 +30,7 @@ #define BXT_DIALOG_CODEC_DAI "da7219-hifi" #define BXT_MAXIM_CODEC_DAI "HiFi" #define DUAL_CHANNEL 2 +#define QUAD_CHANNEL 4 static struct snd_soc_jack broxton_headset; @@ -37,6 +38,7 @@ enum { BXT_DPCM_AUDIO_PB = 0, BXT_DPCM_AUDIO_CP, BXT_DPCM_AUDIO_REF_CP, + BXT_DPCM_AUDIO_DMIC_CP, BXT_DPCM_AUDIO_HDMI1_PB, BXT_DPCM_AUDIO_HDMI2_PB, BXT_DPCM_AUDIO_HDMI3_PB, @@ -129,8 +131,8 @@ static int broxton_da7219_codec_init(struct snd_soc_pcm_runtime *rtd) */ ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, &broxton_headset, - NULL, 0); + SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_LINEOUT, + &broxton_headset, NULL, 0); if (ret) { dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); return ret; @@ -181,6 +183,16 @@ static struct snd_pcm_hw_constraint_list constraints_channels = { .mask = 0, }; +static unsigned int channels_quad[] = { + QUAD_CHANNEL, +}; + +static struct snd_pcm_hw_constraint_list constraints_channels_quad = { + .count = ARRAY_SIZE(channels_quad), + .list = channels_quad, + .mask = 0, +}; + static int bxt_fe_startup(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -247,15 +259,64 @@ static int broxton_da7219_hw_free(struct snd_pcm_substream *substream) return ret; } -static struct snd_soc_ops broxton_da7219_ops = { +static const struct snd_soc_ops broxton_da7219_ops = { .hw_params = broxton_da7219_hw_params, .hw_free = broxton_da7219_hw_free, }; +static int broxton_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + if (params_channels(params) == 2) + channels->min = channels->max = 2; + else + channels->min = channels->max = 4; + + return 0; +} + +static int broxton_dmic_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw.channels_min = runtime->hw.channels_max = QUAD_CHANNEL; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels_quad); + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); +} + +static const struct snd_soc_ops broxton_dmic_ops = { + .startup = broxton_dmic_startup, +}; + +static const unsigned int rates_16000[] = { + 16000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_16000 = { + .count = ARRAY_SIZE(rates_16000), + .list = rates_16000, +}; + +static int broxton_refcap_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_16000); +}; + +static const struct snd_soc_ops broxton_refcap_ops = { + .startup = broxton_refcap_startup, +}; + /* broxton digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link broxton_dais[] = { /* Front End DAI links */ - [BXT_DPCM_AUDIO_PB] + [BXT_DPCM_AUDIO_PB] = { .name = "Bxt Audio Port", .stream_name = "Audio", @@ -271,7 +332,7 @@ static struct snd_soc_dai_link broxton_dais[] = { .dpcm_playback = 1, .ops = &broxton_da7219_fe_ops, }, - [BXT_DPCM_AUDIO_CP] + [BXT_DPCM_AUDIO_CP] = { .name = "Bxt Audio Capture Port", .stream_name = "Audio Record", @@ -286,7 +347,7 @@ static struct snd_soc_dai_link broxton_dais[] = { .dpcm_capture = 1, .ops = &broxton_da7219_fe_ops, }, - [BXT_DPCM_AUDIO_REF_CP] + [BXT_DPCM_AUDIO_REF_CP] = { .name = "Bxt Audio Reference cap", .stream_name = "Refcap", @@ -299,8 +360,23 @@ static struct snd_soc_dai_link broxton_dais[] = { .ignore_suspend = 1, .nonatomic = 1, .dynamic = 1, + .ops = &broxton_refcap_ops, + }, + [BXT_DPCM_AUDIO_DMIC_CP] = + { + .name = "Bxt Audio DMIC cap", + .stream_name = "dmiccap", + .cpu_dai_name = "DMIC Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .dpcm_capture = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &broxton_dmic_ops, }, - [BXT_DPCM_AUDIO_HDMI1_PB] + [BXT_DPCM_AUDIO_HDMI1_PB] = { .name = "Bxt HDMI Port1", .stream_name = "Hdmi1", @@ -313,7 +389,7 @@ static struct snd_soc_dai_link broxton_dais[] = { .nonatomic = 1, .dynamic = 1, }, - [BXT_DPCM_AUDIO_HDMI2_PB] + [BXT_DPCM_AUDIO_HDMI2_PB] = { .name = "Bxt HDMI Port2", .stream_name = "Hdmi2", @@ -326,7 +402,7 @@ static struct snd_soc_dai_link broxton_dais[] = { .nonatomic = 1, .dynamic = 1, }, - [BXT_DPCM_AUDIO_HDMI3_PB] + [BXT_DPCM_AUDIO_HDMI3_PB] = { .name = "Bxt HDMI Port3", .stream_name = "Hdmi3", @@ -382,6 +458,7 @@ static struct snd_soc_dai_link broxton_dais[] = { .codec_dai_name = "dmic-hifi", .platform_name = "0000:00:0e.0", .ignore_suspend = 1, + .be_hw_params_fixup = broxton_dmic_fixup, .dpcm_capture = 1, .no_pcm = 1, }, diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c index 253d7bfbf511..1309405b3808 100644 --- a/sound/soc/intel/boards/bxt_rt298.c +++ b/sound/soc/intel/boards/bxt_rt298.c @@ -181,7 +181,7 @@ static int broxton_rt298_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops broxton_rt298_ops = { +static const struct snd_soc_ops broxton_rt298_ops = { .hw_params = broxton_rt298_hw_params, }; @@ -230,7 +230,7 @@ static int broxton_dmic_startup(struct snd_pcm_substream *substream) SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); } -static struct snd_soc_ops broxton_dmic_ops = { +static const struct snd_soc_ops broxton_dmic_ops = { .startup = broxton_dmic_startup, }; @@ -271,7 +271,7 @@ static const struct snd_soc_ops broxton_rt286_fe_ops = { /* broxton digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link broxton_rt298_dais[] = { /* Front End DAI links */ - [BXT_DPCM_AUDIO_PB] + [BXT_DPCM_AUDIO_PB] = { .name = "Bxt Audio Port", .stream_name = "Audio", @@ -286,7 +286,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { .dpcm_playback = 1, .ops = &broxton_rt286_fe_ops, }, - [BXT_DPCM_AUDIO_CP] + [BXT_DPCM_AUDIO_CP] = { .name = "Bxt Audio Capture Port", .stream_name = "Audio Record", @@ -300,7 +300,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { .dpcm_capture = 1, .ops = &broxton_rt286_fe_ops, }, - [BXT_DPCM_AUDIO_REF_CP] + [BXT_DPCM_AUDIO_REF_CP] = { .name = "Bxt Audio Reference cap", .stream_name = "refcap", @@ -313,7 +313,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { .nonatomic = 1, .dynamic = 1, }, - [BXT_DPCM_AUDIO_DMIC_CP] + [BXT_DPCM_AUDIO_DMIC_CP] = { .name = "Bxt Audio DMIC cap", .stream_name = "dmiccap", @@ -327,7 +327,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { .dynamic = 1, .ops = &broxton_dmic_ops, }, - [BXT_DPCM_AUDIO_HDMI1_PB] + [BXT_DPCM_AUDIO_HDMI1_PB] = { .name = "Bxt HDMI Port1", .stream_name = "Hdmi1", @@ -340,7 +340,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { .nonatomic = 1, .dynamic = 1, }, - [BXT_DPCM_AUDIO_HDMI2_PB] + [BXT_DPCM_AUDIO_HDMI2_PB] = { .name = "Bxt HDMI Port2", .stream_name = "Hdmi2", @@ -353,7 +353,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { .nonatomic = 1, .dynamic = 1, }, - [BXT_DPCM_AUDIO_HDMI3_PB] + [BXT_DPCM_AUDIO_HDMI3_PB] = { .name = "Bxt HDMI Port3", .stream_name = "Hdmi3", diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 88efb62439ba..8d2fb2d6f532 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -24,6 +24,9 @@ #include <linux/device.h> #include <linux/dmi.h> #include <linux/slab.h> +#include <asm/cpu_device_id.h> +#include <asm/platform_sst_audio.h> +#include <linux/clk.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -31,42 +34,151 @@ #include "../../codecs/rt5640.h" #include "../atom/sst-atom-controls.h" #include "../common/sst-acpi.h" +#include "../common/sst-dsp.h" enum { BYT_RT5640_DMIC1_MAP, BYT_RT5640_DMIC2_MAP, BYT_RT5640_IN1_MAP, + BYT_RT5640_IN3_MAP, }; #define BYT_RT5640_MAP(quirk) ((quirk) & 0xff) #define BYT_RT5640_DMIC_EN BIT(16) +#define BYT_RT5640_MONO_SPEAKER BIT(17) +#define BYT_RT5640_DIFF_MIC BIT(18) /* defaut is single-ended */ +#define BYT_RT5640_SSP2_AIF2 BIT(19) /* default is using AIF1 */ +#define BYT_RT5640_SSP0_AIF1 BIT(20) +#define BYT_RT5640_SSP0_AIF2 BIT(21) +#define BYT_RT5640_MCLK_EN BIT(22) +#define BYT_RT5640_MCLK_25MHZ BIT(23) + +struct byt_rt5640_private { + struct clk *mclk; +}; + +static unsigned long byt_rt5640_quirk = BYT_RT5640_MCLK_EN; + +static void log_quirks(struct device *dev) +{ + if (BYT_RT5640_MAP(byt_rt5640_quirk) == BYT_RT5640_DMIC1_MAP) + dev_info(dev, "quirk DMIC1_MAP enabled"); + if (BYT_RT5640_MAP(byt_rt5640_quirk) == BYT_RT5640_DMIC2_MAP) + dev_info(dev, "quirk DMIC2_MAP enabled"); + if (BYT_RT5640_MAP(byt_rt5640_quirk) == BYT_RT5640_IN1_MAP) + dev_info(dev, "quirk IN1_MAP enabled"); + if (BYT_RT5640_MAP(byt_rt5640_quirk) == BYT_RT5640_IN3_MAP) + dev_info(dev, "quirk IN3_MAP enabled"); + if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) + dev_info(dev, "quirk DMIC enabled"); + if (byt_rt5640_quirk & BYT_RT5640_MONO_SPEAKER) + dev_info(dev, "quirk MONO_SPEAKER enabled"); + if (byt_rt5640_quirk & BYT_RT5640_DIFF_MIC) + dev_info(dev, "quirk DIFF_MIC enabled"); + if (byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) + dev_info(dev, "quirk SSP2_AIF2 enabled"); + if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) + dev_info(dev, "quirk SSP0_AIF1 enabled"); + if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2) + dev_info(dev, "quirk SSP0_AIF2 enabled"); + if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN) + dev_info(dev, "quirk MCLK_EN enabled"); + if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ) + dev_info(dev, "quirk MCLK_25MHZ enabled"); +} + -static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP | - BYT_RT5640_DMIC_EN; +#define BYT_CODEC_DAI1 "rt5640-aif1" +#define BYT_CODEC_DAI2 "rt5640-aif2" + +static inline struct snd_soc_dai *byt_get_codec_dai(struct snd_soc_card *card) +{ + struct snd_soc_pcm_runtime *rtd; + + list_for_each_entry(rtd, &card->rtd_list, list) { + if (!strncmp(rtd->codec_dai->name, BYT_CODEC_DAI1, + strlen(BYT_CODEC_DAI1))) + return rtd->codec_dai; + if (!strncmp(rtd->codec_dai->name, BYT_CODEC_DAI2, + strlen(BYT_CODEC_DAI2))) + return rtd->codec_dai; + + } + return NULL; +} + +static int platform_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct snd_soc_dai *codec_dai; + struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card); + int ret; + + codec_dai = byt_get_codec_dai(card); + if (!codec_dai) { + dev_err(card->dev, + "Codec dai not found; Unable to set platform clock\n"); + return -EIO; + } + + if (SND_SOC_DAPM_EVENT_ON(event)) { + if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->mclk) { + ret = clk_prepare_enable(priv->mclk); + if (ret < 0) { + dev_err(card->dev, + "could not configure MCLK state"); + return ret; + } + } + ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1, + 48000 * 512, + SND_SOC_CLOCK_IN); + } else { + /* + * Set codec clock source to internal clock before + * turning off the platform clock. Codec needs clock + * for Jack detection and button press + */ + ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_RCCLK, + 48000 * 512, + SND_SOC_CLOCK_IN); + if (!ret) { + if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->mclk) + clk_disable_unprepare(priv->mclk); + } + } + + if (ret < 0) { + dev_err(card->dev, "can't set codec sysclk: %d\n", ret); + return ret; + } + + return 0; +} static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_MIC("Internal Mic", NULL), SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + platform_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + }; static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = { - {"AIF1 Playback", NULL, "ssp2 Tx"}, - {"ssp2 Tx", NULL, "codec_out0"}, - {"ssp2 Tx", NULL, "codec_out1"}, - {"codec_in0", NULL, "ssp2 Rx"}, - {"codec_in1", NULL, "ssp2 Rx"}, - {"ssp2 Rx", NULL, "AIF1 Capture"}, + {"Headphone", NULL, "Platform Clock"}, + {"Headset Mic", NULL, "Platform Clock"}, + {"Internal Mic", NULL, "Platform Clock"}, + {"Speaker", NULL, "Platform Clock"}, {"Headset Mic", NULL, "MICBIAS1"}, {"IN2P", NULL, "Headset Mic"}, {"Headphone", NULL, "HPOL"}, {"Headphone", NULL, "HPOR"}, - {"Speaker", NULL, "SPOLP"}, - {"Speaker", NULL, "SPOLN"}, - {"Speaker", NULL, "SPORP"}, - {"Speaker", NULL, "SPORN"}, }; static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic1_map[] = { @@ -82,6 +194,59 @@ static const struct snd_soc_dapm_route byt_rt5640_intmic_in1_map[] = { {"IN1P", NULL, "Internal Mic"}, }; +static const struct snd_soc_dapm_route byt_rt5640_intmic_in3_map[] = { + {"Internal Mic", NULL, "MICBIAS1"}, + {"IN3P", NULL, "Internal Mic"}, +}; + +static const struct snd_soc_dapm_route byt_rt5640_ssp2_aif1_map[] = { + {"ssp2 Tx", NULL, "codec_out0"}, + {"ssp2 Tx", NULL, "codec_out1"}, + {"codec_in0", NULL, "ssp2 Rx"}, + {"codec_in1", NULL, "ssp2 Rx"}, + + {"AIF1 Playback", NULL, "ssp2 Tx"}, + {"ssp2 Rx", NULL, "AIF1 Capture"}, +}; + +static const struct snd_soc_dapm_route byt_rt5640_ssp2_aif2_map[] = { + {"ssp2 Tx", NULL, "codec_out0"}, + {"ssp2 Tx", NULL, "codec_out1"}, + {"codec_in0", NULL, "ssp2 Rx"}, + {"codec_in1", NULL, "ssp2 Rx"}, + + {"AIF2 Playback", NULL, "ssp2 Tx"}, + {"ssp2 Rx", NULL, "AIF2 Capture"}, +}; + +static const struct snd_soc_dapm_route byt_rt5640_ssp0_aif1_map[] = { + {"ssp0 Tx", NULL, "modem_out"}, + {"modem_in", NULL, "ssp0 Rx"}, + + {"AIF1 Playback", NULL, "ssp0 Tx"}, + {"ssp0 Rx", NULL, "AIF1 Capture"}, +}; + +static const struct snd_soc_dapm_route byt_rt5640_ssp0_aif2_map[] = { + {"ssp0 Tx", NULL, "modem_out"}, + {"modem_in", NULL, "ssp0 Rx"}, + + {"AIF2 Playback", NULL, "ssp0 Tx"}, + {"ssp0 Rx", NULL, "AIF2 Capture"}, +}; + +static const struct snd_soc_dapm_route byt_rt5640_stereo_spk_map[] = { + {"Speaker", NULL, "SPOLP"}, + {"Speaker", NULL, "SPOLN"}, + {"Speaker", NULL, "SPORP"}, + {"Speaker", NULL, "SPORN"}, +}; + +static const struct snd_soc_dapm_route byt_rt5640_mono_spk_map[] = { + {"Speaker", NULL, "SPOLP"}, + {"Speaker", NULL, "SPOLN"}, +}; + static const struct snd_kcontrol_new byt_rt5640_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone"), SOC_DAPM_PIN_SWITCH("Headset Mic"), @@ -96,19 +261,46 @@ static int byt_rt5640_aif1_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai = rtd->codec_dai; int ret; - snd_soc_dai_set_bclk_ratio(codec_dai, 50); - ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1, params_rate(params) * 512, SND_SOC_CLOCK_IN); + if (ret < 0) { dev_err(rtd->dev, "can't set codec clock %d\n", ret); return ret; } - ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1, - params_rate(params) * 50, - params_rate(params) * 512); + if (!(byt_rt5640_quirk & BYT_RT5640_MCLK_EN)) { + /* use bitclock as PLL input */ + if ((byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) || + (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) { + + /* 2x16 bit slots on SSP0 */ + ret = snd_soc_dai_set_pll(codec_dai, 0, + RT5640_PLL1_S_BCLK1, + params_rate(params) * 32, + params_rate(params) * 512); + } else { + /* 2x15 bit slots on SSP2 */ + ret = snd_soc_dai_set_pll(codec_dai, 0, + RT5640_PLL1_S_BCLK1, + params_rate(params) * 50, + params_rate(params) * 512); + } + } else { + if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ) { + ret = snd_soc_dai_set_pll(codec_dai, 0, + RT5640_PLL1_S_MCLK, + 25000000, + params_rate(params) * 512); + } else { + ret = snd_soc_dai_set_pll(codec_dai, 0, + RT5640_PLL1_S_MCLK, + 19200000, + params_rate(params) * 512); + } + } + if (ret < 0) { dev_err(rtd->dev, "can't set codec pll: %d\n", ret); return ret; @@ -127,27 +319,73 @@ 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"), + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TA"), }, - .driver_data = (unsigned long *)BYT_RT5640_IN1_MAP, + .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_MCLK_EN), }, { .callback = byt_rt5640_quirk_cb, .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "DellInc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"), + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TAF"), + }, + .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_MONO_SPEAKER | + BYT_RT5640_DIFF_MIC | + BYT_RT5640_SSP0_AIF2 | + BYT_RT5640_MCLK_EN + ), + }, + { + .callback = byt_rt5640_quirk_cb, + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "DellInc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"), }, .driver_data = (unsigned long *)(BYT_RT5640_DMIC2_MAP | + BYT_RT5640_DMIC_EN | + BYT_RT5640_MCLK_EN), + }, + { + .callback = byt_rt5640_quirk_cb, + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP ElitePad 1000 G2"), + }, + .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_MCLK_EN), + }, + { + .callback = byt_rt5640_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"), + DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max B3 PLATFORM"), + }, + .driver_data = (unsigned long *)(BYT_RT5640_DMIC1_MAP | BYT_RT5640_DMIC_EN), }, { .callback = byt_rt5640_quirk_cb, .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP ElitePad 1000 G2"), + DMI_MATCH(DMI_BOARD_VENDOR, "TECLAST"), + DMI_MATCH(DMI_BOARD_NAME, "tPAD"), + }, + .driver_data = (unsigned long *)(BYT_RT5640_IN3_MAP | + BYT_RT5640_MCLK_EN | + BYT_RT5640_SSP0_AIF1), + }, + { + .callback = byt_rt5640_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"), }, - .driver_data = (unsigned long *)BYT_RT5640_IN1_MAP, + .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_MCLK_EN | + BYT_RT5640_SSP0_AIF1), + }, {} }; @@ -158,13 +396,18 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) struct snd_soc_codec *codec = runtime->codec; struct snd_soc_card *card = runtime->card; const struct snd_soc_dapm_route *custom_map; + struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card); int num_routes; card->dapm.idle_bias_off = true; rt5640_sel_asrc_clk_src(codec, RT5640_DA_STEREO_FILTER | - RT5640_AD_STEREO_FILTER, + RT5640_DA_MONO_L_FILTER | + RT5640_DA_MONO_R_FILTER | + RT5640_AD_STEREO_FILTER | + RT5640_AD_MONO_L_FILTER | + RT5640_AD_MONO_R_FILTER, RT5640_CLK_SEL_ASRC); ret = snd_soc_add_card_controls(card, byt_rt5640_controls, @@ -179,6 +422,10 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) custom_map = byt_rt5640_intmic_in1_map; num_routes = ARRAY_SIZE(byt_rt5640_intmic_in1_map); break; + case BYT_RT5640_IN3_MAP: + custom_map = byt_rt5640_intmic_in3_map; + num_routes = ARRAY_SIZE(byt_rt5640_intmic_in3_map); + break; case BYT_RT5640_DMIC2_MAP: custom_map = byt_rt5640_intmic_dmic2_map; num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic2_map); @@ -192,6 +439,43 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) if (ret) return ret; + if (byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) { + ret = snd_soc_dapm_add_routes(&card->dapm, + byt_rt5640_ssp2_aif2_map, + ARRAY_SIZE(byt_rt5640_ssp2_aif2_map)); + } else if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) { + ret = snd_soc_dapm_add_routes(&card->dapm, + byt_rt5640_ssp0_aif1_map, + ARRAY_SIZE(byt_rt5640_ssp0_aif1_map)); + } else if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2) { + ret = snd_soc_dapm_add_routes(&card->dapm, + byt_rt5640_ssp0_aif2_map, + ARRAY_SIZE(byt_rt5640_ssp0_aif2_map)); + } else { + ret = snd_soc_dapm_add_routes(&card->dapm, + byt_rt5640_ssp2_aif1_map, + ARRAY_SIZE(byt_rt5640_ssp2_aif1_map)); + } + if (ret) + return ret; + + if (byt_rt5640_quirk & BYT_RT5640_MONO_SPEAKER) { + ret = snd_soc_dapm_add_routes(&card->dapm, + byt_rt5640_mono_spk_map, + ARRAY_SIZE(byt_rt5640_mono_spk_map)); + } else { + ret = snd_soc_dapm_add_routes(&card->dapm, + byt_rt5640_stereo_spk_map, + ARRAY_SIZE(byt_rt5640_stereo_spk_map)); + } + if (ret) + return ret; + + if (byt_rt5640_quirk & BYT_RT5640_DIFF_MIC) { + snd_soc_update_bits(codec, RT5640_IN1_IN2, RT5640_IN_DF1, + RT5640_IN_DF1); + } + if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) { ret = rt5640_dmic_enable(codec, 0, 0); if (ret) @@ -201,6 +485,30 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone"); snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker"); + if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->mclk) { + /* + * The firmware might enable the clock at + * boot (this information may or may not + * be reflected in the enable clock register). + * To change the rate we must disable the clock + * first to cover these cases. Due to common + * clock framework restrictions that do not allow + * to disable a clock that has not been enabled, + * we need to enable the clock first. + */ + ret = clk_prepare_enable(priv->mclk); + if (!ret) + clk_disable_unprepare(priv->mclk); + + if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ) + ret = clk_set_rate(priv->mclk, 25000000); + else + ret = clk_set_rate(priv->mclk, 19200000); + + if (ret) + dev_err(card->dev, "unable to set MCLK rate\n"); + } + return ret; } @@ -221,34 +529,63 @@ static int byt_rt5640_codec_fixup(struct snd_soc_pcm_runtime *rtd, SNDRV_PCM_HW_PARAM_CHANNELS); int ret; - /* The DSP will covert the FE rate to 48k, stereo, 24bits */ + /* The DSP will covert the FE rate to 48k, stereo */ rate->min = rate->max = 48000; channels->min = channels->max = 2; - /* set SSP2 to 24-bit */ - params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); + if ((byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) || + (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) { + + /* set SSP0 to 16-bit */ + params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); + + /* + * Default mode for SSP configuration is TDM 4 slot, override config + * with explicit setting to I2S 2ch 16-bit. The word length is set with + * dai_set_tdm_slot() since there is no other API exposed + */ + ret = snd_soc_dai_set_fmt(rtd->cpu_dai, + SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_IF | + SND_SOC_DAIFMT_CBS_CFS + ); + if (ret < 0) { + dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); + return ret; + } - /* - * Default mode for SSP configuration is TDM 4 slot, override config - * with explicit setting to I2S 2ch 24-bit. The word length is set with - * dai_set_tdm_slot() since there is no other API exposed - */ - ret = snd_soc_dai_set_fmt(rtd->cpu_dai, - SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_IF | - SND_SOC_DAIFMT_CBS_CFS - ); - if (ret < 0) { - dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); - return ret; - } + ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 16); + if (ret < 0) { + dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); + return ret; + } - ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24); - if (ret < 0) { - dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); - return ret; - } + } else { + + /* set SSP2 to 24-bit */ + params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); + + /* + * Default mode for SSP configuration is TDM 4 slot, override config + * with explicit setting to I2S 2ch 24-bit. The word length is set with + * dai_set_tdm_slot() since there is no other API exposed + */ + ret = snd_soc_dai_set_fmt(rtd->cpu_dai, + SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_IF | + SND_SOC_DAIFMT_CBS_CFS + ); + if (ret < 0) { + dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); + return ret; + } + ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24); + if (ret < 0) { + dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); + return ret; + } + } return 0; } @@ -258,11 +595,11 @@ static int byt_rt5640_aif1_startup(struct snd_pcm_substream *substream) SNDRV_PCM_HW_PARAM_RATE, 48000); } -static struct snd_soc_ops byt_rt5640_aif1_ops = { +static const struct snd_soc_ops byt_rt5640_aif1_ops = { .startup = byt_rt5640_aif1_startup, }; -static struct snd_soc_ops byt_rt5640_be_ssp2_ops = { +static const struct snd_soc_ops byt_rt5640_be_ssp2_ops = { .hw_params = byt_rt5640_aif1_hw_params, }; @@ -305,10 +642,10 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = { { .name = "SSP2-Codec", .id = 1, - .cpu_dai_name = "ssp2-port", + .cpu_dai_name = "ssp2-port", /* overwritten for ssp0 routing */ .platform_name = "sst-mfld-platform", .no_pcm = 1, - .codec_dai_name = "rt5640-aif1", + .codec_dai_name = "rt5640-aif1", /* changed w/ quirk */ .codec_name = "i2c-10EC5640:00", /* overwritten with HID */ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, @@ -335,6 +672,25 @@ static struct snd_soc_card byt_rt5640_card = { }; static char byt_rt5640_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */ +static char byt_rt5640_codec_aif_name[12]; /* = "rt5640-aif[1|2]" */ +static char byt_rt5640_cpu_dai_name[10]; /* = "ssp[0|2]-port" */ + +static bool is_valleyview(void) +{ + static const struct x86_cpu_id cpu_ids[] = { + { X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */ + {} + }; + + if (!x86_match_cpu(cpu_ids)) + return false; + return true; +} + +struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */ + u64 aif_value; /* 1: AIF1, 2: AIF2 */ + u64 mclock_value; /* usually 25MHz (0x17d7940), ignored */ +}; static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) { @@ -343,10 +699,17 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) const char *i2c_name = NULL; int i; int dai_index; + struct byt_rt5640_private *priv; + bool is_bytcr = false; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC); + if (!priv) + return -ENOMEM; /* register the soc card */ byt_rt5640_card.dev = &pdev->dev; mach = byt_rt5640_card.dev->platform_data; + snd_soc_card_set_drvdata(&byt_rt5640_card, priv); /* fix index of codec dai */ dai_index = MERR_DPCM_COMPR + 1; @@ -366,8 +729,118 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) byt_rt5640_dais[dai_index].codec_name = byt_rt5640_codec_name; } + /* + * swap SSP0 if bytcr is detected + * (will be overridden if DMI quirk is detected) + */ + if (is_valleyview()) { + struct sst_platform_info *p_info = mach->pdata; + const struct sst_res_info *res_info = p_info->res_info; + + if (res_info->acpi_ipc_irq_index == 0) + is_bytcr = true; + } + + if (is_bytcr) { + /* + * Baytrail CR platforms may have CHAN package in BIOS, try + * to find relevant routing quirk based as done on Windows + * platforms. We have to read the information directly from the + * BIOS, at this stage the card is not created and the links + * with the codec driver/pdata are non-existent + */ + + struct acpi_chan_package chan_package; + + /* format specified: 2 64-bit integers */ + struct acpi_buffer format = {sizeof("NN"), "NN"}; + struct acpi_buffer state = {0, NULL}; + struct sst_acpi_package_context pkg_ctx; + bool pkg_found = false; + + state.length = sizeof(chan_package); + state.pointer = &chan_package; + + pkg_ctx.name = "CHAN"; + pkg_ctx.length = 2; + pkg_ctx.format = &format; + pkg_ctx.state = &state; + pkg_ctx.data_valid = false; + + pkg_found = sst_acpi_find_package_from_hid(mach->id, &pkg_ctx); + if (pkg_found) { + if (chan_package.aif_value == 1) { + dev_info(&pdev->dev, "BIOS Routing: AIF1 connected\n"); + byt_rt5640_quirk |= BYT_RT5640_SSP0_AIF1; + } else if (chan_package.aif_value == 2) { + dev_info(&pdev->dev, "BIOS Routing: AIF2 connected\n"); + byt_rt5640_quirk |= BYT_RT5640_SSP0_AIF2; + } else { + dev_info(&pdev->dev, "BIOS Routing isn't valid, ignored\n"); + pkg_found = false; + } + } + + if (!pkg_found) { + /* no BIOS indications, assume SSP0-AIF2 connection */ + byt_rt5640_quirk |= BYT_RT5640_SSP0_AIF2; + } + + /* change defaults for Baytrail-CR capture */ + byt_rt5640_quirk |= BYT_RT5640_IN1_MAP; + byt_rt5640_quirk |= BYT_RT5640_DIFF_MIC; + } else { + byt_rt5640_quirk |= (BYT_RT5640_DMIC1_MAP | + BYT_RT5640_DMIC_EN); + } + /* check quirks before creating card */ dmi_check_system(byt_rt5640_quirk_table); + log_quirks(&pdev->dev); + + if ((byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) || + (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) { + + /* fixup codec aif name */ + snprintf(byt_rt5640_codec_aif_name, + sizeof(byt_rt5640_codec_aif_name), + "%s", "rt5640-aif2"); + + byt_rt5640_dais[dai_index].codec_dai_name = + byt_rt5640_codec_aif_name; + } + + if ((byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) || + (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) { + + /* fixup cpu dai name name */ + snprintf(byt_rt5640_cpu_dai_name, + sizeof(byt_rt5640_cpu_dai_name), + "%s", "ssp0-port"); + + byt_rt5640_dais[dai_index].cpu_dai_name = + byt_rt5640_cpu_dai_name; + } + + if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && (is_valleyview())) { + priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); + if (IS_ERR(priv->mclk)) { + ret_val = PTR_ERR(priv->mclk); + + dev_err(&pdev->dev, + "Failed to get MCLK from pmc_plt_clk_3: %d\n", + ret_val); + + /* + * Fall back to bit clock usage for -ENOENT (clock not + * available likely due to missing dependencies), bail + * for all other errors, including -EPROBE_DEFER + */ + if (ret_val != -ENOENT) + return ret_val; + byt_rt5640_quirk &= ~BYT_RT5640_MCLK_EN; + } + } ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5640_card); diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index 35f591eab3c9..2d24dc04b597 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -219,11 +219,11 @@ static int byt_rt5651_aif1_startup(struct snd_pcm_substream *substream) &constraints_48000); } -static struct snd_soc_ops byt_rt5651_aif1_ops = { +static const struct snd_soc_ops byt_rt5651_aif1_ops = { .startup = byt_rt5651_aif1_startup, }; -static struct snd_soc_ops byt_rt5651_be_ssp2_ops = { +static const struct snd_soc_ops byt_rt5651_be_ssp2_ops = { .hw_params = byt_rt5651_aif1_hw_params, }; diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c index cdcced9f32b6..742bc0d4e681 100644 --- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c +++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c @@ -204,11 +204,11 @@ static int cht_max98090_headset_init(struct snd_soc_component *component) return ts3a227e_enable_jack_detect(component, &ctx->jack); } -static struct snd_soc_ops cht_aif1_ops = { +static const struct snd_soc_ops cht_aif1_ops = { .startup = cht_aif1_startup, }; -static struct snd_soc_ops cht_be_ssp2_ops = { +static const struct snd_soc_ops cht_be_ssp2_ops = { .hw_params = cht_aif1_hw_params, }; diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index 56056ed7fcfd..f504a0e18f91 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -44,6 +44,7 @@ struct cht_acpi_card { struct cht_mc_private { struct snd_soc_jack jack; struct cht_acpi_card *acpi_card; + char codec_name[16]; }; static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card) @@ -250,11 +251,11 @@ static int cht_aif1_startup(struct snd_pcm_substream *substream) SNDRV_PCM_HW_PARAM_RATE, 48000); } -static struct snd_soc_ops cht_aif1_ops = { +static const struct snd_soc_ops cht_aif1_ops = { .startup = cht_aif1_startup, }; -static struct snd_soc_ops cht_be_ssp2_ops = { +static const struct snd_soc_ops cht_be_ssp2_ops = { .hw_params = cht_aif1_hw_params, }; @@ -354,7 +355,6 @@ static int snd_cht_mc_probe(struct platform_device *pdev) int i; struct cht_mc_private *drv; struct snd_soc_card *card = snd_soc_cards[0].soc_card; - char codec_name[16]; struct sst_acpi_mach *mach; const char *i2c_name = NULL; int dai_index = 0; @@ -374,12 +374,12 @@ static int snd_cht_mc_probe(struct platform_device *pdev) } card->dev = &pdev->dev; mach = card->dev->platform_data; - sprintf(codec_name, "i2c-%s:00", drv->acpi_card->codec_id); + sprintf(drv->codec_name, "i2c-%s:00", drv->acpi_card->codec_id); /* set correct codec name */ for (i = 0; i < ARRAY_SIZE(cht_dailink); i++) if (!strcmp(card->dai_link[i].codec_name, "i2c-10EC5645:00")) { - card->dai_link[i].codec_name = kstrdup(codec_name, GFP_KERNEL); + card->dai_link[i].codec_name = drv->codec_name; dai_index = i; } diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index df9d254baa18..e4d46d4360d7 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -25,12 +25,14 @@ #include <sound/jack.h> #include "../../codecs/rt5670.h" #include "../atom/sst-atom-controls.h" +#include "../common/sst-acpi.h" /* The platform clock #3 outputs 19.2Mhz clock to codec as I2S MCLK */ #define CHT_PLAT_CLK_3_HZ 19200000 #define CHT_CODEC_DAI "rt5670-aif1" static struct snd_soc_jack cht_bsw_headset; +static char cht_bsw_codec_name[16]; /* Headset jack detection DAPM pins */ static struct snd_soc_jack_pin cht_bsw_headset_pins[] = { @@ -225,11 +227,11 @@ static int cht_aif1_startup(struct snd_pcm_substream *substream) SNDRV_PCM_HW_PARAM_RATE, 48000); } -static struct snd_soc_ops cht_aif1_ops = { +static const struct snd_soc_ops cht_aif1_ops = { .startup = cht_aif1_startup, }; -static struct snd_soc_ops cht_be_ssp2_ops = { +static const struct snd_soc_ops cht_be_ssp2_ops = { .hw_params = cht_aif1_hw_params, }; @@ -292,10 +294,12 @@ static struct snd_soc_dai_link cht_dailink[] = { static int cht_suspend_pre(struct snd_soc_card *card) { - struct snd_soc_codec *codec; + struct snd_soc_component *component; + + list_for_each_entry(component, &card->component_dev_list, card_list) { + if (!strcmp(component->name, "i2c-10EC5670:00")) { + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); - list_for_each_entry(codec, &card->codec_dev_list, card_list) { - if (!strcmp(codec->component.name, "i2c-10EC5670:00")) { dev_dbg(codec->dev, "disabling jack detect before going to suspend.\n"); rt5670_jack_suspend(codec); break; @@ -306,10 +310,12 @@ static int cht_suspend_pre(struct snd_soc_card *card) static int cht_resume_post(struct snd_soc_card *card) { - struct snd_soc_codec *codec; + struct snd_soc_component *component; + + list_for_each_entry(component, &card->component_dev_list, card_list) { + if (!strcmp(component->name, "i2c-10EC5670:00")) { + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); - list_for_each_entry(codec, &card->codec_dev_list, card_list) { - if (!strcmp(codec->component.name, "i2c-10EC5670:00")) { dev_dbg(codec->dev, "enabling jack detect for resume.\n"); rt5670_jack_resume(codec); break; @@ -335,9 +341,33 @@ static struct snd_soc_card snd_soc_card_cht = { .resume_post = cht_resume_post, }; +#define RT5672_I2C_DEFAULT "i2c-10EC5670:00" + static int snd_cht_mc_probe(struct platform_device *pdev) { int ret_val = 0; + struct sst_acpi_mach *mach = pdev->dev.platform_data; + const char *i2c_name; + int i; + + strcpy(cht_bsw_codec_name, RT5672_I2C_DEFAULT); + + /* fixup codec name based on HID */ + if (mach) { + i2c_name = sst_acpi_find_name_from_hid(mach->id); + if (i2c_name) { + snprintf(cht_bsw_codec_name, sizeof(cht_bsw_codec_name), + "i2c-%s", i2c_name); + for (i = 0; i < ARRAY_SIZE(cht_dailink); i++) { + if (!strcmp(cht_dailink[i].codec_name, + RT5672_I2C_DEFAULT)) { + cht_dailink[i].codec_name = + cht_bsw_codec_name; + break; + } + } + } + } /* register the soc card */ snd_soc_card_cht.dev = &pdev->dev; diff --git a/sound/soc/intel/boards/haswell.c b/sound/soc/intel/boards/haswell.c index 863f1d5e2a2c..5e1ea0371c90 100644 --- a/sound/soc/intel/boards/haswell.c +++ b/sound/soc/intel/boards/haswell.c @@ -81,7 +81,7 @@ static int haswell_rt5640_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops haswell_rt5640_ops = { +static const struct snd_soc_ops haswell_rt5640_ops = { .hw_params = haswell_rt5640_hw_params, }; diff --git a/sound/soc/intel/boards/mfld_machine.c b/sound/soc/intel/boards/mfld_machine.c index 34f46c72a0e2..4e08885f37aa 100644 --- a/sound/soc/intel/boards/mfld_machine.c +++ b/sound/soc/intel/boards/mfld_machine.c @@ -81,9 +81,9 @@ static struct snd_soc_jack_zone mfld_zones[] = { }; /* sound card controls */ -static const char *headset_switch_text[] = {"Earpiece", "Headset"}; +static const char * const headset_switch_text[] = {"Earpiece", "Headset"}; -static const char *lo_text[] = {"Vibra", "Headset", "IHF", "None"}; +static const char * const lo_text[] = {"Vibra", "Headset", "IHF", "None"}; static const struct soc_enum headset_enum = SOC_ENUM_SINGLE_EXT(2, headset_switch_text); diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c index 25db5be7fdfa..fddd1cd12f13 100644 --- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c +++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c @@ -332,7 +332,7 @@ static int skylake_nau8825_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops skylake_nau8825_ops = { +static const struct snd_soc_ops skylake_nau8825_ops = { .hw_params = skylake_nau8825_hw_params, }; @@ -382,7 +382,7 @@ static int skylake_dmic_startup(struct snd_pcm_substream *substream) SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); } -static struct snd_soc_ops skylake_dmic_ops = { +static const struct snd_soc_ops skylake_dmic_ops = { .startup = skylake_dmic_startup, }; @@ -416,7 +416,7 @@ static int skylake_refcap_startup(struct snd_pcm_substream *substream) &constraints_16000); } -static struct snd_soc_ops skylaye_refcap_ops = { +static const struct snd_soc_ops skylaye_refcap_ops = { .startup = skylake_refcap_startup, }; diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c index 69c5d5da4e86..8ab865ee0cad 100644 --- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c +++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c @@ -394,7 +394,7 @@ static int skylake_nau8825_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops skylake_nau8825_ops = { +static const struct snd_soc_ops skylake_nau8825_ops = { .hw_params = skylake_nau8825_hw_params, }; @@ -430,7 +430,7 @@ static int skylake_dmic_startup(struct snd_pcm_substream *substream) SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); } -static struct snd_soc_ops skylake_dmic_ops = { +static const struct snd_soc_ops skylake_dmic_ops = { .startup = skylake_dmic_startup, }; @@ -464,7 +464,7 @@ static int skylake_refcap_startup(struct snd_pcm_substream *substream) &constraints_16000); } -static struct snd_soc_ops skylaye_refcap_ops = { +static const struct snd_soc_ops skylaye_refcap_ops = { .startup = skylake_refcap_startup, }; diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c index 88c61e8cb87f..dc5c3611a6ff 100644 --- a/sound/soc/intel/boards/skl_rt286.c +++ b/sound/soc/intel/boards/skl_rt286.c @@ -250,7 +250,7 @@ static int skylake_rt286_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops skylake_rt286_ops = { +static const struct snd_soc_ops skylake_rt286_ops = { .hw_params = skylake_rt286_hw_params, }; @@ -289,7 +289,7 @@ static int skylake_dmic_startup(struct snd_pcm_substream *substream) SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); } -static struct snd_soc_ops skylake_dmic_ops = { +static const struct snd_soc_ops skylake_dmic_ops = { .startup = skylake_dmic_startup, }; diff --git a/sound/soc/intel/common/sst-acpi.c b/sound/soc/intel/common/sst-acpi.c index 2c5eda14d510..1285cc597b6b 100644 --- a/sound/soc/intel/common/sst-acpi.c +++ b/sound/soc/intel/common/sst-acpi.c @@ -199,6 +199,7 @@ static struct sst_acpi_desc sst_acpi_haswell_desc = { static struct sst_acpi_mach broadwell_machines[] = { { "INT343A", "broadwell-audio", "intel/IntcSST2.bin", NULL, NULL, NULL }, + { "RT5677CE", "bdw-rt5677", "intel/IntcSST2.bin", NULL, NULL, NULL }, {} }; diff --git a/sound/soc/intel/common/sst-acpi.h b/sound/soc/intel/common/sst-acpi.h index 5d2949324d0e..214e000667ae 100644 --- a/sound/soc/intel/common/sst-acpi.h +++ b/sound/soc/intel/common/sst-acpi.h @@ -12,18 +12,32 @@ * */ -#include <linux/kconfig.h> #include <linux/stddef.h> #include <linux/acpi.h> -/* translation fron HID to I2C name, needed for DAI codec_name */ +struct sst_acpi_package_context { + char *name; /* package name */ + int length; /* number of elements */ + struct acpi_buffer *format; + struct acpi_buffer *state; + bool data_valid; +}; + #if IS_ENABLED(CONFIG_ACPI) +/* translation fron HID to I2C name, needed for DAI codec_name */ const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]); +bool sst_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN], + struct sst_acpi_package_context *ctx); #else static inline const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]) { return NULL; } +static inline bool sst_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN], + struct sst_acpi_package_context *ctx) +{ + return false; +} #endif /* acpi match */ diff --git a/sound/soc/intel/common/sst-ipc.c b/sound/soc/intel/common/sst-ipc.c index a12c7bb08d3b..62f3a8e0ec87 100644 --- a/sound/soc/intel/common/sst-ipc.c +++ b/sound/soc/intel/common/sst-ipc.c @@ -26,7 +26,6 @@ #include <linux/sched.h> #include <linux/delay.h> #include <linux/platform_device.h> -#include <linux/kthread.h> #include <sound/asound.h> #include "sst-dsp.h" @@ -109,10 +108,9 @@ static int ipc_tx_message(struct sst_generic_ipc *ipc, u64 header, ipc->ops.tx_data_copy(msg, tx_data, tx_bytes); list_add_tail(&msg->list, &ipc->tx_list); + schedule_work(&ipc->kwork); spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); - queue_kthread_work(&ipc->kworker, &ipc->kwork); - if (wait) return tx_wait_done(ipc, msg, rx_data); else @@ -156,42 +154,56 @@ free_mem: return -ENOMEM; } -static void ipc_tx_msgs(struct kthread_work *work) +static void ipc_tx_msgs(struct work_struct *work) { struct sst_generic_ipc *ipc = container_of(work, struct sst_generic_ipc, kwork); struct ipc_message *msg; - unsigned long flags; - spin_lock_irqsave(&ipc->dsp->spinlock, flags); + spin_lock_irq(&ipc->dsp->spinlock); - if (list_empty(&ipc->tx_list) || ipc->pending) { - spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); - return; - } - - /* if the DSP is busy, we will TX messages after IRQ. - * also postpone if we are in the middle of procesing completion irq*/ - if (ipc->ops.is_dsp_busy && ipc->ops.is_dsp_busy(ipc->dsp)) { - dev_dbg(ipc->dev, "ipc_tx_msgs dsp busy\n"); - spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); - return; - } + while (!list_empty(&ipc->tx_list) && !ipc->pending) { + /* if the DSP is busy, we will TX messages after IRQ. + * also postpone if we are in the middle of processing + * completion irq + */ + if (ipc->ops.is_dsp_busy && ipc->ops.is_dsp_busy(ipc->dsp)) { + dev_dbg(ipc->dev, "ipc_tx_msgs dsp busy\n"); + break; + } - msg = list_first_entry(&ipc->tx_list, struct ipc_message, list); - list_move(&msg->list, &ipc->rx_list); + msg = list_first_entry(&ipc->tx_list, struct ipc_message, list); + list_move(&msg->list, &ipc->rx_list); - if (ipc->ops.tx_msg != NULL) - ipc->ops.tx_msg(ipc, msg); + if (ipc->ops.tx_msg != NULL) + ipc->ops.tx_msg(ipc, msg); + } - spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + spin_unlock_irq(&ipc->dsp->spinlock); } int sst_ipc_tx_message_wait(struct sst_generic_ipc *ipc, u64 header, void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes) { - return ipc_tx_message(ipc, header, tx_data, tx_bytes, + int ret; + + /* + * DSP maybe in lower power active state, so + * check if the DSP supports DSP lp On method + * if so invoke that before sending IPC + */ + if (ipc->ops.check_dsp_lp_on) + if (ipc->ops.check_dsp_lp_on(ipc->dsp, true)) + return -EIO; + + ret = ipc_tx_message(ipc, header, tx_data, tx_bytes, rx_data, rx_bytes, 1); + + if (ipc->ops.check_dsp_lp_on) + if (ipc->ops.check_dsp_lp_on(ipc->dsp, false)) + return -EIO; + + return ret; } EXPORT_SYMBOL_GPL(sst_ipc_tx_message_wait); @@ -203,6 +215,14 @@ int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc, u64 header, } EXPORT_SYMBOL_GPL(sst_ipc_tx_message_nowait); +int sst_ipc_tx_message_nopm(struct sst_generic_ipc *ipc, u64 header, + void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes) +{ + return ipc_tx_message(ipc, header, tx_data, tx_bytes, + rx_data, rx_bytes, 1); +} +EXPORT_SYMBOL_GPL(sst_ipc_tx_message_nopm); + struct ipc_message *sst_ipc_reply_find_msg(struct sst_generic_ipc *ipc, u64 header) { @@ -280,19 +300,7 @@ int sst_ipc_init(struct sst_generic_ipc *ipc) if (ret < 0) return -ENOMEM; - /* start the IPC message thread */ - init_kthread_worker(&ipc->kworker); - ipc->tx_thread = kthread_run(kthread_worker_fn, - &ipc->kworker, "%s", - dev_name(ipc->dev)); - if (IS_ERR(ipc->tx_thread)) { - dev_err(ipc->dev, "error: failed to create message TX task\n"); - ret = PTR_ERR(ipc->tx_thread); - kfree(ipc->msg); - return ret; - } - - init_kthread_work(&ipc->kwork, ipc_tx_msgs); + INIT_WORK(&ipc->kwork, ipc_tx_msgs); return 0; } EXPORT_SYMBOL_GPL(sst_ipc_init); @@ -301,8 +309,7 @@ void sst_ipc_fini(struct sst_generic_ipc *ipc) { int i; - if (ipc->tx_thread) - kthread_stop(ipc->tx_thread); + cancel_work_sync(&ipc->kwork); if (ipc->msg) { for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) { diff --git a/sound/soc/intel/common/sst-ipc.h b/sound/soc/intel/common/sst-ipc.h index ceb7e468a3fa..7ed42a640ad6 100644 --- a/sound/soc/intel/common/sst-ipc.h +++ b/sound/soc/intel/common/sst-ipc.h @@ -23,7 +23,6 @@ #include <linux/list.h> #include <linux/workqueue.h> #include <linux/sched.h> -#include <linux/kthread.h> #define IPC_MAX_MAILBOX_BYTES 256 @@ -52,6 +51,7 @@ struct sst_plat_ipc_ops { void (*tx_data_copy)(struct ipc_message *, char *, size_t); u64 (*reply_msg_match)(u64 header, u64 *mask); bool (*is_dsp_busy)(struct sst_dsp *dsp); + int (*check_dsp_lp_on)(struct sst_dsp *dsp, bool state); }; /* SST generic IPC data */ @@ -65,8 +65,7 @@ struct sst_generic_ipc { struct list_head empty_list; wait_queue_head_t wait_txq; struct task_struct *tx_thread; - struct kthread_worker kworker; - struct kthread_work kwork; + struct work_struct kwork; bool pending; struct ipc_message *msg; int tx_data_max_size; @@ -81,6 +80,9 @@ int sst_ipc_tx_message_wait(struct sst_generic_ipc *ipc, u64 header, int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc, u64 header, void *tx_data, size_t tx_bytes); +int sst_ipc_tx_message_nopm(struct sst_generic_ipc *ipc, u64 header, + void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes); + struct ipc_message *sst_ipc_reply_find_msg(struct sst_generic_ipc *ipc, u64 header); diff --git a/sound/soc/intel/common/sst-match-acpi.c b/sound/soc/intel/common/sst-match-acpi.c index 789843307a49..1070f3ad23e5 100644 --- a/sound/soc/intel/common/sst-match-acpi.c +++ b/sound/soc/intel/common/sst-match-acpi.c @@ -77,5 +77,62 @@ struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines) } EXPORT_SYMBOL_GPL(sst_acpi_find_machine); +static acpi_status sst_acpi_find_package(acpi_handle handle, u32 level, + void *context, void **ret) +{ + struct acpi_device *adev; + acpi_status status = AE_OK; + struct sst_acpi_package_context *pkg_ctx = context; + + pkg_ctx->data_valid = false; + + if (acpi_bus_get_device(handle, &adev)) + return AE_OK; + + if (adev->status.present && adev->status.functional) { + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + union acpi_object *myobj = NULL; + + status = acpi_evaluate_object_typed(handle, pkg_ctx->name, + NULL, &buffer, + ACPI_TYPE_PACKAGE); + if (ACPI_FAILURE(status)) + return AE_OK; + + myobj = buffer.pointer; + if (!myobj || myobj->package.count != pkg_ctx->length) { + kfree(buffer.pointer); + return AE_OK; + } + + status = acpi_extract_package(myobj, + pkg_ctx->format, pkg_ctx->state); + if (ACPI_FAILURE(status)) { + kfree(buffer.pointer); + return AE_OK; + } + + kfree(buffer.pointer); + pkg_ctx->data_valid = true; + return AE_CTRL_TERMINATE; + } + + return AE_OK; +} + +bool sst_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN], + struct sst_acpi_package_context *ctx) +{ + acpi_status status; + + status = acpi_get_devices(hid, sst_acpi_find_package, ctx, NULL); + + if (ACPI_FAILURE(status) || !ctx->data_valid) + return false; + + return true; +} +EXPORT_SYMBOL_GPL(sst_acpi_find_package_from_hid); + MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/haswell/sst-haswell-ipc.c b/sound/soc/intel/haswell/sst-haswell-ipc.c index 91565229d074..a3459d1682a6 100644 --- a/sound/soc/intel/haswell/sst-haswell-ipc.c +++ b/sound/soc/intel/haswell/sst-haswell-ipc.c @@ -26,7 +26,6 @@ #include <linux/delay.h> #include <linux/sched.h> #include <linux/platform_device.h> -#include <linux/kthread.h> #include <linux/firmware.h> #include <linux/dma-mapping.h> #include <linux/debugfs.h> @@ -818,7 +817,7 @@ static irqreturn_t hsw_irq_thread(int irq, void *context) spin_unlock_irqrestore(&sst->spinlock, flags); /* continue to send any remaining messages... */ - queue_kthread_work(&ipc->kworker, &ipc->kwork); + schedule_work(&ipc->kwork); return IRQ_HANDLED; } diff --git a/sound/soc/intel/haswell/sst-haswell-pcm.c b/sound/soc/intel/haswell/sst-haswell-pcm.c index 3154525c2b83..9e4094e2c6e3 100644 --- a/sound/soc/intel/haswell/sst-haswell-pcm.c +++ b/sound/soc/intel/haswell/sst-haswell-pcm.c @@ -871,7 +871,7 @@ out: return ret; } -static struct snd_pcm_ops hsw_pcm_ops = { +static const struct snd_pcm_ops hsw_pcm_ops = { .open = hsw_pcm_open, .close = hsw_pcm_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index 2663781278aa..1f9f33d34000 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -23,6 +23,7 @@ #include "../common/sst-dsp.h" #include "../common/sst-dsp-priv.h" #include "skl-sst-ipc.h" +#include "skl-tplg-interface.h" #define BXT_BASEFW_TIMEOUT 3000 #define BXT_INIT_TIMEOUT 500 @@ -40,11 +41,76 @@ #define BXT_INSTANCE_ID 0 #define BXT_BASE_FW_MODULE_ID 0 +#define BXT_ADSP_FW_BIN_HDR_OFFSET 0x2000 + +/* Delay before scheduling D0i3 entry */ +#define BXT_D0I3_DELAY 5000 + static unsigned int bxt_get_errorcode(struct sst_dsp *ctx) { return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE); } +static int +bxt_load_library(struct sst_dsp *ctx, struct skl_dfw_manifest *minfo) +{ + struct snd_dma_buffer dmab; + struct skl_sst *skl = ctx->thread_context; + const struct firmware *fw = NULL; + struct firmware stripped_fw; + int ret = 0, i, dma_id, stream_tag; + + /* library indices start from 1 to N. 0 represents base FW */ + for (i = 1; i < minfo->lib_count; i++) { + ret = request_firmware(&fw, minfo->lib[i].name, ctx->dev); + if (ret < 0) { + dev_err(ctx->dev, "Request lib %s failed:%d\n", + minfo->lib[i].name, ret); + return ret; + } + + if (skl->is_first_boot) { + ret = snd_skl_parse_uuids(ctx, fw, + BXT_ADSP_FW_BIN_HDR_OFFSET, i); + if (ret < 0) + goto load_library_failed; + } + + stripped_fw.data = fw->data; + stripped_fw.size = fw->size; + skl_dsp_strip_extended_manifest(&stripped_fw); + + stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, + stripped_fw.size, &dmab); + if (stream_tag <= 0) { + dev_err(ctx->dev, "Lib prepare DMA err: %x\n", + stream_tag); + ret = stream_tag; + goto load_library_failed; + } + + dma_id = stream_tag - 1; + memcpy(dmab.area, stripped_fw.data, stripped_fw.size); + + ctx->dsp_ops.trigger(ctx->dev, true, stream_tag); + ret = skl_sst_ipc_load_library(&skl->ipc, dma_id, i); + if (ret < 0) + dev_err(ctx->dev, "IPC Load Lib for %s fail: %d\n", + minfo->lib[i].name, ret); + + ctx->dsp_ops.trigger(ctx->dev, false, stream_tag); + ctx->dsp_ops.cleanup(ctx->dev, &dmab, stream_tag); + release_firmware(fw); + fw = NULL; + } + + return ret; + +load_library_failed: + release_firmware(fw); + return ret; +} + /* * First boot sequence has some extra steps. Core 0 waits for power * status on core 1, so power up core 1 also momentarily, keep it in @@ -157,8 +223,6 @@ static int sst_transfer_fw_host_dma(struct sst_dsp *ctx) return ret; } -#define BXT_ADSP_FW_BIN_HDR_OFFSET 0x2000 - static int bxt_load_base_firmware(struct sst_dsp *ctx) { struct firmware stripped_fw; @@ -175,9 +239,12 @@ static int bxt_load_base_firmware(struct sst_dsp *ctx) if (ctx->fw == NULL) goto sst_load_base_firmware_failed; - ret = snd_skl_parse_uuids(ctx, BXT_ADSP_FW_BIN_HDR_OFFSET); - if (ret < 0) - goto sst_load_base_firmware_failed; + /* prase uuids on first boot */ + if (skl->is_first_boot) { + ret = snd_skl_parse_uuids(ctx, ctx->fw, BXT_ADSP_FW_BIN_HDR_OFFSET, 0); + if (ret < 0) + goto sst_load_base_firmware_failed; + } stripped_fw.data = ctx->fw->data; stripped_fw.size = ctx->fw->size; @@ -224,18 +291,164 @@ sst_load_base_firmware_failed: return ret; } +/* + * Decide the D0i3 state that can be targeted based on the usecase + * ref counts and DSP state + * + * Decision Matrix: (X= dont care; state = target state) + * + * DSP state != SKL_DSP_RUNNING ; state = no d0i3 + * + * DSP state == SKL_DSP_RUNNING , the following matrix applies + * non_d0i3 >0; streaming =X; non_streaming =X; state = no d0i3 + * non_d0i3 =X; streaming =0; non_streaming =0; state = no d0i3 + * non_d0i3 =0; streaming >0; non_streaming =X; state = streaming d0i3 + * non_d0i3 =0; streaming =0; non_streaming =X; state = non-streaming d0i3 + */ +static int bxt_d0i3_target_state(struct sst_dsp *ctx) +{ + struct skl_sst *skl = ctx->thread_context; + struct skl_d0i3_data *d0i3 = &skl->d0i3; + + if (skl->cores.state[SKL_DSP_CORE0_ID] != SKL_DSP_RUNNING) + return SKL_DSP_D0I3_NONE; + + if (d0i3->non_d0i3) + return SKL_DSP_D0I3_NONE; + else if (d0i3->streaming) + return SKL_DSP_D0I3_STREAMING; + else if (d0i3->non_streaming) + return SKL_DSP_D0I3_NON_STREAMING; + else + return SKL_DSP_D0I3_NONE; +} + +static void bxt_set_dsp_D0i3(struct work_struct *work) +{ + int ret; + struct skl_ipc_d0ix_msg msg; + struct skl_sst *skl = container_of(work, + struct skl_sst, d0i3.work.work); + struct sst_dsp *ctx = skl->dsp; + struct skl_d0i3_data *d0i3 = &skl->d0i3; + int target_state; + + dev_dbg(ctx->dev, "In %s:\n", __func__); + + /* D0i3 entry allowed only if core 0 alone is running */ + if (skl_dsp_get_enabled_cores(ctx) != SKL_DSP_CORE0_MASK) { + dev_warn(ctx->dev, + "D0i3 allowed when only core0 running:Exit\n"); + return; + } + + target_state = bxt_d0i3_target_state(ctx); + if (target_state == SKL_DSP_D0I3_NONE) + return; + + msg.instance_id = 0; + msg.module_id = 0; + msg.wake = 1; + msg.streaming = 0; + if (target_state == SKL_DSP_D0I3_STREAMING) + msg.streaming = 1; + + ret = skl_ipc_set_d0ix(&skl->ipc, &msg); + + if (ret < 0) { + dev_err(ctx->dev, "Failed to set DSP to D0i3 state\n"); + return; + } + + /* Set Vendor specific register D0I3C.I3 to enable D0i3*/ + if (skl->update_d0i3c) + skl->update_d0i3c(skl->dev, true); + + d0i3->state = target_state; + skl->cores.state[SKL_DSP_CORE0_ID] = SKL_DSP_RUNNING_D0I3; +} + +static int bxt_schedule_dsp_D0i3(struct sst_dsp *ctx) +{ + struct skl_sst *skl = ctx->thread_context; + struct skl_d0i3_data *d0i3 = &skl->d0i3; + + /* Schedule D0i3 only if the usecase ref counts are appropriate */ + if (bxt_d0i3_target_state(ctx) != SKL_DSP_D0I3_NONE) { + + dev_dbg(ctx->dev, "%s: Schedule D0i3\n", __func__); + + schedule_delayed_work(&d0i3->work, + msecs_to_jiffies(BXT_D0I3_DELAY)); + } + + return 0; +} + +static int bxt_set_dsp_D0i0(struct sst_dsp *ctx) +{ + int ret; + struct skl_ipc_d0ix_msg msg; + struct skl_sst *skl = ctx->thread_context; + + dev_dbg(ctx->dev, "In %s:\n", __func__); + + /* First Cancel any pending attempt to put DSP to D0i3 */ + cancel_delayed_work_sync(&skl->d0i3.work); + + /* If DSP is currently in D0i3, bring it to D0i0 */ + if (skl->cores.state[SKL_DSP_CORE0_ID] != SKL_DSP_RUNNING_D0I3) + return 0; + + dev_dbg(ctx->dev, "Set DSP to D0i0\n"); + + msg.instance_id = 0; + msg.module_id = 0; + msg.streaming = 0; + msg.wake = 0; + + if (skl->d0i3.state == SKL_DSP_D0I3_STREAMING) + msg.streaming = 1; + + /* Clear Vendor specific register D0I3C.I3 to disable D0i3*/ + if (skl->update_d0i3c) + skl->update_d0i3c(skl->dev, false); + + ret = skl_ipc_set_d0ix(&skl->ipc, &msg); + if (ret < 0) { + dev_err(ctx->dev, "Failed to set DSP to D0i0\n"); + return ret; + } + + skl->cores.state[SKL_DSP_CORE0_ID] = SKL_DSP_RUNNING; + skl->d0i3.state = SKL_DSP_D0I3_NONE; + + return 0; +} + static int bxt_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id) { struct skl_sst *skl = ctx->thread_context; int ret; struct skl_ipc_dxstate_info dx; unsigned int core_mask = SKL_DSP_CORE_MASK(core_id); + struct skl_dfw_manifest *minfo = &skl->manifest; if (skl->fw_loaded == false) { skl->boot_complete = false; ret = bxt_load_base_firmware(ctx); - if (ret < 0) + if (ret < 0) { dev_err(ctx->dev, "reload fw failed: %d\n", ret); + return ret; + } + + if (minfo->lib_count > 1) { + ret = bxt_load_library(ctx, minfo); + if (ret < 0) { + dev_err(ctx->dev, "reload libs failed: %d\n", ret); + return ret; + } + } return ret; } @@ -329,7 +542,7 @@ static int bxt_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id) ret = skl_dsp_disable_core(ctx, core_mask); if (ret < 0) { - dev_err(ctx->dev, "Failed to disable core %d", ret); + dev_err(ctx->dev, "Failed to disable core %d\n", ret); return ret; } skl->cores.state[core_id] = SKL_DSP_RESET; @@ -339,8 +552,11 @@ static int bxt_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id) static struct skl_dsp_fw_ops bxt_fw_ops = { .set_state_D0 = bxt_set_dsp_D0, .set_state_D3 = bxt_set_dsp_D3, + .set_state_D0i3 = bxt_schedule_dsp_D0i3, + .set_state_D0i0 = bxt_set_dsp_D0i0, .load_fw = bxt_load_base_firmware, .get_fw_errcode = bxt_get_errorcode, + .load_library = bxt_load_library, }; static struct sst_ops skl_ops = { @@ -394,25 +610,48 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, if (ret) return ret; + /* set the D0i3 check */ + skl->ipc.ops.check_dsp_lp_on = skl_ipc_check_D0i0; + skl->cores.count = 2; skl->boot_complete = false; init_waitqueue_head(&skl->boot_wait); + skl->is_first_boot = true; + INIT_DELAYED_WORK(&skl->d0i3.work, bxt_set_dsp_D0i3); + skl->d0i3.state = SKL_DSP_D0I3_NONE; + + if (dsp) + *dsp = skl; + + return 0; +} +EXPORT_SYMBOL_GPL(bxt_sst_dsp_init); + +int bxt_sst_init_fw(struct device *dev, struct skl_sst *ctx) +{ + int ret; + struct sst_dsp *sst = ctx->dsp; ret = sst->fw_ops.load_fw(sst); if (ret < 0) { - dev_err(dev, "Load base fw failed: %x", ret); + dev_err(dev, "Load base fw failed: %x\n", ret); return ret; } skl_dsp_init_core_state(sst); - if (dsp) - *dsp = skl; + if (ctx->manifest.lib_count > 1) { + ret = sst->fw_ops.load_library(sst, &ctx->manifest); + if (ret < 0) { + dev_err(dev, "Load Library failed : %x\n", ret); + return ret; + } + } + ctx->is_first_boot = false; return 0; } -EXPORT_SYMBOL_GPL(bxt_sst_dsp_init); - +EXPORT_SYMBOL_GPL(bxt_sst_init_fw); void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx) { diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 44ab595ce21a..e79cbcf6e462 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -203,32 +203,35 @@ static const struct skl_dsp_ops dsp_ops[] = { .id = 0x9d70, .loader_ops = skl_get_loader_ops, .init = skl_sst_dsp_init, + .init_fw = skl_sst_init_fw, .cleanup = skl_sst_dsp_cleanup }, { .id = 0x9d71, .loader_ops = skl_get_loader_ops, .init = skl_sst_dsp_init, + .init_fw = skl_sst_init_fw, .cleanup = skl_sst_dsp_cleanup }, { .id = 0x5a98, .loader_ops = bxt_get_loader_ops, .init = bxt_sst_dsp_init, + .init_fw = bxt_sst_init_fw, .cleanup = bxt_sst_dsp_cleanup }, }; -static int skl_get_dsp_ops(int pci_id) +const struct skl_dsp_ops *skl_get_dsp_ops(int pci_id) { int i; for (i = 0; i < ARRAY_SIZE(dsp_ops); i++) { if (dsp_ops[i].id == pci_id) - return i; + return &dsp_ops[i]; } - return -EINVAL; + return NULL; } int skl_init_dsp(struct skl *skl) @@ -238,7 +241,8 @@ int skl_init_dsp(struct skl *skl) struct hdac_bus *bus = ebus_to_hbus(ebus); struct skl_dsp_loader_ops loader_ops; int irq = bus->irq; - int ret, index; + const struct skl_dsp_ops *ops; + int ret; /* enable ppcap interrupt */ snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true); @@ -251,18 +255,18 @@ int skl_init_dsp(struct skl *skl) return -ENXIO; } - index = skl_get_dsp_ops(skl->pci->device); - if (index < 0) - return -EINVAL; + ops = skl_get_dsp_ops(skl->pci->device); + if (!ops) + return -EIO; - loader_ops = dsp_ops[index].loader_ops(); - ret = dsp_ops[index].init(bus->dev, mmio_base, irq, - skl->fw_name, loader_ops, &skl->skl_sst); + loader_ops = ops->loader_ops(); + ret = ops->init(bus->dev, mmio_base, irq, + skl->fw_name, loader_ops, + &skl->skl_sst); if (ret < 0) return ret; - skl_dsp_enable_notification(skl->skl_sst, false); dev_dbg(bus->dev, "dsp registration status=%d\n", ret); return ret; @@ -273,16 +277,16 @@ int skl_free_dsp(struct skl *skl) struct hdac_ext_bus *ebus = &skl->ebus; struct hdac_bus *bus = ebus_to_hbus(ebus); struct skl_sst *ctx = skl->skl_sst; - int index; + const struct skl_dsp_ops *ops; /* disable ppcap interrupt */ snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, false); - index = skl_get_dsp_ops(skl->pci->device); - if (index < 0) + ops = skl_get_dsp_ops(skl->pci->device); + if (!ops) return -EIO; - dsp_ops[index].cleanup(bus->dev, ctx); + ops->cleanup(bus->dev, ctx); if (ctx->dsp->addr.lpe) iounmap(ctx->dsp->addr.lpe); @@ -290,13 +294,40 @@ int skl_free_dsp(struct skl *skl) return 0; } +/* + * In the case of "suspend_active" i.e, the Audio IP being active + * during system suspend, immediately excecute any pending D0i3 work + * before suspending. This is needed for the IP to work in low power + * mode during system suspend. In the case of normal suspend, cancel + * any pending D0i3 work. + */ +int skl_suspend_late_dsp(struct skl *skl) +{ + struct skl_sst *ctx = skl->skl_sst; + struct delayed_work *dwork; + + if (!ctx) + return 0; + + dwork = &ctx->d0i3.work; + + if (dwork->work.func) { + if (skl->supend_active) + flush_delayed_work(dwork); + else + cancel_delayed_work_sync(dwork); + } + + return 0; +} + int skl_suspend_dsp(struct skl *skl) { struct skl_sst *ctx = skl->skl_sst; int ret; /* if ppcap is not supported return 0 */ - if (!skl->ebus.ppcap) + if (!skl->ebus.bus.ppcap) return 0; ret = skl_dsp_sleep(ctx->dsp); @@ -316,13 +347,17 @@ int skl_resume_dsp(struct skl *skl) int ret; /* if ppcap is not supported return 0 */ - if (!skl->ebus.ppcap) + if (!skl->ebus.bus.ppcap) return 0; /* enable ppcap interrupt */ snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true); snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, true); + /* check if DSP 1st boot is done */ + if (skl->skl_sst->is_first_boot == true) + return 0; + ret = skl_dsp_wake(ctx->dsp); if (ret < 0) return ret; @@ -492,16 +527,14 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx, int skl_dsp_set_dma_control(struct skl_sst *ctx, struct skl_module_cfg *mconfig) { struct skl_dma_control *dma_ctrl; - struct skl_i2s_config_blob config_blob; struct skl_ipc_large_config_msg msg = {0}; int err = 0; /* - * if blob size is same as capablity size, then no dma control - * present so return + * if blob size zero, then return */ - if (mconfig->formats_config.caps_size == sizeof(config_blob)) + if (mconfig->formats_config.caps_size == 0) return 0; msg.large_param_id = DMA_CONTROL_ID; @@ -515,7 +548,7 @@ int skl_dsp_set_dma_control(struct skl_sst *ctx, struct skl_module_cfg *mconfig) dma_ctrl->node_id = skl_get_node_id(ctx, mconfig); /* size in dwords */ - dma_ctrl->config_length = sizeof(config_blob) / 4; + dma_ctrl->config_length = mconfig->formats_config.caps_size / 4; memcpy(dma_ctrl->config_data, mconfig->formats_config.caps, mconfig->formats_config.caps_size); @@ -523,7 +556,6 @@ int skl_dsp_set_dma_control(struct skl_sst *ctx, struct skl_module_cfg *mconfig) err = skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)dma_ctrl); kfree(dma_ctrl); - return err; } @@ -672,6 +704,7 @@ static u16 skl_get_module_param_size(struct skl_sst *ctx, return param_size; case SKL_MODULE_TYPE_BASE_OUTFMT: + case SKL_MODULE_TYPE_KPB: return sizeof(struct skl_base_outfmt_cfg); default: @@ -725,6 +758,7 @@ static int skl_set_module_format(struct skl_sst *ctx, break; case SKL_MODULE_TYPE_BASE_OUTFMT: + case SKL_MODULE_TYPE_KPB: skl_set_base_outfmt_format(ctx, module_config, *param_data); break; @@ -779,6 +813,7 @@ static int skl_alloc_queue(struct skl_module_pin *mpin, mpin[i].in_use = true; mpin[i].id.module_id = id.module_id; mpin[i].id.instance_id = id.instance_id; + mpin[i].id.pvt_id = id.pvt_id; mpin[i].tgt_mcfg = tgt_cfg; return i; } @@ -802,6 +837,7 @@ static void skl_free_queue(struct skl_module_pin *mpin, int q_index) mpin[q_index].in_use = false; mpin[q_index].id.module_id = 0; mpin[q_index].id.instance_id = 0; + mpin[q_index].id.pvt_id = 0; } mpin[q_index].pin_state = SKL_PIN_UNBIND; mpin[q_index].tgt_mcfg = NULL; @@ -842,7 +878,7 @@ int skl_init_module(struct skl_sst *ctx, struct skl_ipc_init_instance_msg msg; dev_dbg(ctx->dev, "%s: module_id = %d instance=%d\n", __func__, - mconfig->id.module_id, mconfig->id.instance_id); + mconfig->id.module_id, mconfig->id.pvt_id); if (mconfig->pipe->state != SKL_PIPE_CREATED) { dev_err(ctx->dev, "Pipe not created state= %d pipe_id= %d\n", @@ -858,10 +894,11 @@ int skl_init_module(struct skl_sst *ctx, } msg.module_id = mconfig->id.module_id; - msg.instance_id = mconfig->id.instance_id; + msg.instance_id = mconfig->id.pvt_id; msg.ppl_instance_id = mconfig->pipe->ppl_id; msg.param_data_size = module_config_size; msg.core_id = mconfig->core_id; + msg.domain = mconfig->domain; ret = skl_ipc_init_instance(&ctx->ipc, &msg, param_data); if (ret < 0) { @@ -878,9 +915,9 @@ static void skl_dump_bind_info(struct skl_sst *ctx, struct skl_module_cfg *src_module, struct skl_module_cfg *dst_module) { dev_dbg(ctx->dev, "%s: src module_id = %d src_instance=%d\n", - __func__, src_module->id.module_id, src_module->id.instance_id); + __func__, src_module->id.module_id, src_module->id.pvt_id); dev_dbg(ctx->dev, "%s: dst_module=%d dst_instacne=%d\n", __func__, - dst_module->id.module_id, dst_module->id.instance_id); + dst_module->id.module_id, dst_module->id.pvt_id); dev_dbg(ctx->dev, "src_module state = %d dst module state = %d\n", src_module->m_state, dst_module->m_state); @@ -927,9 +964,9 @@ int skl_unbind_modules(struct skl_sst *ctx, return 0; msg.module_id = src_mcfg->id.module_id; - msg.instance_id = src_mcfg->id.instance_id; + msg.instance_id = src_mcfg->id.pvt_id; msg.dst_module_id = dst_mcfg->id.module_id; - msg.dst_instance_id = dst_mcfg->id.instance_id; + msg.dst_instance_id = dst_mcfg->id.pvt_id; msg.bind = false; ret = skl_ipc_bind_unbind(&ctx->ipc, &msg); @@ -988,9 +1025,9 @@ int skl_bind_modules(struct skl_sst *ctx, msg.src_queue, msg.dst_queue); msg.module_id = src_mcfg->id.module_id; - msg.instance_id = src_mcfg->id.instance_id; + msg.instance_id = src_mcfg->id.pvt_id; msg.dst_module_id = dst_mcfg->id.module_id; - msg.dst_instance_id = dst_mcfg->id.instance_id; + msg.dst_instance_id = dst_mcfg->id.pvt_id; msg.bind = true; ret = skl_ipc_bind_unbind(&ctx->ipc, &msg); @@ -1029,7 +1066,8 @@ int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe) dev_dbg(ctx->dev, "%s: pipe_id = %d\n", __func__, pipe->ppl_id); ret = skl_ipc_create_pipeline(&ctx->ipc, pipe->memory_pages, - pipe->pipe_priority, pipe->ppl_id); + pipe->pipe_priority, pipe->ppl_id, + pipe->lp_mode); if (ret < 0) { dev_err(ctx->dev, "Failed to create pipeline\n"); return ret; @@ -1168,7 +1206,7 @@ int skl_set_module_params(struct skl_sst *ctx, u32 *params, int size, struct skl_ipc_large_config_msg msg; msg.module_id = mcfg->id.module_id; - msg.instance_id = mcfg->id.instance_id; + msg.instance_id = mcfg->id.pvt_id; msg.param_data_size = size; msg.large_param_id = param_id; @@ -1181,7 +1219,7 @@ int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size, struct skl_ipc_large_config_msg msg; msg.module_id = mcfg->id.module_id; - msg.instance_id = mcfg->id.instance_id; + msg.instance_id = mcfg->id.pvt_id; msg.param_data_size = size; msg.large_param_id = param_id; diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 6e05bf8622f7..6c6b63a6b338 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -106,7 +106,7 @@ static void skl_set_pcm_constrains(struct hdac_ext_bus *ebus, static enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_ext_bus *ebus) { - if (ebus->ppcap) + if ((ebus_to_hbus(ebus))->ppcap) return HDAC_EXT_STREAM_TYPE_HOST; else return HDAC_EXT_STREAM_TYPE_COUPLED; @@ -144,6 +144,8 @@ static int skl_pcm_open(struct snd_pcm_substream *substream, struct hdac_ext_stream *stream; struct snd_pcm_runtime *runtime = substream->runtime; struct skl_dma_params *dma_params; + struct skl *skl = get_skl_ctx(dai->dev); + struct skl_module_cfg *mconfig; dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); @@ -177,6 +179,12 @@ static int skl_pcm_open(struct snd_pcm_substream *substream, skl_set_suspend_active(substream, dai, true); snd_pcm_set_sync(substream); + mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); + if (!mconfig) + return -EINVAL; + + skl_tplg_d0i3_get(skl, mconfig->d0i3_caps); + return 0; } @@ -188,7 +196,7 @@ static int skl_get_format(struct snd_pcm_substream *substream, struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); int format_val = 0; - if (ebus->ppcap) { + if ((ebus_to_hbus(ebus))->ppcap) { struct snd_pcm_runtime *runtime = substream->runtime; format_val = snd_hdac_calc_stream_format(runtime->rate, @@ -302,6 +310,7 @@ static void skl_pcm_close(struct snd_pcm_substream *substream, struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); struct skl_dma_params *dma_params = NULL; struct skl *skl = ebus_to_skl(ebus); + struct skl_module_cfg *mconfig; dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); @@ -325,6 +334,9 @@ static void skl_pcm_close(struct snd_pcm_substream *substream, skl->skl_sst->miscbdcg_disabled = false; } + mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); + skl_tplg_d0i3_put(skl, mconfig->d0i3_caps); + kfree(dma_params); } @@ -648,7 +660,8 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .channels_min = HDA_MONO, .channels_max = HDA_STEREO, .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_8000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, }, .capture = { .stream_name = "System Capture", @@ -1020,7 +1033,7 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream, { struct hdac_ext_bus *ebus = get_bus_ctx(substream); - if (!ebus->ppcap) + if (!(ebus_to_hbus(ebus))->ppcap) return skl_coupled_trigger(substream, cmd); return 0; @@ -1030,10 +1043,24 @@ static snd_pcm_uframes_t skl_platform_pcm_pointer (struct snd_pcm_substream *substream) { struct hdac_ext_stream *hstream = get_hdac_ext_stream(substream); + struct hdac_ext_bus *ebus = get_bus_ctx(substream); unsigned int pos; - /* use the position buffer as default */ - pos = snd_hdac_stream_get_pos_posbuf(hdac_stream(hstream)); + /* + * Use DPIB for Playback stream as the periodic DMA Position-in- + * Buffer Writes may be scheduled at the same time or later than + * the MSI and does not guarantee to reflect the Position of the + * last buffer that was transferred. Whereas DPIB register in + * HAD space reflects the actual data that is transferred. + * Use the position buffer for capture, as DPIB write gets + * completed earlier than the actual data written to the DDR. + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + pos = readl(ebus->bus.remap_addr + AZX_REG_VS_SDXDPIB_XBASE + + (AZX_REG_VS_SDXDPIB_XINTERVAL * + hdac_stream(hstream)->index)); + else + pos = snd_hdac_stream_get_pos_posbuf(hdac_stream(hstream)); if (pos >= hdac_stream(hstream)->bufsize) pos = 0; @@ -1093,7 +1120,7 @@ static int skl_get_time_info(struct snd_pcm_substream *substream, return 0; } -static struct snd_pcm_ops skl_platform_ops = { +static const struct snd_pcm_ops skl_platform_ops = { .open = skl_platform_open, .ioctl = snd_pcm_lib_ioctl, .trigger = skl_platform_pcm_trigger, @@ -1138,20 +1165,68 @@ static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd) return retval; } +static int skl_populate_modules(struct skl *skl) +{ + struct skl_pipeline *p; + struct skl_pipe_module *m; + struct snd_soc_dapm_widget *w; + struct skl_module_cfg *mconfig; + int ret; + + list_for_each_entry(p, &skl->ppl_list, node) { + list_for_each_entry(m, &p->pipe->w_list, node) { + + w = m->w; + mconfig = w->priv; + + ret = snd_skl_get_module_info(skl->skl_sst, mconfig); + if (ret < 0) { + dev_err(skl->skl_sst->dev, + "query module info failed:%d\n", ret); + goto err; + } + } + } +err: + return ret; +} + static int skl_platform_soc_probe(struct snd_soc_platform *platform) { struct hdac_ext_bus *ebus = dev_get_drvdata(platform->dev); struct skl *skl = ebus_to_skl(ebus); + const struct skl_dsp_ops *ops; int ret; - if (ebus->ppcap) { + pm_runtime_get_sync(platform->dev); + if ((ebus_to_hbus(ebus))->ppcap) { ret = skl_tplg_init(platform, ebus); if (ret < 0) { dev_err(platform->dev, "Failed to init topology!\n"); return ret; } skl->platform = platform; + + /* load the firmwares, since all is set */ + ops = skl_get_dsp_ops(skl->pci->device); + if (!ops) + return -EIO; + + if (skl->skl_sst->is_first_boot == false) { + dev_err(platform->dev, "DSP reports first boot done!!!\n"); + return -EIO; + } + + ret = ops->init_fw(platform->dev, skl->skl_sst); + if (ret < 0) { + dev_err(platform->dev, "Failed to boot first fw: %d\n", ret); + return ret; + } + skl_populate_modules(skl); + skl->skl_sst->update_d0i3c = skl_update_d0i3c; } + pm_runtime_mark_last_busy(platform->dev); + pm_runtime_put_autosuspend(platform->dev); return 0; } diff --git a/sound/soc/intel/skylake/skl-sst-cldma.c b/sound/soc/intel/skylake/skl-sst-cldma.c index da2329d17f4d..c9f6d87381db 100644 --- a/sound/soc/intel/skylake/skl-sst-cldma.c +++ b/sound/soc/intel/skylake/skl-sst-cldma.c @@ -17,7 +17,6 @@ #include <linux/device.h> #include <linux/mm.h> -#include <linux/kthread.h> #include <linux/delay.h> #include "../common/sst-dsp.h" #include "../common/sst-dsp-priv.h" @@ -341,14 +340,14 @@ int skl_cldma_prepare(struct sst_dsp *ctx) ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev, &ctx->cl_dev.dmab_data, ctx->cl_dev.bufsize); if (ret < 0) { - dev_err(ctx->dev, "Alloc buffer for base fw failed: %x", ret); + dev_err(ctx->dev, "Alloc buffer for base fw failed: %x\n", ret); return ret; } /* Setup Code loader BDL */ ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev, &ctx->cl_dev.dmab_bdl, PAGE_SIZE); if (ret < 0) { - dev_err(ctx->dev, "Alloc buffer for blde failed: %x", ret); + dev_err(ctx->dev, "Alloc buffer for blde failed: %x\n", ret); ctx->dsp_ops.free_dma_buf(ctx->dev, &ctx->cl_dev.dmab_data); return ret; } diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index 0f8629ef79ac..7c272ba0f4b5 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -20,6 +20,7 @@ #include <sound/memalloc.h> #include "skl-sst-cldma.h" #include "skl-tplg-interface.h" +#include "skl-topology.h" struct sst_dsp; struct skl_sst; @@ -125,17 +126,31 @@ struct sst_dsp_device; #define SKL_ADSPCS_CPA_SHIFT 24 #define SKL_ADSPCS_CPA_MASK(cm) ((cm) << SKL_ADSPCS_CPA_SHIFT) +/* DSP Core state */ enum skl_dsp_states { SKL_DSP_RUNNING = 1, + /* Running in D0i3 state; can be in streaming or non-streaming D0i3 */ + SKL_DSP_RUNNING_D0I3, /* Running in D0i3 state*/ SKL_DSP_RESET, }; +/* D0i3 substates */ +enum skl_dsp_d0i3_states { + SKL_DSP_D0I3_NONE = -1, /* No D0i3 */ + SKL_DSP_D0I3_NON_STREAMING = 0, + SKL_DSP_D0I3_STREAMING = 1, +}; + struct skl_dsp_fw_ops { int (*load_fw)(struct sst_dsp *ctx); /* FW module parser/loader */ + int (*load_library)(struct sst_dsp *ctx, + struct skl_dfw_manifest *minfo); int (*parse_fw)(struct sst_dsp *ctx); int (*set_state_D0)(struct sst_dsp *ctx, unsigned int core_id); int (*set_state_D3)(struct sst_dsp *ctx, unsigned int core_id); + int (*set_state_D0i3)(struct sst_dsp *ctx); + int (*set_state_D0i0)(struct sst_dsp *ctx); unsigned int (*get_fw_errcode)(struct sst_dsp *ctx); int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, u8 *mod_name); int (*unload_mod)(struct sst_dsp *ctx, u16 mod_id); @@ -203,12 +218,21 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, const char *fw_name, struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp); +int skl_sst_init_fw(struct device *dev, struct skl_sst *ctx); +int bxt_sst_init_fw(struct device *dev, struct skl_sst *ctx); void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx); void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx); -int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid, - struct skl_dfw_module *dfw_config); -int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset); +int snd_skl_get_module_info(struct skl_sst *ctx, + struct skl_module_cfg *mconfig); +int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw, + unsigned int offset, int index); +int skl_get_pvt_id(struct skl_sst *ctx, + struct skl_module_cfg *mconfig); +int skl_put_pvt_id(struct skl_sst *ctx, + struct skl_module_cfg *mconfig); +int skl_get_pvt_instance_id_map(struct skl_sst *ctx, + int module_id, int instance_id); void skl_freeup_uuid_list(struct skl_sst *ctx); int skl_dsp_strip_extended_manifest(struct firmware *fw); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 96f2f6889b18..e1391dfbc9e9 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -81,6 +81,11 @@ #define IPC_INSTANCE_ID(x) (((x) & IPC_INSTANCE_ID_MASK) \ << IPC_INSTANCE_ID_SHIFT) +#define IPC_PPL_LP_MODE_SHIFT 0 +#define IPC_PPL_LP_MODE_MASK 0x1 +#define IPC_PPL_LP_MODE(x) (((x) & IPC_PPL_LP_MODE_MASK) \ + << IPC_PPL_LP_MODE_SHIFT) + /* Set pipeline state message */ #define IPC_PPL_STATE_SHIFT 0 #define IPC_PPL_STATE_MASK 0x1F @@ -114,6 +119,11 @@ #define IPC_CORE_ID(x) (((x) & IPC_CORE_ID_MASK) \ << IPC_CORE_ID_SHIFT) +#define IPC_DOMAIN_SHIFT 28 +#define IPC_DOMAIN_MASK 0x1 +#define IPC_DOMAIN(x) (((x) & IPC_DOMAIN_MASK) \ + << IPC_DOMAIN_SHIFT) + /* Bind/Unbind message extension register */ #define IPC_DST_MOD_ID_SHIFT 0 #define IPC_DST_MOD_ID(x) (((x) & IPC_MOD_ID_MASK) \ @@ -167,6 +177,17 @@ << IPC_INITIAL_BLOCK_SHIFT) #define IPC_INITIAL_BLOCK_CLEAR ~(IPC_INITIAL_BLOCK_MASK \ << IPC_INITIAL_BLOCK_SHIFT) +/* Set D0ix IPC extension register */ +#define IPC_D0IX_WAKE_SHIFT 0 +#define IPC_D0IX_WAKE_MASK 0x1 +#define IPC_D0IX_WAKE(x) (((x) & IPC_D0IX_WAKE_MASK) \ + << IPC_D0IX_WAKE_SHIFT) + +#define IPC_D0IX_STREAMING_SHIFT 1 +#define IPC_D0IX_STREAMING_MASK 0x1 +#define IPC_D0IX_STREAMING(x) (((x) & IPC_D0IX_STREAMING_MASK) \ + << IPC_D0IX_STREAMING_SHIFT) + enum skl_ipc_msg_target { IPC_FW_GEN_MSG = 0, @@ -190,6 +211,7 @@ enum skl_ipc_glb_type { IPC_GLB_GET_PPL_CONTEXT_SIZE = 21, IPC_GLB_SAVE_PPL = 22, IPC_GLB_RESTORE_PPL = 23, + IPC_GLB_LOAD_LIBRARY = 24, IPC_GLB_NOTIFY = 26, IPC_GLB_MAX_IPC_MSG_NUMBER = 31 /* Maximum message number */ }; @@ -252,7 +274,8 @@ enum skl_ipc_module_msg { IPC_MOD_LARGE_CONFIG_SET = 4, IPC_MOD_BIND = 5, IPC_MOD_UNBIND = 6, - IPC_MOD_SET_DX = 7 + IPC_MOD_SET_DX = 7, + IPC_MOD_SET_D0IX = 8 }; static void skl_ipc_tx_data_copy(struct ipc_message *msg, char *tx_data, @@ -283,6 +306,23 @@ static void skl_ipc_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg) header->primary | SKL_ADSP_REG_HIPCI_BUSY); } +int skl_ipc_check_D0i0(struct sst_dsp *dsp, bool state) +{ + int ret; + + /* check D0i3 support */ + if (!dsp->fw_ops.set_state_D0i0) + return 0; + + /* Attempt D0i0 or D0i3 based on state */ + if (state) + ret = dsp->fw_ops.set_state_D0i0(dsp); + else + ret = dsp->fw_ops.set_state_D0i3(dsp); + + return ret; +} + static struct ipc_message *skl_ipc_reply_get_msg(struct sst_generic_ipc *ipc, u64 ipc_header) { @@ -338,7 +378,7 @@ static int skl_ipc_process_notification(struct sst_generic_ipc *ipc, break; default: - dev_err(ipc->dev, "ipc: Unhandled error msg=%x", + dev_err(ipc->dev, "ipc: Unhandled error msg=%x\n", header.primary); break; } @@ -379,13 +419,13 @@ static void skl_ipc_process_reply(struct sst_generic_ipc *ipc, break; default: - dev_err(ipc->dev, "Unknown ipc reply: 0x%x", reply); + dev_err(ipc->dev, "Unknown ipc reply: 0x%x\n", reply); msg->errno = -EINVAL; break; } if (reply != IPC_GLB_REPLY_SUCCESS) { - dev_err(ipc->dev, "ipc FW reply: reply=%d", reply); + dev_err(ipc->dev, "ipc FW reply: reply=%d\n", reply); dev_err(ipc->dev, "FW Error Code: %u\n", ipc->dsp->fw_ops.get_fw_errcode(ipc->dsp)); } @@ -434,9 +474,9 @@ irqreturn_t skl_dsp_irq_thread_handler(int irq, void *context) hipcte = sst_dsp_shim_read_unlocked(dsp, SKL_ADSP_REG_HIPCTE); header.primary = hipct; header.extension = hipcte; - dev_dbg(dsp->dev, "IPC irq: Firmware respond primary:%x", + dev_dbg(dsp->dev, "IPC irq: Firmware respond primary:%x\n", header.primary); - dev_dbg(dsp->dev, "IPC irq: Firmware respond extension:%x", + dev_dbg(dsp->dev, "IPC irq: Firmware respond extension:%x\n", header.extension); if (IPC_GLB_NOTIFY_RSP_TYPE(header.primary)) { @@ -458,7 +498,7 @@ irqreturn_t skl_dsp_irq_thread_handler(int irq, void *context) skl_ipc_int_enable(dsp); /* continue to send any remaining messages... */ - queue_kthread_work(&ipc->kworker, &ipc->kwork); + schedule_work(&ipc->kwork); return IRQ_HANDLED; } @@ -541,7 +581,7 @@ void skl_ipc_free(struct sst_generic_ipc *ipc) } int skl_ipc_create_pipeline(struct sst_generic_ipc *ipc, - u16 ppl_mem_size, u8 ppl_type, u8 instance_id) + u16 ppl_mem_size, u8 ppl_type, u8 instance_id, u8 lp_mode) { struct skl_ipc_header header = {0}; u64 *ipc_header = (u64 *)(&header); @@ -554,6 +594,8 @@ int skl_ipc_create_pipeline(struct sst_generic_ipc *ipc, header.primary |= IPC_PPL_TYPE(ppl_type); header.primary |= IPC_PPL_MEM_SIZE(ppl_mem_size); + header.extension = IPC_PPL_LP_MODE(lp_mode); + dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); if (ret < 0) { @@ -704,6 +746,7 @@ int skl_ipc_init_instance(struct sst_generic_ipc *ipc, header.extension = IPC_CORE_ID(msg->core_id); header.extension |= IPC_PPL_INSTANCE_ID(msg->ppl_instance_id); header.extension |= IPC_PARAM_BLOCK_SIZE(param_block_size); + header.extension |= IPC_DOMAIN(msg->domain); dev_dbg(ipc->dev, "In %s primary =%x ext=%x\n", __func__, header.primary, header.extension); @@ -742,7 +785,7 @@ int skl_ipc_bind_unbind(struct sst_generic_ipc *ipc, header.extension); ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); if (ret < 0) { - dev_err(ipc->dev, "ipc: bind/unbind faileden"); + dev_err(ipc->dev, "ipc: bind/unbind failed\n"); return ret; } @@ -902,3 +945,54 @@ int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, return ret; } EXPORT_SYMBOL_GPL(skl_ipc_get_large_config); + +int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc, + u8 dma_id, u8 table_id) +{ + struct skl_ipc_header header = {0}; + u64 *ipc_header = (u64 *)(&header); + int ret = 0; + + header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG); + header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); + header.primary |= IPC_GLB_TYPE(IPC_GLB_LOAD_LIBRARY); + header.primary |= IPC_MOD_INSTANCE_ID(table_id); + header.primary |= IPC_MOD_ID(dma_id); + + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); + + if (ret < 0) + dev_err(ipc->dev, "ipc: load lib failed\n"); + + return ret; +} +EXPORT_SYMBOL_GPL(skl_sst_ipc_load_library); + +int skl_ipc_set_d0ix(struct sst_generic_ipc *ipc, struct skl_ipc_d0ix_msg *msg) +{ + struct skl_ipc_header header = {0}; + u64 *ipc_header = (u64 *)(&header); + int ret; + + header.primary = IPC_MSG_TARGET(IPC_MOD_MSG); + header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); + header.primary |= IPC_GLB_TYPE(IPC_MOD_SET_D0IX); + header.primary |= IPC_MOD_INSTANCE_ID(msg->instance_id); + header.primary |= IPC_MOD_ID(msg->module_id); + + header.extension = IPC_D0IX_WAKE(msg->wake); + header.extension |= IPC_D0IX_STREAMING(msg->streaming); + + dev_dbg(ipc->dev, "In %s primary=%x ext=%x\n", __func__, + header.primary, header.extension); + + /* + * Use the nopm IPC here as we dont want it checking for D0iX + */ + ret = sst_ipc_tx_message_nopm(ipc, *ipc_header, NULL, 0, NULL, 0); + if (ret < 0) + dev_err(ipc->dev, "ipc: set d0ix failed, err %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(skl_ipc_set_d0ix); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 2e3d4e80ef97..cc40341233fa 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -16,7 +16,6 @@ #ifndef __SKL_IPC_H #define __SKL_IPC_H -#include <linux/kthread.h> #include <linux/irqreturn.h> #include "../common/sst-ipc.h" @@ -53,6 +52,23 @@ struct skl_dsp_cores { int usage_count[SKL_DSP_CORES_MAX]; }; +/** + * skl_d0i3_data: skl D0i3 counters data struct + * + * @streaming: Count of usecases that can attempt streaming D0i3 + * @non_streaming: Count of usecases that can attempt non-streaming D0i3 + * @non_d0i3: Count of usecases that cannot attempt D0i3 + * @state: current state + * @work: D0i3 worker thread + */ +struct skl_d0i3_data { + int streaming; + int non_streaming; + int non_d0i3; + enum skl_dsp_d0i3_states state; + struct delayed_work work; +}; + struct skl_sst { struct device *dev; struct sst_dsp *dsp; @@ -66,7 +82,7 @@ struct skl_sst { /* callback for miscbdge */ void (*enable_miscbdcge)(struct device *dev, bool enable); - /*Is CGCTL.MISCBDCGE disabled*/ + /* Is CGCTL.MISCBDCGE disabled */ bool miscbdcg_disabled; /* Populate module information */ @@ -75,8 +91,19 @@ struct skl_sst { /* Is firmware loaded */ bool fw_loaded; + /* first boot ? */ + bool is_first_boot; + /* multi-core */ struct skl_dsp_cores cores; + + /* tplg manifest */ + struct skl_dfw_manifest manifest; + + /* Callback to update D0i3C register */ + void (*update_d0i3c)(struct device *dev, bool enable); + + struct skl_d0i3_data d0i3; }; struct skl_ipc_init_instance_msg { @@ -85,6 +112,7 @@ struct skl_ipc_init_instance_msg { u16 param_data_size; u8 ppl_instance_id; u8 core_id; + u8 domain; }; struct skl_ipc_bind_unbind_msg { @@ -104,6 +132,13 @@ struct skl_ipc_large_config_msg { u32 param_data_size; }; +struct skl_ipc_d0ix_msg { + u32 module_id; + u32 instance_id; + u8 streaming; + u8 wake; +}; + #define SKL_IPC_BOOT_MSECS 3000 #define SKL_IPC_D3_MASK 0 @@ -112,7 +147,7 @@ struct skl_ipc_large_config_msg { irqreturn_t skl_dsp_irq_thread_handler(int irq, void *context); int skl_ipc_create_pipeline(struct sst_generic_ipc *sst_ipc, - u16 ppl_mem_size, u8 ppl_type, u8 instance_id); + u16 ppl_mem_size, u8 ppl_type, u8 instance_id, u8 lp_mode); int skl_ipc_delete_pipeline(struct sst_generic_ipc *sst_ipc, u8 instance_id); @@ -145,6 +180,14 @@ int skl_ipc_set_large_config(struct sst_generic_ipc *ipc, int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, struct skl_ipc_large_config_msg *msg, u32 *param); +int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc, + u8 dma_id, u8 table_id); + +int skl_ipc_set_d0ix(struct sst_generic_ipc *ipc, + struct skl_ipc_d0ix_msg *msg); + +int skl_ipc_check_D0i0(struct sst_dsp *dsp, bool state); + void skl_ipc_int_enable(struct sst_dsp *dsp); void skl_ipc_op_int_enable(struct sst_dsp *ctx); void skl_ipc_op_int_disable(struct sst_dsp *ctx); diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index ddcb52a51854..ea162fbf68e5 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -28,11 +28,6 @@ /* FW Extended Manifest Header id = $AE1 */ #define SKL_EXT_MANIFEST_HEADER_MAGIC 0x31454124 -struct skl_dfw_module_mod { - char name[100]; - struct skl_dfw_module skl_dfw_mod; -}; - struct UUID { u8 id[16]; }; @@ -99,10 +94,15 @@ struct adsp_fw_hdr { u32 load_offset; } __packed; +#define MAX_INSTANCE_BUFF 2 + struct uuid_module { uuid_le uuid; int id; int is_loadable; + int max_instance; + u64 pvt_id[MAX_INSTANCE_BUFF]; + int *instance_id; struct list_head list; }; @@ -115,13 +115,13 @@ struct skl_ext_manifest_hdr { u32 entries; }; -int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid, - struct skl_dfw_module *dfw_config) +int snd_skl_get_module_info(struct skl_sst *ctx, + struct skl_module_cfg *mconfig) { struct uuid_module *module; uuid_le *uuid_mod; - uuid_mod = (uuid_le *)uuid; + uuid_mod = (uuid_le *)mconfig->guid; if (list_empty(&ctx->uuid_list)) { dev_err(ctx->dev, "Module list is empty\n"); @@ -130,8 +130,8 @@ int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid, list_for_each_entry(module, &ctx->uuid_list, list) { if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) { - dfw_config->module_id = module->id; - dfw_config->is_loadable = module->is_loadable; + mconfig->id.module_id = module->id; + mconfig->is_loadable = module->is_loadable; return 0; } @@ -141,15 +141,154 @@ int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid, } EXPORT_SYMBOL_GPL(snd_skl_get_module_info); +static int skl_get_pvtid_map(struct uuid_module *module, int instance_id) +{ + int pvt_id; + + for (pvt_id = 0; pvt_id < module->max_instance; pvt_id++) { + if (module->instance_id[pvt_id] == instance_id) + return pvt_id; + } + return -EINVAL; +} + +int skl_get_pvt_instance_id_map(struct skl_sst *ctx, + int module_id, int instance_id) +{ + struct uuid_module *module; + + list_for_each_entry(module, &ctx->uuid_list, list) { + if (module->id == module_id) + return skl_get_pvtid_map(module, instance_id); + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(skl_get_pvt_instance_id_map); + +static inline int skl_getid_32(struct uuid_module *module, u64 *val, + int word1_mask, int word2_mask) +{ + int index, max_inst, pvt_id; + u32 mask_val; + + max_inst = module->max_instance; + mask_val = (u32)(*val >> word1_mask); + + if (mask_val != 0xffffffff) { + index = ffz(mask_val); + pvt_id = index + word1_mask + word2_mask; + if (pvt_id <= (max_inst - 1)) { + *val |= 1ULL << (index + word1_mask); + return pvt_id; + } + } + + return -EINVAL; +} + +static inline int skl_pvtid_128(struct uuid_module *module) +{ + int j, i, word1_mask, word2_mask = 0, pvt_id; + + for (j = 0; j < MAX_INSTANCE_BUFF; j++) { + word1_mask = 0; + + for (i = 0; i < 2; i++) { + pvt_id = skl_getid_32(module, &module->pvt_id[j], + word1_mask, word2_mask); + if (pvt_id >= 0) + return pvt_id; + + word1_mask += 32; + if ((word1_mask + word2_mask) >= module->max_instance) + return -EINVAL; + } + + word2_mask += 64; + if (word2_mask >= module->max_instance) + return -EINVAL; + } + + return -EINVAL; +} + +/** + * skl_get_pvt_id: generate a private id for use as module id + * + * @ctx: driver context + * @mconfig: module configuration data + * + * This generates a 128 bit private unique id for a module TYPE so that + * module instance is unique + */ +int skl_get_pvt_id(struct skl_sst *ctx, struct skl_module_cfg *mconfig) +{ + struct uuid_module *module; + uuid_le *uuid_mod; + int pvt_id; + + uuid_mod = (uuid_le *)mconfig->guid; + + list_for_each_entry(module, &ctx->uuid_list, list) { + if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) { + + pvt_id = skl_pvtid_128(module); + if (pvt_id >= 0) { + module->instance_id[pvt_id] = + mconfig->id.instance_id; + return pvt_id; + } + } + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(skl_get_pvt_id); + +/** + * skl_put_pvt_id: free up the private id allocated + * + * @ctx: driver context + * @mconfig: module configuration data + * + * This frees a 128 bit private unique id previously generated + */ +int skl_put_pvt_id(struct skl_sst *ctx, struct skl_module_cfg *mconfig) +{ + int i; + uuid_le *uuid_mod; + struct uuid_module *module; + + uuid_mod = (uuid_le *)mconfig->guid; + list_for_each_entry(module, &ctx->uuid_list, list) { + if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) { + + if (mconfig->id.pvt_id != 0) + i = (mconfig->id.pvt_id) / 64; + else + i = 0; + + module->pvt_id[i] &= ~(1 << (mconfig->id.pvt_id)); + mconfig->id.pvt_id = -1; + return 0; + } + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(skl_put_pvt_id); + /* * Parse the firmware binary to get the UUID, module id * and loadable flags */ -int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset) +int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw, + unsigned int offset, int index) { struct adsp_fw_hdr *adsp_hdr; struct adsp_module_entry *mod_entry; - int i, num_entry; + int i, num_entry, size; uuid_le *uuid_bin; const char *buf; struct skl_sst *skl = ctx->thread_context; @@ -158,8 +297,8 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset) unsigned int safe_file; /* Get the FW pointer to derive ADSP header */ - stripped_fw.data = ctx->fw->data; - stripped_fw.size = ctx->fw->size; + stripped_fw.data = fw->data; + stripped_fw.size = fw->size; skl_dsp_strip_extended_manifest(&stripped_fw); @@ -210,8 +349,15 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset) uuid_bin = (uuid_le *)mod_entry->uuid.id; memcpy(&module->uuid, uuid_bin, sizeof(module->uuid)); - module->id = i; + module->id = (i | (index << 12)); module->is_loadable = mod_entry->type.load_type; + module->max_instance = mod_entry->instance_max_count; + size = sizeof(int) * mod_entry->instance_max_count; + module->instance_id = devm_kzalloc(ctx->dev, size, GFP_KERNEL); + if (!module->instance_id) { + kfree(module); + return -ENOMEM; + } list_add_tail(&module->list, &skl->uuid_list); diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c index 588f899ceb65..b30bd384c8d3 100644 --- a/sound/soc/intel/skylake/skl-sst.c +++ b/sound/soc/intel/skylake/skl-sst.c @@ -88,13 +88,15 @@ static int skl_load_base_firmware(struct sst_dsp *ctx) } } - ret = snd_skl_parse_uuids(ctx, SKL_ADSP_FW_BIN_HDR_OFFSET); - if (ret < 0) { - dev_err(ctx->dev, - "UUID parsing err: %d\n", ret); - release_firmware(ctx->fw); - skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); - return ret; + /* prase uuids on first boot */ + if (skl->is_first_boot) { + ret = snd_skl_parse_uuids(ctx, ctx->fw, SKL_ADSP_FW_BIN_HDR_OFFSET, 0); + if (ret < 0) { + dev_err(ctx->dev, "UUID parsing err: %d\n", ret); + release_firmware(ctx->fw); + skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); + return ret; + } } /* check for extended manifest */ @@ -105,13 +107,13 @@ static int skl_load_base_firmware(struct sst_dsp *ctx) ret = skl_dsp_boot(ctx); if (ret < 0) { - dev_err(ctx->dev, "Boot dsp core failed ret: %d", ret); + dev_err(ctx->dev, "Boot dsp core failed ret: %d\n", ret); goto skl_load_base_firmware_failed; } ret = skl_cldma_prepare(ctx); if (ret < 0) { - dev_err(ctx->dev, "CL dma prepare failed : %d", ret); + dev_err(ctx->dev, "CL dma prepare failed : %d\n", ret); goto skl_load_base_firmware_failed; } @@ -484,28 +486,38 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, return ret; skl->cores.count = 2; + skl->is_first_boot = true; + + if (dsp) + *dsp = skl; + + return ret; +} +EXPORT_SYMBOL_GPL(skl_sst_dsp_init); + +int skl_sst_init_fw(struct device *dev, struct skl_sst *ctx) +{ + int ret; + struct sst_dsp *sst = ctx->dsp; ret = sst->fw_ops.load_fw(sst); if (ret < 0) { - dev_err(dev, "Load base fw failed : %d", ret); - goto cleanup; + dev_err(dev, "Load base fw failed : %d\n", ret); + return ret; } skl_dsp_init_core_state(sst); + ctx->is_first_boot = false; - if (dsp) - *dsp = skl; - - return ret; - -cleanup: - skl_sst_dsp_cleanup(dev, skl); - return ret; + return 0; } -EXPORT_SYMBOL_GPL(skl_sst_dsp_init); +EXPORT_SYMBOL_GPL(skl_sst_init_fw); void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx) { + + if (ctx->dsp->fw) + release_firmware(ctx->dsp->fw); skl_clear_module_table(ctx->dsp); skl_freeup_uuid_list(ctx); skl_ipc_free(&ctx->ipc); diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index cc0150fc2601..bd313c907b20 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -21,6 +21,7 @@ #include <linux/firmware.h> #include <sound/soc.h> #include <sound/soc-topology.h> +#include <uapi/sound/snd_sst_tokens.h> #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" #include "skl-topology.h" @@ -32,6 +33,46 @@ #define SKL_CH_FIXUP_MASK (1 << 0) #define SKL_RATE_FIXUP_MASK (1 << 1) #define SKL_FMT_FIXUP_MASK (1 << 2) +#define SKL_IN_DIR_BIT_MASK BIT(0) +#define SKL_PIN_COUNT_MASK GENMASK(7, 4) + +void skl_tplg_d0i3_get(struct skl *skl, enum d0i3_capability caps) +{ + struct skl_d0i3_data *d0i3 = &skl->skl_sst->d0i3; + + switch (caps) { + case SKL_D0I3_NONE: + d0i3->non_d0i3++; + break; + + case SKL_D0I3_STREAMING: + d0i3->streaming++; + break; + + case SKL_D0I3_NON_STREAMING: + d0i3->non_streaming++; + break; + } +} + +void skl_tplg_d0i3_put(struct skl *skl, enum d0i3_capability caps) +{ + struct skl_d0i3_data *d0i3 = &skl->skl_sst->d0i3; + + switch (caps) { + case SKL_D0I3_NONE: + d0i3->non_d0i3--; + break; + + case SKL_D0I3_STREAMING: + d0i3->streaming--; + break; + + case SKL_D0I3_NON_STREAMING: + d0i3->non_streaming--; + break; + } +} /* * SKL DSP driver modelling uses only few DAPM widgets so for rest we will @@ -473,6 +514,14 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) w = w_module->w; mconfig = w->priv; + /* check if module ids are populated */ + if (mconfig->id.module_id < 0) { + dev_err(skl->skl_sst->dev, + "module %pUL id not populated\n", + (uuid_le *)mconfig->guid); + return -EIO; + } + /* check resource available */ if (!skl_is_pipe_mcps_avail(skl, mconfig)) return -ENOMEM; @@ -494,12 +543,15 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) * FE/BE params */ skl_tplg_update_module_params(w, ctx); - + mconfig->id.pvt_id = skl_get_pvt_id(ctx, mconfig); + if (mconfig->id.pvt_id < 0) + return ret; skl_tplg_set_module_init_data(w); ret = skl_init_module(ctx, mconfig); - if (ret < 0) + if (ret < 0) { + skl_put_pvt_id(ctx, mconfig); return ret; - + } skl_tplg_alloc_pipe_mcps(skl, mconfig); ret = skl_tplg_set_module_params(w, ctx); if (ret < 0) @@ -512,6 +564,7 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) static int skl_tplg_unload_pipe_modules(struct skl_sst *ctx, struct skl_pipe *pipe) { + int ret; struct skl_pipe_module *w_module = NULL; struct skl_module_cfg *mconfig = NULL; @@ -519,9 +572,13 @@ static int skl_tplg_unload_pipe_modules(struct skl_sst *ctx, mconfig = w_module->w->priv; if (mconfig->is_loadable && ctx->dsp->fw_ops.unload_mod && - mconfig->m_state > SKL_MODULE_UNINIT) - return ctx->dsp->fw_ops.unload_mod(ctx->dsp, + mconfig->m_state > SKL_MODULE_UNINIT) { + ret = ctx->dsp->fw_ops.unload_mod(ctx->dsp, mconfig->id.module_id); + if (ret < 0) + return -EIO; + } + skl_put_pvt_id(ctx, mconfig); } /* no modules to unload in this path, so return */ @@ -588,6 +645,26 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, return 0; } +static int skl_fill_sink_instance_id(struct skl_sst *ctx, + struct skl_algo_data *alg_data) +{ + struct skl_kpb_params *params = (struct skl_kpb_params *)alg_data->params; + struct skl_mod_inst_map *inst; + int i, pvt_id; + + inst = params->map; + + for (i = 0; i < params->num_modules; i++) { + pvt_id = skl_get_pvt_instance_id_map(ctx, + inst->mod_id, inst->inst_id); + if (pvt_id < 0) + return -EINVAL; + inst->inst_id = pvt_id; + inst++; + } + return 0; +} + /* * Some modules require params to be set after the module is bound to * all pins connected. @@ -636,6 +713,8 @@ static int skl_tplg_set_module_bind_params(struct snd_soc_dapm_widget *w, bc = (struct skl_algo_data *)sb->dobj.private; if (bc->set_params == SKL_PARAM_BIND) { + if (mconfig->m_type == SKL_MODULE_TYPE_KPB) + skl_fill_sink_instance_id(ctx, bc); ret = skl_set_module_params(ctx, (u32 *)bc->params, bc->max, bc->param_id, mconfig); @@ -1460,85 +1539,579 @@ static const struct snd_soc_tplg_bytes_ext_ops skl_tlv_ops[] = { skl_tplg_tlv_control_set}, }; -/* - * The topology binary passes the pin info for a module so initialize the pin - * info passed into module instance - */ -static void skl_fill_module_pin_info(struct skl_dfw_module_pin *dfw_pin, - struct skl_module_pin *m_pin, - bool is_dynamic, int max_pin) +static int skl_tplg_fill_pipe_tkn(struct device *dev, + struct skl_pipe *pipe, u32 tkn, + u32 tkn_val) { - int i; - for (i = 0; i < max_pin; i++) { - m_pin[i].id.module_id = dfw_pin[i].module_id; - m_pin[i].id.instance_id = dfw_pin[i].instance_id; - m_pin[i].in_use = false; - m_pin[i].is_dynamic = is_dynamic; - m_pin[i].pin_state = SKL_PIN_UNBIND; + switch (tkn) { + case SKL_TKN_U32_PIPE_CONN_TYPE: + pipe->conn_type = tkn_val; + break; + + case SKL_TKN_U32_PIPE_PRIORITY: + pipe->pipe_priority = tkn_val; + break; + + case SKL_TKN_U32_PIPE_MEM_PGS: + pipe->memory_pages = tkn_val; + break; + + case SKL_TKN_U32_PMODE: + pipe->lp_mode = tkn_val; + break; + + default: + dev_err(dev, "Token not handled %d\n", tkn); + return -EINVAL; } + + return 0; } /* - * Add pipeline from topology binary into driver pipeline list - * - * If already added we return that instance - * Otherwise we create a new instance and add into driver list + * Add pipeline by parsing the relevant tokens + * Return an existing pipe if the pipe already exists. */ -static struct skl_pipe *skl_tplg_add_pipe(struct device *dev, - struct skl *skl, struct skl_dfw_pipe *dfw_pipe) +static int skl_tplg_add_pipe(struct device *dev, + struct skl_module_cfg *mconfig, struct skl *skl, + struct snd_soc_tplg_vendor_value_elem *tkn_elem) { struct skl_pipeline *ppl; struct skl_pipe *pipe; struct skl_pipe_params *params; list_for_each_entry(ppl, &skl->ppl_list, node) { - if (ppl->pipe->ppl_id == dfw_pipe->pipe_id) - return ppl->pipe; + if (ppl->pipe->ppl_id == tkn_elem->value) { + mconfig->pipe = ppl->pipe; + return EEXIST; + } } ppl = devm_kzalloc(dev, sizeof(*ppl), GFP_KERNEL); if (!ppl) - return NULL; + return -ENOMEM; pipe = devm_kzalloc(dev, sizeof(*pipe), GFP_KERNEL); if (!pipe) - return NULL; + return -ENOMEM; params = devm_kzalloc(dev, sizeof(*params), GFP_KERNEL); if (!params) - return NULL; + return -ENOMEM; - pipe->ppl_id = dfw_pipe->pipe_id; - pipe->memory_pages = dfw_pipe->memory_pages; - pipe->pipe_priority = dfw_pipe->pipe_priority; - pipe->conn_type = dfw_pipe->conn_type; - pipe->state = SKL_PIPE_INVALID; pipe->p_params = params; + pipe->ppl_id = tkn_elem->value; INIT_LIST_HEAD(&pipe->w_list); ppl->pipe = pipe; list_add(&ppl->node, &skl->ppl_list); - return ppl->pipe; + mconfig->pipe = pipe; + mconfig->pipe->state = SKL_PIPE_INVALID; + + return 0; +} + +static int skl_tplg_fill_pin(struct device *dev, u32 tkn, + struct skl_module_pin *m_pin, + int pin_index, u32 value) +{ + switch (tkn) { + case SKL_TKN_U32_PIN_MOD_ID: + m_pin[pin_index].id.module_id = value; + break; + + case SKL_TKN_U32_PIN_INST_ID: + m_pin[pin_index].id.instance_id = value; + break; + + default: + dev_err(dev, "%d Not a pin token\n", value); + return -EINVAL; + } + + return 0; +} + +/* + * Parse for pin config specific tokens to fill up the + * module private data + */ +static int skl_tplg_fill_pins_info(struct device *dev, + struct skl_module_cfg *mconfig, + struct snd_soc_tplg_vendor_value_elem *tkn_elem, + int dir, int pin_count) +{ + int ret; + struct skl_module_pin *m_pin; + + switch (dir) { + case SKL_DIR_IN: + m_pin = mconfig->m_in_pin; + break; + + case SKL_DIR_OUT: + m_pin = mconfig->m_out_pin; + break; + + default: + dev_err(dev, "Invalid direction value\n"); + return -EINVAL; + } + + ret = skl_tplg_fill_pin(dev, tkn_elem->token, + m_pin, pin_count, tkn_elem->value); + + if (ret < 0) + return ret; + + m_pin[pin_count].in_use = false; + m_pin[pin_count].pin_state = SKL_PIN_UNBIND; + + return 0; +} + +/* + * Fill up input/output module config format based + * on the direction + */ +static int skl_tplg_fill_fmt(struct device *dev, + struct skl_module_cfg *mconfig, u32 tkn, + u32 value, u32 dir, u32 pin_count) +{ + struct skl_module_fmt *dst_fmt; + + switch (dir) { + case SKL_DIR_IN: + dst_fmt = mconfig->in_fmt; + dst_fmt += pin_count; + break; + + case SKL_DIR_OUT: + dst_fmt = mconfig->out_fmt; + dst_fmt += pin_count; + break; + + default: + dev_err(dev, "Invalid direction value\n"); + return -EINVAL; + } + + switch (tkn) { + case SKL_TKN_U32_FMT_CH: + dst_fmt->channels = value; + break; + + case SKL_TKN_U32_FMT_FREQ: + dst_fmt->s_freq = value; + break; + + case SKL_TKN_U32_FMT_BIT_DEPTH: + dst_fmt->bit_depth = value; + break; + + case SKL_TKN_U32_FMT_SAMPLE_SIZE: + dst_fmt->valid_bit_depth = value; + break; + + case SKL_TKN_U32_FMT_CH_CONFIG: + dst_fmt->ch_cfg = value; + break; + + case SKL_TKN_U32_FMT_INTERLEAVE: + dst_fmt->interleaving_style = value; + break; + + case SKL_TKN_U32_FMT_SAMPLE_TYPE: + dst_fmt->sample_type = value; + break; + + case SKL_TKN_U32_FMT_CH_MAP: + dst_fmt->ch_map = value; + break; + + default: + dev_err(dev, "Invalid token %d\n", tkn); + return -EINVAL; + } + + return 0; +} + +static int skl_tplg_get_uuid(struct device *dev, struct skl_module_cfg *mconfig, + struct snd_soc_tplg_vendor_uuid_elem *uuid_tkn) +{ + if (uuid_tkn->token == SKL_TKN_UUID) + memcpy(&mconfig->guid, &uuid_tkn->uuid, 16); + else { + dev_err(dev, "Not an UUID token tkn %d\n", uuid_tkn->token); + return -EINVAL; + } + + return 0; } -static void skl_tplg_fill_fmt(struct skl_module_fmt *dst_fmt, - struct skl_dfw_module_fmt *src_fmt, - int pins) +static void skl_tplg_fill_pin_dynamic_val( + struct skl_module_pin *mpin, u32 pin_count, u32 value) { int i; - for (i = 0; i < pins; i++) { - dst_fmt[i].channels = src_fmt[i].channels; - dst_fmt[i].s_freq = src_fmt[i].freq; - dst_fmt[i].bit_depth = src_fmt[i].bit_depth; - dst_fmt[i].valid_bit_depth = src_fmt[i].valid_bit_depth; - dst_fmt[i].ch_cfg = src_fmt[i].ch_cfg; - dst_fmt[i].ch_map = src_fmt[i].ch_map; - dst_fmt[i].interleaving_style = src_fmt[i].interleaving_style; - dst_fmt[i].sample_type = src_fmt[i].sample_type; + for (i = 0; i < pin_count; i++) + mpin[i].is_dynamic = value; +} + +/* + * Parse tokens to fill up the module private data + */ +static int skl_tplg_get_token(struct device *dev, + struct snd_soc_tplg_vendor_value_elem *tkn_elem, + struct skl *skl, struct skl_module_cfg *mconfig) +{ + int tkn_count = 0; + int ret; + static int is_pipe_exists; + static int pin_index, dir; + + if (tkn_elem->token > SKL_TKN_MAX) + return -EINVAL; + + switch (tkn_elem->token) { + case SKL_TKN_U8_IN_QUEUE_COUNT: + mconfig->max_in_queue = tkn_elem->value; + mconfig->m_in_pin = devm_kzalloc(dev, mconfig->max_in_queue * + sizeof(*mconfig->m_in_pin), + GFP_KERNEL); + if (!mconfig->m_in_pin) + return -ENOMEM; + + break; + + case SKL_TKN_U8_OUT_QUEUE_COUNT: + mconfig->max_out_queue = tkn_elem->value; + mconfig->m_out_pin = devm_kzalloc(dev, mconfig->max_out_queue * + sizeof(*mconfig->m_out_pin), + GFP_KERNEL); + + if (!mconfig->m_out_pin) + return -ENOMEM; + + break; + + case SKL_TKN_U8_DYN_IN_PIN: + if (!mconfig->m_in_pin) + return -ENOMEM; + + skl_tplg_fill_pin_dynamic_val(mconfig->m_in_pin, + mconfig->max_in_queue, tkn_elem->value); + + break; + + case SKL_TKN_U8_DYN_OUT_PIN: + if (!mconfig->m_out_pin) + return -ENOMEM; + + skl_tplg_fill_pin_dynamic_val(mconfig->m_out_pin, + mconfig->max_out_queue, tkn_elem->value); + + break; + + case SKL_TKN_U8_TIME_SLOT: + mconfig->time_slot = tkn_elem->value; + break; + + case SKL_TKN_U8_CORE_ID: + mconfig->core_id = tkn_elem->value; + + case SKL_TKN_U8_MOD_TYPE: + mconfig->m_type = tkn_elem->value; + break; + + case SKL_TKN_U8_DEV_TYPE: + mconfig->dev_type = tkn_elem->value; + break; + + case SKL_TKN_U8_HW_CONN_TYPE: + mconfig->hw_conn_type = tkn_elem->value; + break; + + case SKL_TKN_U16_MOD_INST_ID: + mconfig->id.instance_id = + tkn_elem->value; + break; + + case SKL_TKN_U32_MEM_PAGES: + mconfig->mem_pages = tkn_elem->value; + break; + + case SKL_TKN_U32_MAX_MCPS: + mconfig->mcps = tkn_elem->value; + break; + + case SKL_TKN_U32_OBS: + mconfig->obs = tkn_elem->value; + break; + + case SKL_TKN_U32_IBS: + mconfig->ibs = tkn_elem->value; + break; + + case SKL_TKN_U32_VBUS_ID: + mconfig->vbus_id = tkn_elem->value; + break; + + case SKL_TKN_U32_PARAMS_FIXUP: + mconfig->params_fixup = tkn_elem->value; + break; + + case SKL_TKN_U32_CONVERTER: + mconfig->converter = tkn_elem->value; + break; + + case SKL_TKL_U32_D0I3_CAPS: + mconfig->d0i3_caps = tkn_elem->value; + break; + + case SKL_TKN_U32_PIPE_ID: + ret = skl_tplg_add_pipe(dev, + mconfig, skl, tkn_elem); + + if (ret < 0) + return is_pipe_exists; + + if (ret == EEXIST) + is_pipe_exists = 1; + + break; + + case SKL_TKN_U32_PIPE_CONN_TYPE: + case SKL_TKN_U32_PIPE_PRIORITY: + case SKL_TKN_U32_PIPE_MEM_PGS: + case SKL_TKN_U32_PMODE: + if (is_pipe_exists) { + ret = skl_tplg_fill_pipe_tkn(dev, mconfig->pipe, + tkn_elem->token, tkn_elem->value); + if (ret < 0) + return ret; + } + + break; + + /* + * SKL_TKN_U32_DIR_PIN_COUNT token has the value for both + * direction and the pin count. The first four bits represent + * direction and next four the pin count. + */ + case SKL_TKN_U32_DIR_PIN_COUNT: + dir = tkn_elem->value & SKL_IN_DIR_BIT_MASK; + pin_index = (tkn_elem->value & + SKL_PIN_COUNT_MASK) >> 4; + + break; + + case SKL_TKN_U32_FMT_CH: + case SKL_TKN_U32_FMT_FREQ: + case SKL_TKN_U32_FMT_BIT_DEPTH: + case SKL_TKN_U32_FMT_SAMPLE_SIZE: + case SKL_TKN_U32_FMT_CH_CONFIG: + case SKL_TKN_U32_FMT_INTERLEAVE: + case SKL_TKN_U32_FMT_SAMPLE_TYPE: + case SKL_TKN_U32_FMT_CH_MAP: + ret = skl_tplg_fill_fmt(dev, mconfig, tkn_elem->token, + tkn_elem->value, dir, pin_index); + + if (ret < 0) + return ret; + + break; + + case SKL_TKN_U32_PIN_MOD_ID: + case SKL_TKN_U32_PIN_INST_ID: + ret = skl_tplg_fill_pins_info(dev, + mconfig, tkn_elem, dir, + pin_index); + if (ret < 0) + return ret; + + break; + + case SKL_TKN_U32_CAPS_SIZE: + mconfig->formats_config.caps_size = + tkn_elem->value; + + break; + + case SKL_TKN_U32_PROC_DOMAIN: + mconfig->domain = + tkn_elem->value; + + break; + + case SKL_TKN_U8_IN_PIN_TYPE: + case SKL_TKN_U8_OUT_PIN_TYPE: + case SKL_TKN_U8_CONN_TYPE: + break; + + default: + dev_err(dev, "Token %d not handled\n", + tkn_elem->token); + return -EINVAL; + } + + tkn_count++; + + return tkn_count; +} + +/* + * Parse the vendor array for specific tokens to construct + * module private data + */ +static int skl_tplg_get_tokens(struct device *dev, + char *pvt_data, struct skl *skl, + struct skl_module_cfg *mconfig, int block_size) +{ + struct snd_soc_tplg_vendor_array *array; + struct snd_soc_tplg_vendor_value_elem *tkn_elem; + int tkn_count = 0, ret; + int off = 0, tuple_size = 0; + + if (block_size <= 0) + return -EINVAL; + + while (tuple_size < block_size) { + array = (struct snd_soc_tplg_vendor_array *)(pvt_data + off); + + off += array->size; + + switch (array->type) { + case SND_SOC_TPLG_TUPLE_TYPE_STRING: + dev_warn(dev, "no string tokens expected for skl tplg\n"); + continue; + + case SND_SOC_TPLG_TUPLE_TYPE_UUID: + ret = skl_tplg_get_uuid(dev, mconfig, array->uuid); + if (ret < 0) + return ret; + + tuple_size += sizeof(*array->uuid); + + continue; + + default: + tkn_elem = array->value; + tkn_count = 0; + break; + } + + while (tkn_count <= (array->num_elems - 1)) { + ret = skl_tplg_get_token(dev, tkn_elem, + skl, mconfig); + + if (ret < 0) + return ret; + + tkn_count = tkn_count + ret; + tkn_elem++; + } + + tuple_size += tkn_count * sizeof(*tkn_elem); + } + + return 0; +} + +/* + * Every data block is preceded by a descriptor to read the number + * of data blocks, they type of the block and it's size + */ +static int skl_tplg_get_desc_blocks(struct device *dev, + struct snd_soc_tplg_vendor_array *array) +{ + struct snd_soc_tplg_vendor_value_elem *tkn_elem; + + tkn_elem = array->value; + + switch (tkn_elem->token) { + case SKL_TKN_U8_NUM_BLOCKS: + case SKL_TKN_U8_BLOCK_TYPE: + case SKL_TKN_U16_BLOCK_SIZE: + return tkn_elem->value; + + default: + dev_err(dev, "Invalid descriptor token %d\n", tkn_elem->token); + break; + } + + return -EINVAL; +} + +/* + * Parse the private data for the token and corresponding value. + * The private data can have multiple data blocks. So, a data block + * is preceded by a descriptor for number of blocks and a descriptor + * for the type and size of the suceeding data block. + */ +static int skl_tplg_get_pvt_data(struct snd_soc_tplg_dapm_widget *tplg_w, + struct skl *skl, struct device *dev, + struct skl_module_cfg *mconfig) +{ + struct snd_soc_tplg_vendor_array *array; + int num_blocks, block_size = 0, block_type, off = 0; + char *data; + int ret; + + /* Read the NUM_DATA_BLOCKS descriptor */ + array = (struct snd_soc_tplg_vendor_array *)tplg_w->priv.data; + ret = skl_tplg_get_desc_blocks(dev, array); + if (ret < 0) + return ret; + num_blocks = ret; + + off += array->size; + array = (struct snd_soc_tplg_vendor_array *)(tplg_w->priv.data + off); + + /* Read the BLOCK_TYPE and BLOCK_SIZE descriptor */ + while (num_blocks > 0) { + ret = skl_tplg_get_desc_blocks(dev, array); + + if (ret < 0) + return ret; + block_type = ret; + off += array->size; + + array = (struct snd_soc_tplg_vendor_array *) + (tplg_w->priv.data + off); + + ret = skl_tplg_get_desc_blocks(dev, array); + + if (ret < 0) + return ret; + block_size = ret; + off += array->size; + + array = (struct snd_soc_tplg_vendor_array *) + (tplg_w->priv.data + off); + + data = (tplg_w->priv.data + off); + + if (block_type == SKL_TYPE_TUPLE) { + ret = skl_tplg_get_tokens(dev, data, + skl, mconfig, block_size); + + if (ret < 0) + return ret; + + --num_blocks; + } else { + if (mconfig->formats_config.caps_size > 0) + memcpy(mconfig->formats_config.caps, data, + mconfig->formats_config.caps_size); + --num_blocks; + } } + + return 0; } static void skl_clear_pin_config(struct snd_soc_platform *platform, @@ -1606,9 +2179,6 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, struct skl *skl = ebus_to_skl(ebus); struct hdac_bus *bus = ebus_to_hbus(ebus); struct skl_module_cfg *mconfig; - struct skl_pipe *pipe; - struct skl_dfw_module *dfw_config = - (struct skl_dfw_module *)tplg_w->priv.data; if (!tplg_w->priv.size) goto bind_event; @@ -1619,76 +2189,17 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, return -ENOMEM; w->priv = mconfig; - memcpy(&mconfig->guid, &dfw_config->uuid, 16); - ret = snd_skl_get_module_info(skl->skl_sst, mconfig->guid, dfw_config); + /* + * module binary can be loaded later, so set it to query when + * module is load for a use case + */ + mconfig->id.module_id = -1; + + /* Parse private data for tuples */ + ret = skl_tplg_get_pvt_data(tplg_w, skl, bus->dev, mconfig); if (ret < 0) return ret; - - mconfig->id.module_id = dfw_config->module_id; - mconfig->id.instance_id = dfw_config->instance_id; - mconfig->mcps = dfw_config->max_mcps; - mconfig->ibs = dfw_config->ibs; - mconfig->obs = dfw_config->obs; - mconfig->core_id = dfw_config->core_id; - mconfig->max_in_queue = dfw_config->max_in_queue; - mconfig->max_out_queue = dfw_config->max_out_queue; - mconfig->is_loadable = dfw_config->is_loadable; - skl_tplg_fill_fmt(mconfig->in_fmt, dfw_config->in_fmt, - MODULE_MAX_IN_PINS); - skl_tplg_fill_fmt(mconfig->out_fmt, dfw_config->out_fmt, - MODULE_MAX_OUT_PINS); - - mconfig->params_fixup = dfw_config->params_fixup; - mconfig->converter = dfw_config->converter; - mconfig->m_type = dfw_config->module_type; - mconfig->vbus_id = dfw_config->vbus_id; - mconfig->mem_pages = dfw_config->mem_pages; - - pipe = skl_tplg_add_pipe(bus->dev, skl, &dfw_config->pipe); - if (pipe) - mconfig->pipe = pipe; - - mconfig->dev_type = dfw_config->dev_type; - mconfig->hw_conn_type = dfw_config->hw_conn_type; - mconfig->time_slot = dfw_config->time_slot; - mconfig->formats_config.caps_size = dfw_config->caps.caps_size; - - mconfig->m_in_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) * - sizeof(*mconfig->m_in_pin), - GFP_KERNEL); - if (!mconfig->m_in_pin) - return -ENOMEM; - - mconfig->m_out_pin = devm_kzalloc(bus->dev, (mconfig->max_out_queue) * - sizeof(*mconfig->m_out_pin), - GFP_KERNEL); - if (!mconfig->m_out_pin) - return -ENOMEM; - - skl_fill_module_pin_info(dfw_config->in_pin, mconfig->m_in_pin, - dfw_config->is_dynamic_in_pin, - mconfig->max_in_queue); - - skl_fill_module_pin_info(dfw_config->out_pin, mconfig->m_out_pin, - dfw_config->is_dynamic_out_pin, - mconfig->max_out_queue); - - - if (mconfig->formats_config.caps_size == 0) - goto bind_event; - - mconfig->formats_config.caps = (u32 *)devm_kzalloc(bus->dev, - mconfig->formats_config.caps_size, GFP_KERNEL); - - if (mconfig->formats_config.caps == NULL) - return -ENOMEM; - - memcpy(mconfig->formats_config.caps, dfw_config->caps.caps, - dfw_config->caps.caps_size); - mconfig->formats_config.param_id = dfw_config->caps.param_id; - mconfig->formats_config.set_params = dfw_config->caps.set_params; - bind_event: if (tplg_w->event_type == 0) { dev_dbg(bus->dev, "ASoC: No event handler required\n"); @@ -1767,11 +2278,229 @@ static int skl_tplg_control_load(struct snd_soc_component *cmpnt, return 0; } +static int skl_tplg_fill_str_mfest_tkn(struct device *dev, + struct snd_soc_tplg_vendor_string_elem *str_elem, + struct skl_dfw_manifest *minfo) +{ + int tkn_count = 0; + static int ref_count; + + switch (str_elem->token) { + case SKL_TKN_STR_LIB_NAME: + if (ref_count > minfo->lib_count - 1) { + ref_count = 0; + return -EINVAL; + } + + strncpy(minfo->lib[ref_count].name, str_elem->string, + ARRAY_SIZE(minfo->lib[ref_count].name)); + ref_count++; + tkn_count++; + break; + + default: + dev_err(dev, "Not a string token %d\n", str_elem->token); + break; + } + + return tkn_count; +} + +static int skl_tplg_get_str_tkn(struct device *dev, + struct snd_soc_tplg_vendor_array *array, + struct skl_dfw_manifest *minfo) +{ + int tkn_count = 0, ret; + struct snd_soc_tplg_vendor_string_elem *str_elem; + + str_elem = (struct snd_soc_tplg_vendor_string_elem *)array->value; + while (tkn_count < array->num_elems) { + ret = skl_tplg_fill_str_mfest_tkn(dev, str_elem, minfo); + str_elem++; + + if (ret < 0) + return ret; + + tkn_count = tkn_count + ret; + } + + return tkn_count; +} + +static int skl_tplg_get_int_tkn(struct device *dev, + struct snd_soc_tplg_vendor_value_elem *tkn_elem, + struct skl_dfw_manifest *minfo) +{ + int tkn_count = 0; + + switch (tkn_elem->token) { + case SKL_TKN_U32_LIB_COUNT: + minfo->lib_count = tkn_elem->value; + tkn_count++; + break; + + default: + dev_err(dev, "Not a manifest token %d\n", tkn_elem->token); + return -EINVAL; + } + + return tkn_count; +} + +/* + * Fill the manifest structure by parsing the tokens based on the + * type. + */ +static int skl_tplg_get_manifest_tkn(struct device *dev, + char *pvt_data, struct skl_dfw_manifest *minfo, + int block_size) +{ + int tkn_count = 0, ret; + int off = 0, tuple_size = 0; + struct snd_soc_tplg_vendor_array *array; + struct snd_soc_tplg_vendor_value_elem *tkn_elem; + + if (block_size <= 0) + return -EINVAL; + + while (tuple_size < block_size) { + array = (struct snd_soc_tplg_vendor_array *)(pvt_data + off); + off += array->size; + switch (array->type) { + case SND_SOC_TPLG_TUPLE_TYPE_STRING: + ret = skl_tplg_get_str_tkn(dev, array, minfo); + + if (ret < 0) + return ret; + tkn_count += ret; + + tuple_size += tkn_count * + sizeof(struct snd_soc_tplg_vendor_string_elem); + continue; + + case SND_SOC_TPLG_TUPLE_TYPE_UUID: + dev_warn(dev, "no uuid tokens for skl tplf manifest\n"); + continue; + + default: + tkn_elem = array->value; + tkn_count = 0; + break; + } + + while (tkn_count <= array->num_elems - 1) { + ret = skl_tplg_get_int_tkn(dev, + tkn_elem, minfo); + if (ret < 0) + return ret; + + tkn_count = tkn_count + ret; + tkn_elem++; + tuple_size += tkn_count * + sizeof(struct snd_soc_tplg_vendor_value_elem); + break; + } + tkn_count = 0; + } + + return 0; +} + +/* + * Parse manifest private data for tokens. The private data block is + * preceded by descriptors for type and size of data block. + */ +static int skl_tplg_get_manifest_data(struct snd_soc_tplg_manifest *manifest, + struct device *dev, struct skl_dfw_manifest *minfo) +{ + struct snd_soc_tplg_vendor_array *array; + int num_blocks, block_size = 0, block_type, off = 0; + char *data; + int ret; + + /* Read the NUM_DATA_BLOCKS descriptor */ + array = (struct snd_soc_tplg_vendor_array *)manifest->priv.data; + ret = skl_tplg_get_desc_blocks(dev, array); + if (ret < 0) + return ret; + num_blocks = ret; + + off += array->size; + array = (struct snd_soc_tplg_vendor_array *) + (manifest->priv.data + off); + + /* Read the BLOCK_TYPE and BLOCK_SIZE descriptor */ + while (num_blocks > 0) { + ret = skl_tplg_get_desc_blocks(dev, array); + + if (ret < 0) + return ret; + block_type = ret; + off += array->size; + + array = (struct snd_soc_tplg_vendor_array *) + (manifest->priv.data + off); + + ret = skl_tplg_get_desc_blocks(dev, array); + + if (ret < 0) + return ret; + block_size = ret; + off += array->size; + + array = (struct snd_soc_tplg_vendor_array *) + (manifest->priv.data + off); + + data = (manifest->priv.data + off); + + if (block_type == SKL_TYPE_TUPLE) { + ret = skl_tplg_get_manifest_tkn(dev, data, minfo, + block_size); + + if (ret < 0) + return ret; + + --num_blocks; + } else { + return -EINVAL; + } + } + + return 0; +} + +static int skl_manifest_load(struct snd_soc_component *cmpnt, + struct snd_soc_tplg_manifest *manifest) +{ + struct skl_dfw_manifest *minfo; + struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt); + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl *skl = ebus_to_skl(ebus); + int ret = 0; + + /* proceed only if we have private data defined */ + if (manifest->priv.size == 0) + return 0; + + minfo = &skl->skl_sst->manifest; + + skl_tplg_get_manifest_data(manifest, bus->dev, minfo); + + if (minfo->lib_count > HDA_MAX_LIB) { + dev_err(bus->dev, "Exceeding max Library count. Got:%d\n", + minfo->lib_count); + ret = -EINVAL; + } + + return ret; +} + static struct snd_soc_tplg_ops skl_tplg_ops = { .widget_load = skl_tplg_widget_load, .control_load = skl_tplg_control_load, .bytes_ext_ops = skl_tlv_ops, .bytes_ext_ops_count = ARRAY_SIZE(skl_tlv_ops), + .manifest = skl_manifest_load, }; /* diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 22d3ef83817d..08d39280b07b 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -113,27 +113,10 @@ struct skl_cpr_gtw_cfg { u32 config_data[1]; } __packed; -struct skl_i2s_config_blob { - u32 gateway_attrib; - u32 tdm_ts_group[8]; - u32 ssc0; - u32 ssc1; - u32 sscto; - u32 sspsp; - u32 sstsa; - u32 ssrsa; - u32 ssc2; - u32 sspsp2; - u32 ssc3; - u32 ssioc; - u32 mdivc; - u32 mdivr; -} __packed; - struct skl_dma_control { u32 node_id; u32 config_length; - u32 config_data[1]; + u32 config_data[0]; } __packed; struct skl_cpr_cfg { @@ -215,9 +198,20 @@ struct skl_module_fmt { struct skl_module_cfg; +struct skl_mod_inst_map { + u16 mod_id; + u16 inst_id; +}; + +struct skl_kpb_params { + u32 num_modules; + struct skl_mod_inst_map map[0]; +}; + struct skl_module_inst_id { - u32 module_id; + int module_id; u32 instance_id; + int pvt_id; }; enum skl_module_pin_state { @@ -268,6 +262,7 @@ struct skl_pipe { u8 pipe_priority; u16 conn_type; u32 memory_pages; + u8 lp_mode; struct skl_pipe_params *p_params; enum skl_pipe_state state; struct list_head w_list; @@ -282,6 +277,12 @@ enum skl_module_state { SKL_MODULE_UNLOADED = 4, }; +enum d0i3_capability { + SKL_D0I3_NONE = 0, + SKL_D0I3_STREAMING = 1, + SKL_D0I3_NON_STREAMING = 2, +}; + struct skl_module_cfg { u8 guid[16]; struct skl_module_inst_id id; @@ -308,6 +309,7 @@ struct skl_module_cfg { u32 converter; u32 vbus_id; u32 mem_pages; + enum d0i3_capability d0i3_caps; struct skl_module_pin *m_in_pin; struct skl_module_pin *m_out_pin; enum skl_module_type m_type; @@ -350,6 +352,9 @@ struct skl_module_cfg *skl_tplg_fe_get_cpr_module( int skl_tplg_update_pipe_params(struct device *dev, struct skl_module_cfg *mconfig, struct skl_pipe_params *params); +void skl_tplg_d0i3_get(struct skl *skl, enum d0i3_capability caps); +void skl_tplg_d0i3_put(struct skl *skl, enum d0i3_capability caps); + int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe); int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h index a32e5e9cc530..2f6281e056d6 100644 --- a/sound/soc/intel/skylake/skl-tplg-interface.h +++ b/sound/soc/intel/skylake/skl-tplg-interface.h @@ -80,7 +80,8 @@ enum skl_module_type { SKL_MODULE_TYPE_UPDWMIX, SKL_MODULE_TYPE_SRCINT, SKL_MODULE_TYPE_ALGO, - SKL_MODULE_TYPE_BASE_OUTFMT + SKL_MODULE_TYPE_BASE_OUTFMT, + SKL_MODULE_TYPE_KPB, }; enum skl_core_affinity { @@ -148,84 +149,34 @@ enum skl_module_param_type { SKL_PARAM_BIND }; -struct skl_dfw_module_pin { - u16 module_id; - u16 instance_id; -} __packed; - -struct skl_dfw_module_fmt { - u32 channels; - u32 freq; - u32 bit_depth; - u32 valid_bit_depth; - u32 ch_cfg; - u32 interleaving_style; - u32 sample_type; - u32 ch_map; -} __packed; - -struct skl_dfw_module_caps { +struct skl_dfw_algo_data { u32 set_params:2; u32 rsvd:30; u32 param_id; - u32 caps_size; - u32 caps[HDA_SST_CFG_MAX]; -}; - -struct skl_dfw_pipe { - u8 pipe_id; - u8 pipe_priority; - u16 conn_type:4; - u16 rsvd:4; - u16 memory_pages:8; + u32 max; + char params[0]; } __packed; -struct skl_dfw_module { - u8 uuid[16]; - - u16 module_id; - u16 instance_id; - u32 max_mcps; - u32 mem_pages; - u32 obs; - u32 ibs; - u32 vbus_id; - - u32 max_in_queue:8; - u32 max_out_queue:8; - u32 time_slot:8; - u32 core_id:4; - u32 rsvd1:4; - - u32 module_type:8; - u32 conn_type:4; - u32 dev_type:4; - u32 hw_conn_type:4; - u32 rsvd2:12; - - u32 params_fixup:8; - u32 converter:8; - u32 input_pin_type:1; - u32 output_pin_type:1; - u32 is_dynamic_in_pin:1; - u32 is_dynamic_out_pin:1; - u32 is_loadable:1; - u32 rsvd3:11; - - struct skl_dfw_pipe pipe; - struct skl_dfw_module_fmt in_fmt[MAX_IN_QUEUE]; - struct skl_dfw_module_fmt out_fmt[MAX_OUT_QUEUE]; - struct skl_dfw_module_pin in_pin[MAX_IN_QUEUE]; - struct skl_dfw_module_pin out_pin[MAX_OUT_QUEUE]; - struct skl_dfw_module_caps caps; +#define LIB_NAME_LENGTH 128 +#define HDA_MAX_LIB 16 + +struct lib_info { + char name[LIB_NAME_LENGTH]; } __packed; -struct skl_dfw_algo_data { - u32 set_params:2; - u32 rsvd:30; - u32 param_id; - u32 max; - char params[0]; +struct skl_dfw_manifest { + u32 lib_count; + struct lib_info lib[HDA_MAX_LIB]; } __packed; +enum skl_tkn_dir { + SKL_DIR_IN, + SKL_DIR_OUT +}; + +enum skl_tuple_type { + SKL_TYPE_TUPLE, + SKL_TYPE_DATA +}; + #endif diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index e3e764167765..da5db5098274 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -26,6 +26,7 @@ #include <linux/pm_runtime.h> #include <linux/platform_device.h> #include <linux/firmware.h> +#include <linux/delay.h> #include <sound/pcm.h> #include "../common/sst-acpi.h" #include <sound/hda_register.h> @@ -109,6 +110,52 @@ static int skl_init_chip(struct hdac_bus *bus, bool full_reset) return ret; } +void skl_update_d0i3c(struct device *dev, bool enable) +{ + struct pci_dev *pci = to_pci_dev(dev); + struct hdac_ext_bus *ebus = pci_get_drvdata(pci); + struct hdac_bus *bus = ebus_to_hbus(ebus); + u8 reg; + int timeout = 50; + + reg = snd_hdac_chip_readb(bus, VS_D0I3C); + /* Do not write to D0I3C until command in progress bit is cleared */ + while ((reg & AZX_REG_VS_D0I3C_CIP) && --timeout) { + udelay(10); + reg = snd_hdac_chip_readb(bus, VS_D0I3C); + } + + /* Highly unlikely. But if it happens, flag error explicitly */ + if (!timeout) { + dev_err(bus->dev, "Before D0I3C update: D0I3C CIP timeout\n"); + return; + } + + if (enable) + reg = reg | AZX_REG_VS_D0I3C_I3; + else + reg = reg & (~AZX_REG_VS_D0I3C_I3); + + snd_hdac_chip_writeb(bus, VS_D0I3C, reg); + + timeout = 50; + /* Wait for cmd in progress to be cleared before exiting the function */ + reg = snd_hdac_chip_readb(bus, VS_D0I3C); + while ((reg & AZX_REG_VS_D0I3C_CIP) && --timeout) { + udelay(10); + reg = snd_hdac_chip_readb(bus, VS_D0I3C); + } + + /* Highly unlikely. But if it happens, flag error explicitly */ + if (!timeout) { + dev_err(bus->dev, "After D0I3C update: D0I3C CIP timeout\n"); + return; + } + + dev_dbg(bus->dev, "D0I3C register = 0x%x\n", + snd_hdac_chip_readb(bus, VS_D0I3C)); +} + /* called from IRQ */ static void skl_stream_update(struct hdac_bus *bus, struct hdac_stream *hstr) { @@ -181,6 +228,15 @@ static int skl_acquire_irq(struct hdac_ext_bus *ebus, int do_disconnect) return 0; } +static int skl_suspend_late(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + struct hdac_ext_bus *ebus = pci_get_drvdata(pci); + struct skl *skl = ebus_to_skl(ebus); + + return skl_suspend_late_dsp(skl); +} + #ifdef CONFIG_PM static int _skl_suspend(struct hdac_ext_bus *ebus) { @@ -243,7 +299,6 @@ static int skl_suspend(struct device *dev) enable_irq_wake(bus->irq); pci_save_state(pci); - pci_disable_device(pci); } else { ret = _skl_suspend(ebus); if (ret < 0) @@ -286,7 +341,6 @@ static int skl_resume(struct device *dev) */ if (skl->supend_active) { pci_restore_state(pci); - ret = pci_enable_device(pci); snd_hdac_ext_bus_link_power_up_all(ebus); disable_irq_wake(bus->irq); /* @@ -345,6 +399,7 @@ static int skl_runtime_resume(struct device *dev) static const struct dev_pm_ops skl_pm = { SET_SYSTEM_SLEEP_PM_OPS(skl_suspend, skl_resume) SET_RUNTIME_PM_OPS(skl_runtime_suspend, skl_runtime_resume, NULL) + .suspend_late = skl_suspend_late, }; /* @@ -587,7 +642,7 @@ static int skl_first_init(struct hdac_ext_bus *ebus) return -ENXIO; } - snd_hdac_ext_bus_parse_capabilities(ebus); + snd_hdac_bus_parse_capabilities(bus); if (skl_acquire_irq(ebus, 0) < 0) return -EBUSY; @@ -674,7 +729,7 @@ static int skl_probe(struct pci_dev *pci, if (skl->nhlt == NULL) { err = -ENODEV; - goto out_free; + goto out_display_power_off; } skl_nhlt_update_topology_bin(skl); @@ -684,7 +739,7 @@ static int skl_probe(struct pci_dev *pci, skl_dmic_data.dmic_num = skl_get_dmic_geo(skl); /* check if dsp is there */ - if (ebus->ppcap) { + if (bus->ppcap) { err = skl_machine_device_register(skl, (void *)pci_id->driver_data); if (err < 0) @@ -698,7 +753,7 @@ static int skl_probe(struct pci_dev *pci, skl->skl_sst->enable_miscbdcge = skl_enable_miscbdcge; } - if (ebus->mlcap) + if (bus->mlcap) snd_hdac_ext_bus_get_ml_capabilities(ebus); /* create device for soc dmic */ @@ -746,6 +801,9 @@ out_mach_free: skl_machine_device_unregister(skl); out_nhlt_free: skl_nhlt_free(skl->nhlt); +out_display_power_off: + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) + snd_hdac_display_power(bus, false); out_free: skl->init_failed = 1; skl_free(ebus); @@ -785,8 +843,7 @@ static void skl_remove(struct pci_dev *pci) release_firmware(skl->tplg); - if (pci_dev_run_wake(pci)) - pm_runtime_get_noresume(&pci->dev); + pm_runtime_get_noresume(&pci->dev); /* codec removal, invoke bus_device_remove */ snd_hdac_ext_bus_device_remove(ebus); diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 9064e5b0d676..4986e3929dd3 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -52,6 +52,9 @@ #define AZX_PGCTL_LSRMD_MASK (1 << 4) #define AZX_PCIREG_CGCTL 0x48 #define AZX_CGCTL_MISCBDCGE_MASK (1 << 6) +/* D0I3C Register fields */ +#define AZX_REG_VS_D0I3C_CIP 0x1 /* Command in progress */ +#define AZX_REG_VS_D0I3C_I3 0x4 /* D0i3 enable */ struct skl_dsp_resource { u32 max_mcps; @@ -105,6 +108,7 @@ struct skl_dsp_ops { int irq, const char *fw_name, struct skl_dsp_loader_ops loader_ops, struct skl_sst **skl_sst); + int (*init_fw)(struct device *dev, struct skl_sst *ctx); void (*cleanup)(struct device *dev, struct skl_sst *ctx); }; @@ -120,7 +124,11 @@ int skl_get_dmic_geo(struct skl *skl); int skl_nhlt_update_topology_bin(struct skl *skl); int skl_init_dsp(struct skl *skl); int skl_free_dsp(struct skl *skl); +int skl_suspend_late_dsp(struct skl *skl); int skl_suspend_dsp(struct skl *skl); int skl_resume_dsp(struct skl *skl); void skl_cleanup_resources(struct skl *skl); +const struct skl_dsp_ops *skl_get_dsp_ops(int pci_id); +void skl_update_d0i3c(struct device *dev, bool enable); + #endif /* __SOUND_SOC_SKL_H */ diff --git a/sound/soc/kirkwood/armada-370-db.c b/sound/soc/kirkwood/armada-370-db.c index e0304d544f26..677a48d7b891 100644 --- a/sound/soc/kirkwood/armada-370-db.c +++ b/sound/soc/kirkwood/armada-370-db.c @@ -42,7 +42,7 @@ static int a370db_hw_params(struct snd_pcm_substream *substream, return snd_soc_dai_set_sysclk(codec_dai, 0, freq, SND_SOC_CLOCK_IN); } -static struct snd_soc_ops a370db_ops = { +static const struct snd_soc_ops a370db_ops = { .hw_params = a370db_hw_params, }; diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c index dbfdfe99c69d..dafd22e874e9 100644 --- a/sound/soc/kirkwood/kirkwood-dma.c +++ b/sound/soc/kirkwood/kirkwood-dma.c @@ -242,7 +242,7 @@ static snd_pcm_uframes_t kirkwood_dma_pointer(struct snd_pcm_substream return count; } -static struct snd_pcm_ops kirkwood_dma_ops = { +static const struct snd_pcm_ops kirkwood_dma_ops = { .open = kirkwood_dma_open, .close = kirkwood_dma_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.c b/sound/soc/mediatek/common/mtk-afe-fe-dai.c index b788791b0a35..ac231d33d8fe 100644 --- a/sound/soc/mediatek/common/mtk-afe-fe-dai.c +++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.c @@ -23,7 +23,8 @@ #define AFE_BASE_END_OFFSET 8 -int mtk_regmap_update_bits(struct regmap *map, int reg, unsigned int mask, +static int mtk_regmap_update_bits(struct regmap *map, int reg, + unsigned int mask, unsigned int val) { if (reg < 0) @@ -31,7 +32,7 @@ int mtk_regmap_update_bits(struct regmap *map, int reg, unsigned int mask, return regmap_update_bits(map, reg, mask, val); } -int mtk_regmap_write(struct regmap *map, int reg, unsigned int val) +static int mtk_regmap_write(struct regmap *map, int reg, unsigned int val) { if (reg < 0) return 0; diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index 13631003cb7c..a002ab892772 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -735,6 +735,11 @@ static int mxs_saif_probe(struct platform_device *pdev) else saif->id = ret; + if (saif->id >= ARRAY_SIZE(mxs_saif)) { + dev_err(&pdev->dev, "get wrong saif id\n"); + return -EINVAL; + } + /* * If there is no "fsl,saif-master" phandle, it's a saif * master. Otherwise, it's a slave and its phandle points @@ -749,11 +754,11 @@ static int mxs_saif_probe(struct platform_device *pdev) return ret; else saif->master_id = ret; - } - if (saif->master_id >= ARRAY_SIZE(mxs_saif)) { - dev_err(&pdev->dev, "get wrong master id\n"); - return -EINVAL; + if (saif->master_id >= ARRAY_SIZE(mxs_saif)) { + dev_err(&pdev->dev, "get wrong master id\n"); + return -EINVAL; + } } mxs_saif[saif->id] = saif; diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index 2b23ffbac6b1..a96276e77332 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -68,7 +68,7 @@ static int mxs_sgtl5000_hw_params(struct snd_pcm_substream *substream, return 0; } -static struct snd_soc_ops mxs_sgtl5000_hifi_ops = { +static const struct snd_soc_ops mxs_sgtl5000_hifi_ops = { .hw_params = mxs_sgtl5000_hw_params, }; diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c index 76ce33199bf9..06fec5699cc8 100644 --- a/sound/soc/omap/mcbsp.c +++ b/sound/soc/omap/mcbsp.c @@ -221,7 +221,8 @@ void omap_mcbsp_config(struct omap_mcbsp *mcbsp, /* Enable TX/RX sync error interrupts by default */ if (mcbsp->irq) - MCBSP_WRITE(mcbsp, IRQEN, RSYNCERREN | XSYNCERREN); + MCBSP_WRITE(mcbsp, IRQEN, RSYNCERREN | XSYNCERREN | + RUNDFLEN | ROVFLEN | XUNDFLEN | XOVFLEN); } /** diff --git a/sound/soc/omap/omap-abe-twl6040.c b/sound/soc/omap/omap-abe-twl6040.c index f61b3b58083b..89fe95e877db 100644 --- a/sound/soc/omap/omap-abe-twl6040.c +++ b/sound/soc/omap/omap-abe-twl6040.c @@ -305,23 +305,14 @@ static int omap_abe_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, priv); - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) - dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", + dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n", ret); return ret; } -static int omap_abe_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - - return 0; -} - static const struct of_device_id omap_abe_of_match[] = { {.compatible = "ti,abe-twl6040", }, { }, @@ -335,7 +326,6 @@ static struct platform_driver omap_abe_driver = { .of_match_table = omap_abe_of_match, }, .probe = omap_abe_probe, - .remove = omap_abe_remove, }; static int __init omap_abe_init(void) diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c index a84f677234f0..94e9ff791f3a 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c @@ -58,7 +58,7 @@ static void omap_pcm_limit_supported_formats(void) { int i; - for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) { + for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) { switch (snd_pcm_format_physical_width(i)) { case 8: case 16: diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index f2bf8661dd21..823b5a236d8d 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -208,7 +208,7 @@ config SND_PXA2XX_SOC_IMOTE2 config SND_MMP_SOC_BROWNSTONE tristate "SoC Audio support for Marvell Brownstone" - depends on SND_MMP_SOC && MACH_BROWNSTONE + depends on SND_MMP_SOC && MACH_BROWNSTONE && I2C select SND_MMP_SOC_SSPA select MFD_WM8994 select SND_SOC_WM8994 diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c index dcbb7aa9830c..311774e9ca46 100644 --- a/sound/soc/pxa/corgi.c +++ b/sound/soc/pxa/corgi.c @@ -244,9 +244,9 @@ static const struct snd_soc_dapm_route corgi_audio_map[] = { {"MICIN", NULL, "Line Jack"}, }; -static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset", - "Off"}; -static const char *spk_function[] = {"On", "Off"}; +static const char * const jack_function[] = {"Headphone", "Mic", "Line", + "Headset", "Off"}; +static const char * const spk_function[] = {"On", "Off"}; static const struct soc_enum corgi_enum[] = { SOC_ENUM_SINGLE_EXT(5, jack_function), SOC_ENUM_SINGLE_EXT(2, spk_function), diff --git a/sound/soc/pxa/e740_wm9705.c b/sound/soc/pxa/e740_wm9705.c index 1de876529aa1..086c37a85630 100644 --- a/sound/soc/pxa/e740_wm9705.c +++ b/sound/soc/pxa/e740_wm9705.c @@ -22,7 +22,6 @@ #include <asm/mach-types.h> -#include "../codecs/wm9705.h" #include "pxa2xx-ac97.h" diff --git a/sound/soc/pxa/e750_wm9705.c b/sound/soc/pxa/e750_wm9705.c index b7eb7cd5df7d..7823278012a6 100644 --- a/sound/soc/pxa/e750_wm9705.c +++ b/sound/soc/pxa/e750_wm9705.c @@ -22,7 +22,6 @@ #include <asm/mach-types.h> -#include "../codecs/wm9705.h" #include "pxa2xx-ac97.h" static int e750_spk_amp_event(struct snd_soc_dapm_widget *w, diff --git a/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c index 41bf71466a7b..07b9c6e17df9 100644 --- a/sound/soc/pxa/e800_wm9712.c +++ b/sound/soc/pxa/e800_wm9712.c @@ -21,7 +21,6 @@ #include <mach/audio.h> #include <mach/eseries-gpio.h> -#include "../codecs/wm9712.h" #include "pxa2xx-ac97.h" static int e800_spk_amp_event(struct snd_soc_dapm_widget *w, diff --git a/sound/soc/pxa/em-x270.c b/sound/soc/pxa/em-x270.c index 64743a05aeae..966163d1c813 100644 --- a/sound/soc/pxa/em-x270.c +++ b/sound/soc/pxa/em-x270.c @@ -30,7 +30,6 @@ #include <asm/mach-types.h> #include <mach/audio.h> -#include "../codecs/wm9712.h" #include "pxa2xx-ac97.h" static struct snd_soc_dai_link em_x270_dai[] = { diff --git a/sound/soc/pxa/hx4700.c b/sound/soc/pxa/hx4700.c index ecbf2873b7ff..85483049b916 100644 --- a/sound/soc/pxa/hx4700.c +++ b/sound/soc/pxa/hx4700.c @@ -27,8 +27,6 @@ #include <asm/mach-types.h> #include "pxa2xx-i2s.h" -#include "../codecs/ak4641.h" - static struct snd_soc_jack hs_jack; /* Headphones jack detection DAPM pin */ diff --git a/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c index 62b8377a9d2b..2d4d4455fe87 100644 --- a/sound/soc/pxa/magician.c +++ b/sound/soc/pxa/magician.c @@ -376,7 +376,7 @@ static const struct snd_soc_dapm_route audio_map[] = { {"VINM", NULL, "Call Mic"}, }; -static const char *input_select[] = {"Call Mic", "Headset Mic"}; +static const char * const input_select[] = {"Call Mic", "Headset Mic"}; static const struct soc_enum magician_in_sel_enum = SOC_ENUM_SINGLE_EXT(2, input_select); diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c index d1661fa6ee08..0fe0abec8fc4 100644 --- a/sound/soc/pxa/mioa701_wm9713.c +++ b/sound/soc/pxa/mioa701_wm9713.c @@ -187,7 +187,7 @@ static int mioa701_wm9713_probe(struct platform_device *pdev) mioa701.dev = &pdev->dev; rc = devm_snd_soc_register_card(&pdev->dev, &mioa701); if (!rc) - dev_warn(&pdev->dev, "Be warned that incorrect mixers/muxes setup will" + dev_warn(&pdev->dev, "Be warned that incorrect mixers/muxes setup will " "lead to overheating and possible destruction of your device." " Do not use without a good knowledge of mio's board design!\n"); return rc; diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c index bcc81e920a67..387492d46b6c 100644 --- a/sound/soc/pxa/palm27x.c +++ b/sound/soc/pxa/palm27x.c @@ -27,7 +27,6 @@ #include <mach/audio.h> #include <linux/platform_data/asoc-palm27x.h> -#include "../codecs/wm9712.h" #include "pxa2xx-ac97.h" static struct snd_soc_jack hs_jack; diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c index 4b3b714f5ee7..a879aba0691f 100644 --- a/sound/soc/pxa/poodle.c +++ b/sound/soc/pxa/poodle.c @@ -209,8 +209,8 @@ static const struct snd_soc_dapm_route poodle_audio_map[] = { {"MICIN", NULL, "Microphone"}, }; -static const char *jack_function[] = {"Off", "Headphone"}; -static const char *spk_function[] = {"Off", "On"}; +static const char * const jack_function[] = {"Off", "Headphone"}; +static const char * const spk_function[] = {"Off", "On"}; static const struct soc_enum poodle_enum[] = { SOC_ENUM_SINGLE_EXT(2, jack_function), SOC_ENUM_SINGLE_EXT(2, spk_function), diff --git a/sound/soc/pxa/pxa-ssp.h b/sound/soc/pxa/pxa-ssp.h index bc79da221c0d..abf6ec080258 100644 --- a/sound/soc/pxa/pxa-ssp.h +++ b/sound/soc/pxa/pxa-ssp.h @@ -9,12 +9,6 @@ #ifndef _PXA_SSP_H #define _PXA_SSP_H -/* pxa DAI SSP IDs */ -#define PXA_DAI_SSP1 0 -#define PXA_DAI_SSP2 1 -#define PXA_DAI_SSP3 2 -#define PXA_DAI_SSP4 3 - /* SSP clock sources */ #define PXA_SSP_CLK_PLL 0 #define PXA_SSP_CLK_EXT 1 diff --git a/sound/soc/pxa/pxa2xx-i2s.h b/sound/soc/pxa/pxa2xx-i2s.h index 070f3c6059fe..7e218e2105a9 100644 --- a/sound/soc/pxa/pxa2xx-i2s.h +++ b/sound/soc/pxa/pxa2xx-i2s.h @@ -9,9 +9,6 @@ #ifndef _PXA2XX_I2S_H #define _PXA2XX_I2S_H -/* pxa2xx DAI ID's */ -#define PXA2XX_DAI_I2S 0 - /* I2S clock */ #define PXA2XX_I2S_SYSCLK 0 diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c index 0e02634c8b7f..07d77cddac60 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c @@ -241,9 +241,9 @@ static const struct snd_soc_dapm_route spitz_audio_map[] = { {"LINPUT1", NULL, "Line Jack"}, }; -static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset", - "Off"}; -static const char *spk_function[] = {"On", "Off"}; +static const char * const jack_function[] = {"Headphone", "Mic", "Line", + "Headset", "Off"}; +static const char * const spk_function[] = {"On", "Off"}; static const struct soc_enum spitz_enum[] = { SOC_ENUM_SINGLE_EXT(5, jack_function), SOC_ENUM_SINGLE_EXT(2, spk_function), diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c index c508f024ecfb..2e312c62e3c7 100644 --- a/sound/soc/pxa/tosa.c +++ b/sound/soc/pxa/tosa.c @@ -31,7 +31,6 @@ #include <mach/tosa.h> #include <mach/audio.h> -#include "../codecs/wm9712.h" #include "pxa2xx-ac97.h" #define TOSA_HP 0 @@ -170,9 +169,9 @@ static const struct snd_soc_dapm_route audio_map[] = { {"Mic Bias", NULL, "Headset Jack"}, }; -static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset", - "Off"}; -static const char *spk_function[] = {"On", "Off"}; +static const char * const jack_function[] = {"Headphone", "Mic", "Line", + "Headset", "Off"}; +static const char * const spk_function[] = {"On", "Off"}; static const struct soc_enum tosa_enum[] = { SOC_ENUM_SINGLE_EXT(5, jack_function), SOC_ENUM_SINGLE_EXT(2, spk_function), diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c index 1289543c8fb2..d084d7468299 100644 --- a/sound/soc/qcom/apq8016_sbc.c +++ b/sound/soc/qcom/apq8016_sbc.c @@ -85,6 +85,15 @@ static struct apq8016_sbc_data *apq8016_sbc_parse_of(struct snd_soc_card *card) return ERR_PTR(ret); } + /* DAPM routes */ + if (of_property_read_bool(node, "qcom,audio-routing")) { + ret = snd_soc_of_parse_audio_routing(card, + "qcom,audio-routing"); + if (ret) + return ERR_PTR(ret); + } + + /* Populate links */ num_links = of_get_child_count(node); @@ -114,20 +123,15 @@ static struct apq8016_sbc_data *apq8016_sbc_parse_of(struct snd_soc_card *card) return ERR_PTR(-EINVAL); } - link->codec_of_node = of_parse_phandle(codec, "sound-dai", 0); - if (!link->codec_of_node) { - dev_err(card->dev, "error getting codec phandle\n"); - return ERR_PTR(-EINVAL); - } - ret = snd_soc_of_get_dai_name(cpu, &link->cpu_dai_name); if (ret) { dev_err(card->dev, "error getting cpu dai name\n"); return ERR_PTR(ret); } - ret = snd_soc_of_get_dai_name(codec, &link->codec_dai_name); - if (ret) { + ret = snd_soc_of_get_dai_link_codecs(dev, codec, link); + + if (ret < 0) { dev_err(card->dev, "error getting codec dai name\n"); return ERR_PTR(ret); } @@ -147,6 +151,15 @@ static struct apq8016_sbc_data *apq8016_sbc_parse_of(struct snd_soc_card *card) return data; } +static const struct snd_soc_dapm_widget apq8016_sbc_dapm_widgets[] = { + + SND_SOC_DAPM_MIC("Handset Mic", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Secondary Mic", NULL), + SND_SOC_DAPM_MIC("Digital Mic1", NULL), + SND_SOC_DAPM_MIC("Digital Mic2", NULL), +}; + static int apq8016_sbc_platform_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -159,6 +172,8 @@ static int apq8016_sbc_platform_probe(struct platform_device *pdev) return -ENOMEM; card->dev = dev; + card->dapm_widgets = apq8016_sbc_dapm_widgets; + card->num_dapm_widgets = ARRAY_SIZE(apq8016_sbc_dapm_widgets); data = apq8016_sbc_parse_of(card); if (IS_ERR(data)) { dev_err(&pdev->dev, "Error resolving dai links: %ld\n", diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index 3cde9fb977fa..eff3f9a8b685 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -586,3 +586,6 @@ int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev) return 0; } EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_remove); + +MODULE_DESCRIPTION("QTi LPASS CPU Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index db000c6987a1..dd5bdd0da730 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -25,8 +25,7 @@ #include "lpass.h" struct lpass_pcm_data { - int rdma_ch; - int wrdma_ch; + int dma_ch; int i2s_port; }; @@ -61,7 +60,41 @@ static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - int ret; + struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai; + struct lpass_data *drvdata = + snd_soc_platform_get_drvdata(soc_runtime->platform); + struct lpass_variant *v = drvdata->variant; + int ret, dma_ch, dir = substream->stream; + struct lpass_pcm_data *data; + + data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->i2s_port = cpu_dai->driver->id; + runtime->private_data = data; + + dma_ch = 0; + if (v->alloc_dma_channel) + dma_ch = v->alloc_dma_channel(drvdata, dir); + else + dma_ch = 0; + + if (dma_ch < 0) + return dma_ch; + + drvdata->substream[dma_ch] = substream; + + ret = regmap_write(drvdata->lpaif_map, + LPAIF_DMACTL_REG(v, dma_ch, dir), 0); + if (ret) { + dev_err(soc_runtime->dev, + "%s() error writing to rdmactl reg: %d\n", + __func__, ret); + return ret; + } + + data->dma_ch = dma_ch; snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware); @@ -80,13 +113,32 @@ static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream) return 0; } +static int lpass_platform_pcmops_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; + struct lpass_data *drvdata = + snd_soc_platform_get_drvdata(soc_runtime->platform); + struct lpass_variant *v = drvdata->variant; + struct lpass_pcm_data *data; + + data = runtime->private_data; + v = drvdata->variant; + drvdata->substream[data->dma_ch] = NULL; + if (v->free_dma_channel) + v->free_dma_channel(drvdata, data->dma_ch); + + return 0; +} + static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime); struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); + struct snd_pcm_runtime *rt = substream->runtime; + struct lpass_pcm_data *pcm_data = rt->private_data; struct lpass_variant *v = drvdata->variant; snd_pcm_format_t format = params_format(params); unsigned int channels = params_channels(params); @@ -95,10 +147,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, int bitwidth; int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start; - if (dir == SNDRV_PCM_STREAM_PLAYBACK) - ch = pcm_data->rdma_ch; - else - ch = pcm_data->wrdma_ch; + ch = pcm_data->dma_ch; bitwidth = snd_pcm_format_width(format); if (bitwidth < 0) { @@ -177,18 +226,15 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime); struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); + struct snd_pcm_runtime *rt = substream->runtime; + struct lpass_pcm_data *pcm_data = rt->private_data; struct lpass_variant *v = drvdata->variant; unsigned int reg; int ret; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - reg = LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch); - else - reg = LPAIF_WRDMACTL_REG(v, pcm_data->wrdma_ch); - + reg = LPAIF_DMACTL_REG(v, pcm_data->dma_ch, substream->stream); ret = regmap_write(drvdata->lpaif_map, reg, 0); if (ret) dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", @@ -201,16 +247,14 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime); struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); + struct snd_pcm_runtime *rt = substream->runtime; + struct lpass_pcm_data *pcm_data = rt->private_data; struct lpass_variant *v = drvdata->variant; int ret, ch, dir = substream->stream; - if (dir == SNDRV_PCM_STREAM_PLAYBACK) - ch = pcm_data->rdma_ch; - else - ch = pcm_data->wrdma_ch; + ch = pcm_data->dma_ch; ret = regmap_write(drvdata->lpaif_map, LPAIF_DMABASE_REG(v, ch, dir), @@ -255,16 +299,14 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime); struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); + struct snd_pcm_runtime *rt = substream->runtime; + struct lpass_pcm_data *pcm_data = rt->private_data; struct lpass_variant *v = drvdata->variant; int ret, ch, dir = substream->stream; - if (dir == SNDRV_PCM_STREAM_PLAYBACK) - ch = pcm_data->rdma_ch; - else - ch = pcm_data->wrdma_ch; + ch = pcm_data->dma_ch; switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -331,17 +373,15 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer( struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime); struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); + struct snd_pcm_runtime *rt = substream->runtime; + struct lpass_pcm_data *pcm_data = rt->private_data; struct lpass_variant *v = drvdata->variant; unsigned int base_addr, curr_addr; int ret, ch, dir = substream->stream; - if (dir == SNDRV_PCM_STREAM_PLAYBACK) - ch = pcm_data->rdma_ch; - else - ch = pcm_data->wrdma_ch; + ch = pcm_data->dma_ch; ret = regmap_read(drvdata->lpaif_map, LPAIF_DMABASE_REG(v, ch, dir), &base_addr); @@ -372,8 +412,9 @@ static int lpass_platform_pcmops_mmap(struct snd_pcm_substream *substream, runtime->dma_bytes); } -static struct snd_pcm_ops lpass_platform_pcm_ops = { +static const struct snd_pcm_ops lpass_platform_pcm_ops = { .open = lpass_platform_pcmops_open, + .close = lpass_platform_pcmops_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = lpass_platform_pcmops_hw_params, .hw_free = lpass_platform_pcmops_hw_free, @@ -470,117 +511,45 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime) { struct snd_pcm *pcm = soc_runtime->pcm; struct snd_pcm_substream *psubstream, *csubstream; - struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai; - struct lpass_data *drvdata = - snd_soc_platform_get_drvdata(soc_runtime->platform); - struct lpass_variant *v = drvdata->variant; int ret = -EINVAL; - struct lpass_pcm_data *data; size_t size = lpass_platform_pcm_hardware.buffer_bytes_max; - data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - data->i2s_port = cpu_dai->driver->id; - snd_soc_pcm_set_drvdata(soc_runtime, data); - psubstream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; if (psubstream) { - if (v->alloc_dma_channel) - data->rdma_ch = v->alloc_dma_channel(drvdata, - SNDRV_PCM_STREAM_PLAYBACK); - - if (data->rdma_ch < 0) - return data->rdma_ch; - - drvdata->substream[data->rdma_ch] = psubstream; - ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, soc_runtime->platform->dev, size, &psubstream->dma_buffer); - if (ret) - goto playback_alloc_err; - - ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(v, data->rdma_ch), 0); if (ret) { - dev_err(soc_runtime->dev, - "%s() error writing to rdmactl reg: %d\n", - __func__, ret); - goto capture_alloc_err; + dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n"); + return ret; } } csubstream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; if (csubstream) { - if (v->alloc_dma_channel) - data->wrdma_ch = v->alloc_dma_channel(drvdata, - SNDRV_PCM_STREAM_CAPTURE); - - if (data->wrdma_ch < 0) { - ret = data->wrdma_ch; - goto capture_alloc_err; - } - - drvdata->substream[data->wrdma_ch] = csubstream; - ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, soc_runtime->platform->dev, size, &csubstream->dma_buffer); - if (ret) - goto capture_alloc_err; - - ret = regmap_write(drvdata->lpaif_map, - LPAIF_WRDMACTL_REG(v, data->wrdma_ch), 0); if (ret) { - dev_err(soc_runtime->dev, - "%s() error writing to wrdmactl reg: %d\n", - __func__, ret); - goto capture_reg_err; + dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n"); + if (psubstream) + snd_dma_free_pages(&psubstream->dma_buffer); + return ret; } + } return 0; - -capture_reg_err: - if (csubstream) - snd_dma_free_pages(&csubstream->dma_buffer); - -capture_alloc_err: - if (psubstream) - snd_dma_free_pages(&psubstream->dma_buffer); - - playback_alloc_err: - dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n"); - - return ret; } static void lpass_platform_pcm_free(struct snd_pcm *pcm) { - struct snd_soc_pcm_runtime *rt; - struct lpass_data *drvdata; - struct lpass_pcm_data *data; - struct lpass_variant *v; struct snd_pcm_substream *substream; - int ch, i; + int i; for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) { substream = pcm->streams[i].substream; if (substream) { - rt = substream->private_data; - data = snd_soc_pcm_get_drvdata(rt); - drvdata = snd_soc_platform_get_drvdata(rt->platform); - - ch = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - ? data->rdma_ch - : data->wrdma_ch; - v = drvdata->variant; - drvdata->substream[ch] = NULL; - if (v->free_dma_channel) - v->free_dma_channel(drvdata, ch); - snd_dma_free_pages(&substream->dma_buffer); substream->dma_buffer.area = NULL; substream->dma_buffer.addr = 0; diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h index 30714ad1e138..924971b6ded5 100644 --- a/sound/soc/qcom/lpass.h +++ b/sound/soc/qcom/lpass.h @@ -58,6 +58,7 @@ struct lpass_data { /* 8016 specific */ struct clk *pcnoc_mport_clk; struct clk *pcnoc_sway_clk; + }; /* Vairant data per each SOC */ diff --git a/sound/soc/qcom/storm.c b/sound/soc/qcom/storm.c index 2d833bffdba0..8fcac2ac3aa6 100644 --- a/sound/soc/qcom/storm.c +++ b/sound/soc/qcom/storm.c @@ -58,7 +58,7 @@ static int storm_ops_hw_params(struct snd_pcm_substream *substream, return 0; } -static struct snd_soc_ops storm_soc_ops = { +static const struct snd_soc_ops storm_soc_ops = { .hw_params = storm_ops_hw_params, }; diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig index f1e0c703e0d2..c783f9a22595 100644 --- a/sound/soc/rockchip/Kconfig +++ b/sound/soc/rockchip/Kconfig @@ -41,3 +41,15 @@ config SND_SOC_ROCKCHIP_RT5645 help Say Y or M here if you want to add support for SoC audio on Rockchip boards using the RT5645/RT5650 codec, such as Veyron. + +config SND_SOC_RK3399_GRU_SOUND + tristate "ASoC support multiple codecs for Rockchip RK3399 GRU boards" + depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP && SPI + select SND_SOC_ROCKCHIP_I2S + select SND_SOC_MAX98357A + select SND_SOC_RT5514 + select SND_SOC_DA7219 + select SND_SOC_RT5514_SPI + help + Say Y or M here if you want to add support multiple codecs for SoC + audio on Rockchip RK3399 GRU boards. diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile index c0bf560125f3..84e5c7c700e7 100644 --- a/sound/soc/rockchip/Makefile +++ b/sound/soc/rockchip/Makefile @@ -7,6 +7,8 @@ obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o snd-soc-rockchip-max98090-objs := rockchip_max98090.o snd-soc-rockchip-rt5645-objs := rockchip_rt5645.o +snd-soc-rk3399-gru-sound-objs := rk3399_gru_sound.o obj-$(CONFIG_SND_SOC_ROCKCHIP_MAX98090) += snd-soc-rockchip-max98090.o obj-$(CONFIG_SND_SOC_ROCKCHIP_RT5645) += snd-soc-rockchip-rt5645.o +obj-$(CONFIG_SND_SOC_RK3399_GRU_SOUND) += snd-soc-rk3399-gru-sound.o diff --git a/sound/soc/rockchip/rk3399_gru_sound.c b/sound/soc/rockchip/rk3399_gru_sound.c new file mode 100644 index 000000000000..3475c61a5fa0 --- /dev/null +++ b/sound/soc/rockchip/rk3399_gru_sound.c @@ -0,0 +1,397 @@ +/* + * Rockchip machine ASoC driver for boards using MAX98357A/RT5514/DA7219 + * + * Copyright (c) 2016, ROCKCHIP CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/delay.h> +#include <linux/spi/spi.h> +#include <linux/input.h> +#include <sound/core.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include "rockchip_i2s.h" +#include "../codecs/da7219.h" +#include "../codecs/da7219-aad.h" +#include "../codecs/rt5514.h" + +#define DRV_NAME "rk3399-gru-sound" + +#define SOUND_FS 256 + +static unsigned int rt5514_dmic_delay; + +static struct snd_soc_jack rockchip_sound_jack; + +static const struct snd_soc_dapm_widget rockchip_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_SPK("Speakers", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), +}; + +static const struct snd_soc_dapm_route rockchip_dapm_routes[] = { + /* Input Lines */ + {"MIC", NULL, "Headset Mic"}, + {"DMIC1L", NULL, "Int Mic"}, + {"DMIC1R", NULL, "Int Mic"}, + + /* Output Lines */ + {"Headphones", NULL, "HPL"}, + {"Headphones", NULL, "HPR"}, + {"Speakers", NULL, "Speaker"}, +}; + +static const struct snd_kcontrol_new rockchip_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphones"), + SOC_DAPM_PIN_SWITCH("Speakers"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Int Mic"), +}; + +static int rockchip_sound_max98357a_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + unsigned int mclk; + int ret; + + /* max98357a supports these sample rates */ + switch (params_rate(params)) { + case 8000: + case 16000: + case 48000: + case 96000: + mclk = params_rate(params) * SOUND_FS; + break; + default: + dev_err(rtd->card->dev, "%s() doesn't support this sample rate: %d\n", + __func__, params_rate(params)); + return -EINVAL; + } + + ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, 0, mclk, 0); + if (ret) { + dev_err(rtd->card->dev, "%s() error setting sysclk to %u: %d\n", + __func__, mclk, ret); + return ret; + } + + return 0; +} + +static int rockchip_sound_rt5514_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + unsigned int mclk; + int ret; + + mclk = params_rate(params) * SOUND_FS; + + ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, + SND_SOC_CLOCK_OUT); + if (ret < 0) { + dev_err(rtd->card->dev, "Can't set cpu clock out %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, RT5514_SCLK_S_MCLK, + mclk, SND_SOC_CLOCK_IN); + if (ret) { + dev_err(rtd->card->dev, "%s() error setting sysclk to %u: %d\n", + __func__, params_rate(params) * 512, ret); + return ret; + } + + /* Wait for DMIC stable */ + msleep(rt5514_dmic_delay); + + return 0; +} + +static int rockchip_sound_da7219_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int mclk, ret; + + /* in bypass mode, the mclk has to be one of the frequencies below */ + switch (params_rate(params)) { + case 8000: + case 16000: + case 24000: + case 32000: + case 48000: + case 64000: + case 96000: + mclk = 12288000; + break; + case 11025: + case 22050: + case 44100: + case 88200: + mclk = 11289600; + break; + default: + return -EINVAL; + } + + ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, + SND_SOC_CLOCK_OUT); + if (ret < 0) { + dev_err(codec_dai->dev, "Can't set cpu clock out %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(codec_dai->dev, "Can't set codec clock in %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_MCLK, 0, 0); + if (ret < 0) { + dev_err(codec_dai->dev, "Can't set pll sysclk mclk %d\n", ret); + return ret; + } + + return 0; +} + +static int rockchip_sound_da7219_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec_dais[0]->codec; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + /* We need default MCLK and PLL settings for the accessory detection */ + ret = snd_soc_dai_set_sysclk(codec_dai, 0, 12288000, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(codec_dai->dev, "Init can't set codec clock in %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_MCLK, 0, 0); + if (ret < 0) { + dev_err(codec_dai->dev, "Init can't set pll sysclk mclk %d\n", ret); + return ret; + } + + /* Enable Headset and 4 Buttons Jack detection */ + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_LINEOUT | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &rockchip_sound_jack, NULL, 0); + + if (ret) { + dev_err(rtd->card->dev, "New Headset Jack failed! (%d)\n", ret); + return ret; + } + + snd_jack_set_key(rockchip_sound_jack.jack, SND_JACK_BTN_0, KEY_MEDIA); + snd_jack_set_key( + rockchip_sound_jack.jack, SND_JACK_BTN_1, KEY_VOLUMEUP); + snd_jack_set_key( + rockchip_sound_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); + snd_jack_set_key( + rockchip_sound_jack.jack, SND_JACK_BTN_3, KEY_VOICECOMMAND); + + da7219_aad_jack_det(codec, &rockchip_sound_jack); + + return 0; +} + +static const struct snd_soc_ops rockchip_sound_max98357a_ops = { + .hw_params = rockchip_sound_max98357a_hw_params, +}; + +static const struct snd_soc_ops rockchip_sound_rt5514_ops = { + .hw_params = rockchip_sound_rt5514_hw_params, +}; + +static const struct snd_soc_ops rockchip_sound_da7219_ops = { + .hw_params = rockchip_sound_da7219_hw_params, +}; + +enum { + DAILINK_MAX98357A, + DAILINK_RT5514, + DAILINK_DA7219, + DAILINK_RT5514_DSP, +}; + +#define DAILINK_ENTITIES (DAILINK_DA7219 + 1) + +static struct snd_soc_dai_link rockchip_dailinks[] = { + [DAILINK_MAX98357A] = { + .name = "MAX98357A", + .stream_name = "MAX98357A PCM", + .codec_dai_name = "HiFi", + .ops = &rockchip_sound_max98357a_ops, + /* set max98357a as slave */ + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + }, + [DAILINK_RT5514] = { + .name = "RT5514", + .stream_name = "RT5514 PCM", + .codec_dai_name = "rt5514-aif1", + .ops = &rockchip_sound_rt5514_ops, + /* set rt5514 as slave */ + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + }, + [DAILINK_DA7219] = { + .name = "DA7219", + .stream_name = "DA7219 PCM", + .codec_dai_name = "da7219-hifi", + .init = rockchip_sound_da7219_init, + .ops = &rockchip_sound_da7219_ops, + /* set da7219 as slave */ + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + }, + /* RT5514 DSP for voice wakeup via spi bus */ + [DAILINK_RT5514_DSP] = { + .name = "RT5514 DSP", + .stream_name = "Wake on Voice", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + }, +}; + +static struct snd_soc_card rockchip_sound_card = { + .name = "rk3399-gru-sound", + .owner = THIS_MODULE, + .dai_link = rockchip_dailinks, + .num_links = ARRAY_SIZE(rockchip_dailinks), + .dapm_widgets = rockchip_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rockchip_dapm_widgets), + .dapm_routes = rockchip_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rockchip_dapm_routes), + .controls = rockchip_controls, + .num_controls = ARRAY_SIZE(rockchip_controls), +}; + +static int rockchip_sound_match_stub(struct device *dev, void *data) +{ + return 1; +} + +static int rockchip_sound_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &rockchip_sound_card; + struct device_node *cpu_node; + struct device *dev; + struct device_driver *drv; + int i, ret; + + cpu_node = of_parse_phandle(pdev->dev.of_node, "rockchip,cpu", 0); + if (!cpu_node) { + dev_err(&pdev->dev, "Property 'rockchip,cpu' missing or invalid\n"); + return -EINVAL; + } + + for (i = 0; i < DAILINK_ENTITIES; i++) { + rockchip_dailinks[i].platform_of_node = cpu_node; + rockchip_dailinks[i].cpu_of_node = cpu_node; + + rockchip_dailinks[i].codec_of_node = + of_parse_phandle(pdev->dev.of_node, "rockchip,codec", i); + if (!rockchip_dailinks[i].codec_of_node) { + dev_err(&pdev->dev, + "Property[%d] 'rockchip,codec' missing or invalid\n", i); + return -EINVAL; + } + } + + /** + * To acquire the spi driver of the rt5514 and set the dai-links names + * for soc_bind_dai_link + */ + drv = driver_find("rt5514", &spi_bus_type); + if (!drv) { + dev_err(&pdev->dev, "Can not find the rt5514 driver at the spi bus\n"); + return -EINVAL; + } + + dev = driver_find_device(drv, NULL, NULL, rockchip_sound_match_stub); + if (!dev) { + dev_err(&pdev->dev, "Can not find the rt5514 device\n"); + return -ENODEV; + } + + /* Set DMIC delay */ + ret = device_property_read_u32(&pdev->dev, "dmic-delay", + &rt5514_dmic_delay); + if (ret) { + rt5514_dmic_delay = 0; + dev_dbg(&pdev->dev, + "no optional property 'dmic-delay' found, default: no delay\n"); + } + + rockchip_dailinks[DAILINK_RT5514_DSP].cpu_name = kstrdup_const(dev_name(dev), GFP_KERNEL); + rockchip_dailinks[DAILINK_RT5514_DSP].cpu_dai_name = kstrdup_const(dev_name(dev), GFP_KERNEL); + rockchip_dailinks[DAILINK_RT5514_DSP].platform_name = kstrdup_const(dev_name(dev), GFP_KERNEL); + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) + dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", + __func__, ret); + + return ret; +} + +static const struct of_device_id rockchip_sound_of_match[] = { + { .compatible = "rockchip,rk3399-gru-sound", }, + {}, +}; + +static struct platform_driver rockchip_sound_driver = { + .probe = rockchip_sound_probe, + .driver = { + .name = DRV_NAME, + .of_match_table = rockchip_sound_of_match, +#ifdef CONFIG_PM + .pm = &snd_soc_pm_ops, +#endif + }, +}; + +module_platform_driver(rockchip_sound_driver); + +MODULE_AUTHOR("Xing Zheng <zhengxing@rock-chips.com>"); +MODULE_DESCRIPTION("Rockchip ASoC Machine Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, rockchip_sound_of_match); diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 652e8c5ea166..974915cb4c4f 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -57,6 +57,7 @@ static int i2s_runtime_suspend(struct device *dev) { struct rk_i2s_dev *i2s = dev_get_drvdata(dev); + regcache_cache_only(i2s->regmap, true); clk_disable_unprepare(i2s->mclk); return 0; @@ -73,7 +74,14 @@ static int i2s_runtime_resume(struct device *dev) return ret; } - return 0; + regcache_cache_only(i2s->regmap, false); + regcache_mark_dirty(i2s->regmap); + + ret = regcache_sync(i2s->regmap); + if (ret) + clk_disable_unprepare(i2s->mclk); + + return ret; } static inline struct rk_i2s_dev *to_info(struct snd_soc_dai *dai) diff --git a/sound/soc/rockchip/rockchip_max98090.c b/sound/soc/rockchip/rockchip_max98090.c index e70ffad07184..789d6f1e2b5f 100644 --- a/sound/soc/rockchip/rockchip_max98090.c +++ b/sound/soc/rockchip/rockchip_max98090.c @@ -119,7 +119,7 @@ static int rk_aif1_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops rk_aif1_ops = { +static const struct snd_soc_ops rk_aif1_ops = { .hw_params = rk_aif1_hw_params, }; diff --git a/sound/soc/rockchip/rockchip_rt5645.c b/sound/soc/rockchip/rockchip_rt5645.c index 440a8026346a..9e0c17805807 100644 --- a/sound/soc/rockchip/rockchip_rt5645.c +++ b/sound/soc/rockchip/rockchip_rt5645.c @@ -135,7 +135,7 @@ static int rk_init(struct snd_soc_pcm_runtime *runtime) &headset_jack); } -static struct snd_soc_ops rk_aif1_ops = { +static const struct snd_soc_ops rk_aif1_ops = { .hw_params = rk_aif1_hw_params, }; diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index 4ca265737eda..fa8101d1e16f 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -65,6 +65,7 @@ static int __maybe_unused rk_spdif_runtime_suspend(struct device *dev) { struct rk_spdif_dev *spdif = dev_get_drvdata(dev); + regcache_cache_only(spdif->regmap, true); clk_disable_unprepare(spdif->mclk); clk_disable_unprepare(spdif->hclk); @@ -88,7 +89,16 @@ static int __maybe_unused rk_spdif_runtime_resume(struct device *dev) return ret; } - return 0; + regcache_cache_only(spdif->regmap, false); + regcache_mark_dirty(spdif->regmap); + + ret = regcache_sync(spdif->regmap); + if (ret) { + clk_disable_unprepare(spdif->mclk); + clk_disable_unprepare(spdif->hclk); + } + + return ret; } static int rk_spdif_hw_params(struct snd_pcm_substream *substream, diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index 7b722b0094d9..7c423151ef7d 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -1,12 +1,15 @@ -config SND_SOC_SAMSUNG +menuconfig SND_SOC_SAMSUNG tristate "ASoC support for Samsung" - depends on (PLAT_SAMSUNG || ARCH_EXYNOS) + depends on PLAT_SAMSUNG || ARCH_EXYNOS || COMPILE_TEST + depends on COMMON_CLK select SND_SOC_GENERIC_DMAENGINE_PCM - help + ---help--- Say Y or M if you want to add support for codecs attached to the Samsung SoCs' Audio interfaces. You will also need to select the audio interfaces to support below. +if SND_SOC_SAMSUNG + config SND_S3C24XX_I2S tristate @@ -18,22 +21,18 @@ config SND_S3C2412_SOC_I2S select SND_S3C_I2SV2_SOC config SND_SAMSUNG_PCM - tristate - -config SND_SAMSUNG_AC97 - tristate - select SND_SOC_AC97_BUS + tristate "Samsung PCM interface support" config SND_SAMSUNG_SPDIF - tristate + tristate "Samsung SPDIF transmitter support" select SND_SOC_SPDIF config SND_SAMSUNG_I2S - tristate + tristate "Samsung I2S interface support" config SND_SOC_SAMSUNG_NEO1973_WM8753 tristate "Audio support for Openmoko Neo1973 Smartphones (GTA02)" - depends on SND_SOC_SAMSUNG && MACH_NEO1973_GTA02 + depends on MACH_NEO1973_GTA02 select SND_S3C24XX_I2S select SND_SOC_WM8753 select SND_SOC_BT_SCO @@ -43,7 +42,7 @@ config SND_SOC_SAMSUNG_NEO1973_WM8753 config SND_SOC_SAMSUNG_JIVE_WM8750 tristate "SoC I2S Audio support for Jive" - depends on SND_SOC_SAMSUNG && MACH_JIVE && I2C + depends on MACH_JIVE && I2C select SND_SOC_WM8750 select SND_S3C2412_SOC_I2S help @@ -51,7 +50,7 @@ config SND_SOC_SAMSUNG_JIVE_WM8750 config SND_SOC_SAMSUNG_SMDK_WM8580 tristate "SoC I2S Audio support for WM8580 on SMDK" - depends on SND_SOC_SAMSUNG && (MACH_SMDK6410 || MACH_SMDKC100 || MACH_SMDKV210 || MACH_SMDKC110) + depends on MACH_SMDK6410 || COMPILE_TEST depends on I2C select SND_SOC_WM8580 select SND_SAMSUNG_I2S @@ -60,7 +59,6 @@ config SND_SOC_SAMSUNG_SMDK_WM8580 config SND_SOC_SAMSUNG_SMDK_WM8994 tristate "SoC I2S Audio support for WM8994 on SMDK" - depends on SND_SOC_SAMSUNG depends on I2C=y select MFD_WM8994 select SND_SOC_WM8994 @@ -68,29 +66,9 @@ config SND_SOC_SAMSUNG_SMDK_WM8994 help Say Y if you want to add support for SoC audio on the SMDKs. -config SND_SOC_SAMSUNG_SMDK2443_WM9710 - tristate "SoC AC97 Audio support for SMDK2443 - WM9710" - depends on SND_SOC_SAMSUNG && MACH_SMDK2443 - select AC97_BUS - select SND_SOC_AC97_CODEC - select SND_SAMSUNG_AC97 - help - Say Y if you want to add support for SoC audio on smdk2443 - with the WM9710. - -config SND_SOC_SAMSUNG_LN2440SBC_ALC650 - tristate "SoC AC97 Audio support for LN2440SBC - ALC650" - depends on SND_SOC_SAMSUNG && ARCH_S3C24XX - select AC97_BUS - select SND_SOC_AC97_CODEC - select SND_SAMSUNG_AC97 - help - Say Y if you want to add support for SoC audio on ln2440sbc - with the ALC650. - config SND_SOC_SAMSUNG_S3C24XX_UDA134X tristate "SoC I2S Audio support UDA134X wired to a S3C24XX" - depends on SND_SOC_SAMSUNG && ARCH_S3C24XX + depends on ARCH_S3C24XX select SND_S3C24XX_I2S select SND_SOC_L3 select SND_SOC_UDA134X @@ -102,21 +80,21 @@ config SND_SOC_SAMSUNG_SIMTEC config SND_SOC_SAMSUNG_SIMTEC_TLV320AIC23 tristate "SoC I2S Audio support for TLV320AIC23 on Simtec boards" - depends on SND_SOC_SAMSUNG && ARCH_S3C24XX && I2C + depends on ARCH_S3C24XX && I2C select SND_S3C24XX_I2S select SND_SOC_TLV320AIC23_I2C select SND_SOC_SAMSUNG_SIMTEC config SND_SOC_SAMSUNG_SIMTEC_HERMES tristate "SoC I2S Audio support for Simtec Hermes board" - depends on SND_SOC_SAMSUNG && ARCH_S3C24XX && I2C + depends on ARCH_S3C24XX && I2C select SND_S3C24XX_I2S select SND_SOC_TLV320AIC3X select SND_SOC_SAMSUNG_SIMTEC config SND_SOC_SAMSUNG_H1940_UDA1380 tristate "Audio support for the HP iPAQ H1940" - depends on SND_SOC_SAMSUNG && ARCH_H1940 && I2C + depends on ARCH_H1940 && I2C select SND_S3C24XX_I2S select SND_SOC_UDA1380 help @@ -124,45 +102,27 @@ config SND_SOC_SAMSUNG_H1940_UDA1380 config SND_SOC_SAMSUNG_RX1950_UDA1380 tristate "Audio support for the HP iPAQ RX1950" - depends on SND_SOC_SAMSUNG && MACH_RX1950 && I2C + depends on MACH_RX1950 && I2C select SND_S3C24XX_I2S select SND_SOC_UDA1380 help This driver provides audio support for HP iPAQ RX1950 PDA. -config SND_SOC_SAMSUNG_SMDK_WM9713 - tristate "SoC AC97 Audio support for SMDK with WM9713" - depends on SND_SOC_SAMSUNG && (MACH_SMDK6410 || MACH_SMDKC100 || MACH_SMDKV210 || MACH_SMDKC110) - select SND_SOC_WM9713 - select SND_SAMSUNG_AC97 - help - Say Y if you want to add support for SoC audio on the SMDK. - config SND_SOC_SMARTQ tristate "SoC I2S Audio support for SmartQ board" - depends on SND_SOC_SAMSUNG && MACH_SMARTQ && I2C + depends on MACH_SMARTQ || COMPILE_TEST + depends on I2C select SND_SAMSUNG_I2S select SND_SOC_WM8750 config SND_SOC_SAMSUNG_SMDK_SPDIF tristate "SoC S/PDIF Audio support for SMDK" - depends on SND_SOC_SAMSUNG select SND_SAMSUNG_SPDIF help Say Y if you want to add support for SoC S/PDIF audio on the SMDK. -config SND_SOC_SMDK_WM8580_PCM - tristate "SoC PCM Audio support for WM8580 on SMDK" - depends on SND_SOC_SAMSUNG && (MACH_SMDKV210 || MACH_SMDKC110) - depends on I2C - select SND_SOC_WM8580 - select SND_SAMSUNG_PCM - help - Say Y if you want to add support for SoC audio on the SMDK. - config SND_SOC_SMDK_WM8994_PCM tristate "SoC PCM Audio support for WM8994 on SMDK" - depends on SND_SOC_SAMSUNG depends on I2C=y select MFD_WM8994 select SND_SOC_WM8994 @@ -172,7 +132,7 @@ config SND_SOC_SMDK_WM8994_PCM config SND_SOC_SPEYSIDE tristate "Audio support for Wolfson Speyside" - depends on SND_SOC_SAMSUNG && I2C && SPI_MASTER + depends on I2C && SPI_MASTER depends on MACH_WLF_CRAGG_6410 || COMPILE_TEST select SND_SAMSUNG_I2S select SND_SOC_WM8996 @@ -182,14 +142,14 @@ config SND_SOC_SPEYSIDE config SND_SOC_TOBERMORY tristate "Audio support for Wolfson Tobermory" - depends on SND_SOC_SAMSUNG && INPUT && I2C + depends on INPUT && I2C depends on MACH_WLF_CRAGG_6410 || COMPILE_TEST select SND_SAMSUNG_I2S select SND_SOC_WM8962 config SND_SOC_BELLS tristate "Audio support for Wolfson Bells" - depends on SND_SOC_SAMSUNG && MFD_ARIZONA && I2C && SPI_MASTER + depends on MFD_ARIZONA && I2C && SPI_MASTER depends on MACH_WLF_CRAGG_6410 || COMPILE_TEST select SND_SAMSUNG_I2S select SND_SOC_WM5102 @@ -200,7 +160,7 @@ config SND_SOC_BELLS config SND_SOC_LOWLAND tristate "Audio support for Wolfson Lowland" - depends on SND_SOC_SAMSUNG && I2C + depends on I2C depends on MACH_WLF_CRAGG_6410 || COMPILE_TEST select SND_SAMSUNG_I2S select SND_SOC_WM5100 @@ -208,7 +168,7 @@ config SND_SOC_LOWLAND config SND_SOC_LITTLEMILL tristate "Audio support for Wolfson Littlemill" - depends on SND_SOC_SAMSUNG && I2C + depends on I2C depends on MACH_WLF_CRAGG_6410 || COMPILE_TEST select SND_SAMSUNG_I2S select MFD_WM8994 @@ -216,7 +176,7 @@ config SND_SOC_LITTLEMILL config SND_SOC_SNOW tristate "Audio support for Google Snow boards" - depends on SND_SOC_SAMSUNG && I2C + depends on I2C select SND_SOC_MAX98090 select SND_SOC_MAX98095 select SND_SAMSUNG_I2S @@ -226,6 +186,17 @@ config SND_SOC_SNOW config SND_SOC_ARNDALE_RT5631_ALC5631 tristate "Audio support for RT5631(ALC5631) on Arndale Board" - depends on SND_SOC_SAMSUNG && I2C + depends on I2C select SND_SAMSUNG_I2S select SND_SOC_RT5631 + +config SND_SOC_SAMSUNG_TM2_WM5110 + tristate "SoC I2S Audio support for WM5110 on TM2 board" + depends on SND_SOC_SAMSUNG && MFD_ARIZONA && I2C && SPI_MASTER + select SND_SOC_MAX98504 + select SND_SOC_WM5110 + select SND_SAMSUNG_I2S + help + Say Y if you want to add support for SoC audio on the TM2 board. + +endif #SND_SOC_SAMSUNG diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile index 5d03f5ce6916..b5df5e2e3d94 100644 --- a/sound/soc/samsung/Makefile +++ b/sound/soc/samsung/Makefile @@ -3,7 +3,6 @@ snd-soc-s3c-dma-objs := dmaengine.o snd-soc-idma-objs := idma.o snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o -snd-soc-ac97-objs := ac97.o snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o snd-soc-samsung-spdif-objs := spdif.o snd-soc-pcm-objs := pcm.o @@ -11,7 +10,6 @@ snd-soc-i2s-objs := i2s.o obj-$(CONFIG_SND_SOC_SAMSUNG) += snd-soc-s3c-dma.o obj-$(CONFIG_SND_S3C24XX_I2S) += snd-soc-s3c24xx-i2s.o -obj-$(CONFIG_SND_SAMSUNG_AC97) += snd-soc-ac97.o obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o obj-$(CONFIG_SND_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o obj-$(CONFIG_SND_SAMSUNG_SPDIF) += snd-soc-samsung-spdif.o @@ -36,7 +34,6 @@ snd-soc-snow-objs := snow.o snd-soc-smdk-wm9713-objs := smdk_wm9713.o snd-soc-s3c64xx-smartq-wm8987-objs := smartq_wm8987.o snd-soc-smdk-spdif-objs := smdk_spdif.o -snd-soc-smdk-wm8580pcm-objs := smdk_wm8580pcm.o snd-soc-smdk-wm8994pcm-objs := smdk_wm8994pcm.o snd-soc-speyside-objs := speyside.o snd-soc-tobermory-objs := tobermory.o @@ -44,11 +41,10 @@ snd-soc-lowland-objs := lowland.o snd-soc-littlemill-objs := littlemill.o snd-soc-bells-objs := bells.o snd-soc-arndale-rt5631-objs := arndale_rt5631.o +snd-soc-tm2-wm5110-objs := tm2_wm5110.o obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o -obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o -obj-$(CONFIG_SND_SOC_SAMSUNG_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o obj-$(CONFIG_SND_SOC_SAMSUNG_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o obj-$(CONFIG_SND_SOC_SAMSUNG_SIMTEC) += snd-soc-s3c24xx-simtec.o obj-$(CONFIG_SND_SOC_SAMSUNG_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o @@ -58,10 +54,8 @@ obj-$(CONFIG_SND_SOC_SAMSUNG_RX1950_UDA1380) += snd-soc-rx1950-uda1380.o obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_WM8580) += snd-soc-smdk-wm8580.o obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_WM8994) += snd-soc-smdk-wm8994.o obj-$(CONFIG_SND_SOC_SNOW) += snd-soc-snow.o -obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_WM9713) += snd-soc-smdk-wm9713.o obj-$(CONFIG_SND_SOC_SMARTQ) += snd-soc-s3c64xx-smartq-wm8987.o obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_SPDIF) += snd-soc-smdk-spdif.o -obj-$(CONFIG_SND_SOC_SMDK_WM8580_PCM) += snd-soc-smdk-wm8580pcm.o obj-$(CONFIG_SND_SOC_SMDK_WM8994_PCM) += snd-soc-smdk-wm8994pcm.o obj-$(CONFIG_SND_SOC_SPEYSIDE) += snd-soc-speyside.o obj-$(CONFIG_SND_SOC_TOBERMORY) += snd-soc-tobermory.o @@ -69,3 +63,4 @@ obj-$(CONFIG_SND_SOC_LOWLAND) += snd-soc-lowland.o obj-$(CONFIG_SND_SOC_LITTLEMILL) += snd-soc-littlemill.o obj-$(CONFIG_SND_SOC_BELLS) += snd-soc-bells.o obj-$(CONFIG_SND_SOC_ARNDALE_RT5631_ALC5631) += snd-soc-arndale-rt5631.o +obj-$(CONFIG_SND_SOC_SAMSUNG_TM2_WM5110) += snd-soc-tm2-wm5110.o diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c deleted file mode 100644 index 547d31032088..000000000000 --- a/sound/soc/samsung/ac97.c +++ /dev/null @@ -1,437 +0,0 @@ -/* sound/soc/samsung/ac97.c - * - * ALSA SoC Audio Layer - S3C AC97 Controller driver - * Evolved from s3c2443-ac97.c - * - * Copyright (c) 2010 Samsung Electronics Co. Ltd - * Author: Jaswinder Singh <jassisinghbrar@gmail.com> - * Credits: Graeme Gregory, Sean Choi - * - * 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/io.h> -#include <linux/delay.h> -#include <linux/clk.h> -#include <linux/module.h> - -#include <sound/soc.h> - -#include "regs-ac97.h" -#include <linux/platform_data/asoc-s3c.h> - -#include "dma.h" - -#define AC_CMD_ADDR(x) (x << 16) -#define AC_CMD_DATA(x) (x & 0xffff) - -#define S3C_AC97_DAI_PCM 0 -#define S3C_AC97_DAI_MIC 1 - -struct s3c_ac97_info { - struct clk *ac97_clk; - void __iomem *regs; - struct mutex lock; - struct completion done; -}; -static struct s3c_ac97_info s3c_ac97; - -static struct s3c_dma_params s3c_ac97_pcm_out = { - .dma_size = 4, -}; - -static struct s3c_dma_params s3c_ac97_pcm_in = { - .dma_size = 4, -}; - -static struct s3c_dma_params s3c_ac97_mic_in = { - .dma_size = 4, -}; - -static void s3c_ac97_activate(struct snd_ac97 *ac97) -{ - u32 ac_glbctrl, stat; - - stat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT) & 0x7; - if (stat == S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE) - return; /* Return if already active */ - - reinit_completion(&s3c_ac97.done); - - ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL); - ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON; - writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL); - msleep(1); - - ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE; - writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL); - msleep(1); - - ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL); - ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE; - writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL); - - if (!wait_for_completion_timeout(&s3c_ac97.done, HZ)) - pr_err("AC97: Unable to activate!"); -} - -static unsigned short s3c_ac97_read(struct snd_ac97 *ac97, - unsigned short reg) -{ - u32 ac_glbctrl, ac_codec_cmd; - u32 stat, addr, data; - - mutex_lock(&s3c_ac97.lock); - - s3c_ac97_activate(ac97); - - reinit_completion(&s3c_ac97.done); - - ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD); - ac_codec_cmd = S3C_AC97_CODEC_CMD_READ | AC_CMD_ADDR(reg); - writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD); - - udelay(50); - - ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL); - ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE; - writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL); - - if (!wait_for_completion_timeout(&s3c_ac97.done, HZ)) - pr_err("AC97: Unable to read!"); - - stat = readl(s3c_ac97.regs + S3C_AC97_STAT); - addr = (stat >> 16) & 0x7f; - data = (stat & 0xffff); - - if (addr != reg) - pr_err("ac97: req addr = %02x, rep addr = %02x\n", - reg, addr); - - mutex_unlock(&s3c_ac97.lock); - - return (unsigned short)data; -} - -static void s3c_ac97_write(struct snd_ac97 *ac97, unsigned short reg, - unsigned short val) -{ - u32 ac_glbctrl, ac_codec_cmd; - - mutex_lock(&s3c_ac97.lock); - - s3c_ac97_activate(ac97); - - reinit_completion(&s3c_ac97.done); - - ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD); - ac_codec_cmd = AC_CMD_ADDR(reg) | AC_CMD_DATA(val); - writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD); - - udelay(50); - - ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL); - ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE; - writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL); - - if (!wait_for_completion_timeout(&s3c_ac97.done, HZ)) - pr_err("AC97: Unable to write!"); - - ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD); - ac_codec_cmd |= S3C_AC97_CODEC_CMD_READ; - writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD); - - mutex_unlock(&s3c_ac97.lock); -} - -static void s3c_ac97_cold_reset(struct snd_ac97 *ac97) -{ - pr_debug("AC97: Cold reset\n"); - writel(S3C_AC97_GLBCTRL_COLDRESET, - s3c_ac97.regs + S3C_AC97_GLBCTRL); - msleep(1); - - writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL); - msleep(1); -} - -static void s3c_ac97_warm_reset(struct snd_ac97 *ac97) -{ - u32 stat; - - stat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT) & 0x7; - if (stat == S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE) - return; /* Return if already active */ - - pr_debug("AC97: Warm reset\n"); - - writel(S3C_AC97_GLBCTRL_WARMRESET, s3c_ac97.regs + S3C_AC97_GLBCTRL); - msleep(1); - - writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL); - msleep(1); - - s3c_ac97_activate(ac97); -} - -static irqreturn_t s3c_ac97_irq(int irq, void *dev_id) -{ - u32 ac_glbctrl, ac_glbstat; - - ac_glbstat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT); - - if (ac_glbstat & S3C_AC97_GLBSTAT_CODECREADY) { - - ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL); - ac_glbctrl &= ~S3C_AC97_GLBCTRL_CODECREADYIE; - writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL); - - complete(&s3c_ac97.done); - } - - ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL); - ac_glbctrl |= (1<<30); /* Clear interrupt */ - writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL); - - return IRQ_HANDLED; -} - -static struct snd_ac97_bus_ops s3c_ac97_ops = { - .read = s3c_ac97_read, - .write = s3c_ac97_write, - .warm_reset = s3c_ac97_warm_reset, - .reset = s3c_ac97_cold_reset, -}; - -static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) -{ - u32 ac_glbctrl; - - ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL); - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) - ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK; - else - ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMOUTTM_MASK; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) - ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA; - else - ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA; - break; - - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - break; - } - - writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL); - - return 0; -} - -static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream, - int cmd, struct snd_soc_dai *dai) -{ - u32 ac_glbctrl; - - ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL); - ac_glbctrl &= ~S3C_AC97_GLBCTRL_MICINTM_MASK; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - ac_glbctrl |= S3C_AC97_GLBCTRL_MICINTM_DMA; - break; - - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - break; - } - - writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL); - - return 0; -} - -static const struct snd_soc_dai_ops s3c_ac97_dai_ops = { - .trigger = s3c_ac97_trigger, -}; - -static const struct snd_soc_dai_ops s3c_ac97_mic_dai_ops = { - .trigger = s3c_ac97_mic_trigger, -}; - -static int s3c_ac97_dai_probe(struct snd_soc_dai *dai) -{ - samsung_asoc_init_dma_data(dai, &s3c_ac97_pcm_out, &s3c_ac97_pcm_in); - - return 0; -} - -static int s3c_ac97_mic_dai_probe(struct snd_soc_dai *dai) -{ - samsung_asoc_init_dma_data(dai, NULL, &s3c_ac97_mic_in); - - return 0; -} - -static struct snd_soc_dai_driver s3c_ac97_dai[] = { - [S3C_AC97_DAI_PCM] = { - .name = "samsung-ac97", - .bus_control = true, - .playback = { - .stream_name = "AC97 Playback", - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, - .capture = { - .stream_name = "AC97 Capture", - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, - .probe = s3c_ac97_dai_probe, - .ops = &s3c_ac97_dai_ops, - }, - [S3C_AC97_DAI_MIC] = { - .name = "samsung-ac97-mic", - .bus_control = true, - .capture = { - .stream_name = "AC97 Mic Capture", - .channels_min = 1, - .channels_max = 1, - .rates = SNDRV_PCM_RATE_8000_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, - .probe = s3c_ac97_mic_dai_probe, - .ops = &s3c_ac97_mic_dai_ops, - }, -}; - -static const struct snd_soc_component_driver s3c_ac97_component = { - .name = "s3c-ac97", -}; - -static int s3c_ac97_probe(struct platform_device *pdev) -{ - struct resource *mem_res, *irq_res; - struct s3c_audio_pdata *ac97_pdata; - int ret; - - ac97_pdata = pdev->dev.platform_data; - if (!ac97_pdata || !ac97_pdata->cfg_gpio) { - dev_err(&pdev->dev, "cfg_gpio callback not provided!\n"); - return -EINVAL; - } - - /* Check for availability of necessary resource */ - irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!irq_res) { - dev_err(&pdev->dev, "AC97 IRQ not provided!\n"); - return -ENXIO; - } - - mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - s3c_ac97.regs = devm_ioremap_resource(&pdev->dev, mem_res); - if (IS_ERR(s3c_ac97.regs)) - return PTR_ERR(s3c_ac97.regs); - - s3c_ac97_pcm_out.slave = ac97_pdata->dma_playback; - s3c_ac97_pcm_out.dma_addr = mem_res->start + S3C_AC97_PCM_DATA; - s3c_ac97_pcm_in.slave = ac97_pdata->dma_capture; - s3c_ac97_pcm_in.dma_addr = mem_res->start + S3C_AC97_PCM_DATA; - s3c_ac97_mic_in.slave = ac97_pdata->dma_capture_mic; - s3c_ac97_mic_in.dma_addr = mem_res->start + S3C_AC97_MIC_DATA; - - init_completion(&s3c_ac97.done); - mutex_init(&s3c_ac97.lock); - - s3c_ac97.ac97_clk = devm_clk_get(&pdev->dev, "ac97"); - if (IS_ERR(s3c_ac97.ac97_clk)) { - dev_err(&pdev->dev, "ac97 failed to get ac97_clock\n"); - ret = -ENODEV; - goto err2; - } - clk_prepare_enable(s3c_ac97.ac97_clk); - - if (ac97_pdata->cfg_gpio(pdev)) { - dev_err(&pdev->dev, "Unable to configure gpio\n"); - ret = -EINVAL; - goto err3; - } - - ret = request_irq(irq_res->start, s3c_ac97_irq, - 0, "AC97", NULL); - if (ret < 0) { - dev_err(&pdev->dev, "ac97: interrupt request failed.\n"); - goto err4; - } - - ret = snd_soc_set_ac97_ops(&s3c_ac97_ops); - if (ret != 0) { - dev_err(&pdev->dev, "Failed to set AC'97 ops: %d\n", ret); - goto err4; - } - - ret = devm_snd_soc_register_component(&pdev->dev, &s3c_ac97_component, - s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai)); - if (ret) - goto err5; - - ret = samsung_asoc_dma_platform_register(&pdev->dev, - ac97_pdata->dma_filter, - NULL, NULL); - if (ret) { - dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret); - goto err5; - } - - return 0; -err5: - free_irq(irq_res->start, NULL); -err4: -err3: - clk_disable_unprepare(s3c_ac97.ac97_clk); -err2: - snd_soc_set_ac97_ops(NULL); - return ret; -} - -static int s3c_ac97_remove(struct platform_device *pdev) -{ - struct resource *irq_res; - - irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (irq_res) - free_irq(irq_res->start, NULL); - - clk_disable_unprepare(s3c_ac97.ac97_clk); - snd_soc_set_ac97_ops(NULL); - - return 0; -} - -static struct platform_driver s3c_ac97_driver = { - .probe = s3c_ac97_probe, - .remove = s3c_ac97_remove, - .driver = { - .name = "samsung-ac97", - }, -}; - -module_platform_driver(s3c_ac97_driver); - -MODULE_AUTHOR("Jaswinder Singh, <jassisinghbrar@gmail.com>"); -MODULE_DESCRIPTION("AC97 driver for the Samsung SoC"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:samsung-ac97"); diff --git a/sound/soc/samsung/dma.h b/sound/soc/samsung/dma.h index 3830f297e0b6..7ae580d677c8 100644 --- a/sound/soc/samsung/dma.h +++ b/sound/soc/samsung/dma.h @@ -1,6 +1,4 @@ /* - * dma.h -- - * * 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; either version 2 of the License, or (at your @@ -9,27 +7,15 @@ * ALSA PCM interface for the Samsung SoC */ -#ifndef _S3C_AUDIO_H -#define _S3C_AUDIO_H +#ifndef _SAMSUNG_DMA_H +#define _SAMSUNG_DMA_H #include <sound/dmaengine_pcm.h> -#include <linux/dmaengine.h> - -struct s3c_dma_params { - void *slave; /* Channel ID */ - dma_addr_t dma_addr; - int dma_size; /* Size of the DMA transfer */ - char *ch_name; - struct snd_dmaengine_dai_dma_data dma_data; -}; -void samsung_asoc_init_dma_data(struct snd_soc_dai *dai, - struct s3c_dma_params *playback, - struct s3c_dma_params *capture); /* * @tx, @rx arguments can be NULL if the DMA channel names are "tx", "rx", * otherwise actual DMA channel names must be passed to this function. */ int samsung_asoc_dma_platform_register(struct device *dev, dma_filter_fn filter, const char *tx, const char *rx); -#endif +#endif /* _SAMSUNG_DMA_H */ diff --git a/sound/soc/samsung/dmaengine.c b/sound/soc/samsung/dmaengine.c index 2c87f380bfc4..cda656e4afc6 100644 --- a/sound/soc/samsung/dmaengine.c +++ b/sound/soc/samsung/dmaengine.c @@ -16,49 +16,18 @@ */ #include <linux/module.h> -#include <linux/amba/pl08x.h> -#include <linux/platform_data/dma-s3c24xx.h> - #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/dmaengine_pcm.h> #include <sound/soc.h> -#include <sound/soc-dai.h> #include "dma.h" -void samsung_asoc_init_dma_data(struct snd_soc_dai *dai, - struct s3c_dma_params *playback, - struct s3c_dma_params *capture) -{ - struct snd_dmaengine_dai_dma_data *playback_data = NULL; - struct snd_dmaengine_dai_dma_data *capture_data = NULL; - - if (playback) { - playback_data = &playback->dma_data; - playback_data->filter_data = playback->slave; - playback_data->chan_name = playback->ch_name; - playback_data->addr = playback->dma_addr; - playback_data->addr_width = playback->dma_size; - } - if (capture) { - capture_data = &capture->dma_data; - capture_data->filter_data = capture->slave; - capture_data->chan_name = capture->ch_name; - capture_data->addr = capture->dma_addr; - capture_data->addr_width = capture->dma_size; - } - - snd_soc_dai_init_dma_data(dai, playback_data, capture_data); -} -EXPORT_SYMBOL_GPL(samsung_asoc_init_dma_data); - int samsung_asoc_dma_platform_register(struct device *dev, dma_filter_fn filter, const char *tx, const char *rx) { unsigned int flags = SND_DMAENGINE_PCM_FLAG_COMPAT; - struct snd_dmaengine_pcm_config *pcm_conf; pcm_conf = devm_kzalloc(dev, sizeof(*pcm_conf), GFP_KERNEL); @@ -68,12 +37,8 @@ int samsung_asoc_dma_platform_register(struct device *dev, dma_filter_fn filter, pcm_conf->prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config; pcm_conf->compat_filter_fn = filter; - if (dev->of_node) { - pcm_conf->chan_names[SNDRV_PCM_STREAM_PLAYBACK] = tx; - pcm_conf->chan_names[SNDRV_PCM_STREAM_CAPTURE] = rx; - } else { - flags |= SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME; - } + pcm_conf->chan_names[SNDRV_PCM_STREAM_PLAYBACK] = tx; + pcm_conf->chan_names[SNDRV_PCM_STREAM_CAPTURE] = rx; return devm_snd_dmaengine_pcm_register(dev, pcm_conf, flags); } diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 50635ee8ff20..e00974bc5616 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -87,9 +87,9 @@ struct i2s_dai { /* Driver for this DAI */ struct snd_soc_dai_driver i2s_dai_drv; /* DMA parameters */ - struct s3c_dma_params dma_playback; - struct s3c_dma_params dma_capture; - struct s3c_dma_params idma_playback; + struct snd_dmaengine_dai_dma_data dma_playback; + struct snd_dmaengine_dai_dma_data dma_capture; + struct snd_dmaengine_dai_dma_data idma_playback; dma_filter_fn filter; u32 quirks; u32 suspend_i2smod; @@ -692,15 +692,15 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, break; case 2: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - i2s->dma_playback.dma_size = 4; + i2s->dma_playback.addr_width = 4; else - i2s->dma_capture.dma_size = 4; + i2s->dma_capture.addr_width = 4; break; case 1: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - i2s->dma_playback.dma_size = 2; + i2s->dma_playback.addr_width = 2; else - i2s->dma_capture.dma_size = 2; + i2s->dma_capture.addr_width = 2; break; default: @@ -754,7 +754,7 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, writel(mod, i2s->addr + I2SMOD); spin_unlock_irqrestore(i2s->lock, flags); - samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); + snd_soc_dai_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); i2s->frmclk = params_rate(params); @@ -991,10 +991,10 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) unsigned long flags; if (is_secondary(i2s)) { /* If this is probe on the secondary DAI */ - samsung_asoc_init_dma_data(dai, &other->sec_dai->dma_playback, + snd_soc_dai_init_dma_data(dai, &other->sec_dai->dma_playback, NULL); } else { - samsung_asoc_init_dma_data(dai, &i2s->dma_playback, + snd_soc_dai_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); if (i2s->quirks & QUIRK_NEED_RSTCLR) @@ -1002,7 +1002,7 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) if (i2s->quirks & QUIRK_SUPPORTS_IDMA) idma_reg_addr_init(i2s->addr, - i2s->sec_dai->idma_playback.dma_addr); + i2s->sec_dai->idma_playback.addr); } /* Reset any constraint on RFS and BFS */ @@ -1029,12 +1029,13 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) static int samsung_i2s_dai_remove(struct snd_soc_dai *dai) { struct i2s_dai *i2s = snd_soc_dai_get_drvdata(dai); + unsigned long flags; if (!is_secondary(i2s)) { if (i2s->quirks & QUIRK_NEED_RSTCLR) { - spin_lock(i2s->lock); + spin_lock_irqsave(i2s->lock, flags); writel(0, i2s->addr + I2SCON); - spin_unlock(i2s->lock); + spin_unlock_irqrestore(i2s->lock, flags); } } @@ -1237,14 +1238,14 @@ static int samsung_i2s_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Unable to get drvdata\n"); return -EFAULT; } - ret = devm_snd_soc_register_component(&sec_dai->pdev->dev, - &samsung_i2s_component, - &sec_dai->i2s_dai_drv, 1); + ret = samsung_asoc_dma_platform_register(&pdev->dev, + sec_dai->filter, "tx-sec", NULL); if (ret != 0) return ret; - return samsung_asoc_dma_platform_register(&pdev->dev, - sec_dai->filter, "tx-sec", NULL); + return devm_snd_soc_register_component(&sec_dai->pdev->dev, + &samsung_i2s_component, + &sec_dai->i2s_dai_drv, 1); } pri_dai = i2s_alloc_dai(pdev, false); @@ -1262,8 +1263,8 @@ static int samsung_i2s_probe(struct platform_device *pdev) return -EINVAL; } - pri_dai->dma_playback.slave = i2s_pdata->dma_playback; - pri_dai->dma_capture.slave = i2s_pdata->dma_capture; + pri_dai->dma_playback.filter_data = i2s_pdata->dma_playback; + pri_dai->dma_capture.filter_data = i2s_pdata->dma_capture; pri_dai->filter = i2s_pdata->dma_filter; if (&i2s_pdata->type) @@ -1302,47 +1303,51 @@ static int samsung_i2s_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to enable clock: %d\n", ret); return ret; } - pri_dai->dma_playback.dma_addr = regs_base + I2STXD; - pri_dai->dma_capture.dma_addr = regs_base + I2SRXD; - pri_dai->dma_playback.ch_name = "tx"; - pri_dai->dma_capture.ch_name = "rx"; - pri_dai->dma_playback.dma_size = 4; - pri_dai->dma_capture.dma_size = 4; + pri_dai->dma_playback.addr = regs_base + I2STXD; + pri_dai->dma_capture.addr = regs_base + I2SRXD; + pri_dai->dma_playback.addr_width = 4; + pri_dai->dma_capture.addr_width = 4; pri_dai->quirks = quirks; pri_dai->variant_regs = i2s_dai_data->i2s_variant_regs; if (quirks & QUIRK_PRI_6CHAN) pri_dai->i2s_dai_drv.playback.channels_max = 6; + ret = samsung_asoc_dma_platform_register(&pdev->dev, pri_dai->filter, + NULL, NULL); + if (ret < 0) + goto err_disable_clk; + if (quirks & QUIRK_SEC_DAI) { sec_dai = i2s_alloc_dai(pdev, true); if (!sec_dai) { dev_err(&pdev->dev, "Unable to alloc I2S_sec\n"); - return -ENOMEM; + ret = -ENOMEM; + goto err_disable_clk; } sec_dai->lock = &pri_dai->spinlock; sec_dai->variant_regs = pri_dai->variant_regs; - sec_dai->dma_playback.dma_addr = regs_base + I2STXDS; - sec_dai->dma_playback.ch_name = "tx-sec"; + sec_dai->dma_playback.addr = regs_base + I2STXDS; if (!np) { - sec_dai->dma_playback.slave = i2s_pdata->dma_play_sec; + sec_dai->dma_playback.filter_data = i2s_pdata->dma_play_sec; sec_dai->filter = i2s_pdata->dma_filter; } - sec_dai->dma_playback.dma_size = 4; + sec_dai->dma_playback.addr_width = 4; sec_dai->addr = pri_dai->addr; sec_dai->clk = pri_dai->clk; sec_dai->quirks = quirks; - sec_dai->idma_playback.dma_addr = idma_addr; + sec_dai->idma_playback.addr = idma_addr; sec_dai->pri_dai = pri_dai; pri_dai->sec_dai = sec_dai; } if (i2s_pdata && i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) { dev_err(&pdev->dev, "Unable to configure gpio\n"); - return -EINVAL; + ret = -EINVAL; + goto err_disable_clk; } ret = devm_snd_soc_register_component(&pri_dai->pdev->dev, @@ -1351,10 +1356,6 @@ static int samsung_i2s_probe(struct platform_device *pdev) if (ret < 0) goto err_free_dai; - ret = samsung_asoc_dma_platform_register(&pdev->dev, pri_dai->filter, - NULL, NULL); - if (ret < 0) - goto err_free_dai; pm_runtime_enable(&pdev->dev); @@ -1366,6 +1367,8 @@ static int samsung_i2s_probe(struct platform_device *pdev) err_free_dai: if (sec_dai) i2s_free_sec_dai(sec_dai); +err_disable_clk: + clk_disable_unprepare(pri_dai->clk); return ret; } diff --git a/sound/soc/samsung/idma.c b/sound/soc/samsung/idma.c index 4ed29ffc1c54..3e408158625d 100644 --- a/sound/soc/samsung/idma.c +++ b/sound/soc/samsung/idma.c @@ -22,7 +22,6 @@ #include "i2s.h" #include "idma.h" -#include "dma.h" #include "i2s-regs.h" #define ST_RUNNING (1<<0) diff --git a/sound/soc/samsung/ln2440sbc_alc650.c b/sound/soc/samsung/ln2440sbc_alc650.c deleted file mode 100644 index 9342fc270c2b..000000000000 --- a/sound/soc/samsung/ln2440sbc_alc650.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * SoC audio for ln2440sbc - * - * Copyright 2007 KonekTel, a.s. - * Author: Ivan Kuten - * ivan.kuten@promwad.com - * - * Heavily based on smdk2443_wm9710.c - * Copyright 2007 Wolfson Microelectronics PLC. - * Author: Graeme Gregory - * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.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 <sound/soc.h> - -static struct snd_soc_card ln2440sbc; - -static struct snd_soc_dai_link ln2440sbc_dai[] = { -{ - .name = "AC97", - .stream_name = "AC97 HiFi", - .cpu_dai_name = "samsung-ac97", - .codec_dai_name = "ac97-hifi", - .codec_name = "ac97-codec", - .platform_name = "samsung-ac97", -}, -}; - -static struct snd_soc_card ln2440sbc = { - .name = "LN2440SBC", - .owner = THIS_MODULE, - .dai_link = ln2440sbc_dai, - .num_links = ARRAY_SIZE(ln2440sbc_dai), -}; - -static struct platform_device *ln2440sbc_snd_ac97_device; - -static int __init ln2440sbc_init(void) -{ - int ret; - - ln2440sbc_snd_ac97_device = platform_device_alloc("soc-audio", -1); - if (!ln2440sbc_snd_ac97_device) - return -ENOMEM; - - platform_set_drvdata(ln2440sbc_snd_ac97_device, &ln2440sbc); - ret = platform_device_add(ln2440sbc_snd_ac97_device); - - if (ret) - platform_device_put(ln2440sbc_snd_ac97_device); - - return ret; -} - -static void __exit ln2440sbc_exit(void) -{ - platform_device_unregister(ln2440sbc_snd_ac97_device); -} - -module_init(ln2440sbc_init); -module_exit(ln2440sbc_exit); - -/* Module information */ -MODULE_AUTHOR("Ivan Kuten"); -MODULE_DESCRIPTION("ALSA SoC ALC650 LN2440SBC"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/samsung/pcm.c b/sound/soc/samsung/pcm.c index 490c1a87fd66..d50a6377c23d 100644 --- a/sound/soc/samsung/pcm.c +++ b/sound/soc/samsung/pcm.c @@ -127,25 +127,25 @@ struct s3c_pcm_info { struct clk *pclk; struct clk *cclk; - struct s3c_dma_params *dma_playback; - struct s3c_dma_params *dma_capture; + struct snd_dmaengine_dai_dma_data *dma_playback; + struct snd_dmaengine_dai_dma_data *dma_capture; }; -static struct s3c_dma_params s3c_pcm_stereo_out[] = { +static struct snd_dmaengine_dai_dma_data s3c_pcm_stereo_out[] = { [0] = { - .dma_size = 4, + .addr_width = 4, }, [1] = { - .dma_size = 4, + .addr_width = 4, }, }; -static struct s3c_dma_params s3c_pcm_stereo_in[] = { +static struct snd_dmaengine_dai_dma_data s3c_pcm_stereo_in[] = { [0] = { - .dma_size = 4, + .addr_width = 4, }, [1] = { - .dma_size = 4, + .addr_width = 4, }, }; @@ -499,13 +499,6 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev) pcm_pdata = pdev->dev.platform_data; - /* Check for availability of necessary resource */ - mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem_res) { - dev_err(&pdev->dev, "Unable to get register resource\n"); - return -ENXIO; - } - if (pcm_pdata && pcm_pdata->cfg_gpio && pcm_pdata->cfg_gpio(pdev)) { dev_err(&pdev->dev, "Unable to configure gpio\n"); return -EINVAL; @@ -519,96 +512,74 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev) /* Default is 128fs */ pcm->sclk_per_fs = 128; + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pcm->regs = devm_ioremap_resource(&pdev->dev, mem_res); + if (IS_ERR(pcm->regs)) + return PTR_ERR(pcm->regs); + pcm->cclk = devm_clk_get(&pdev->dev, "audio-bus"); if (IS_ERR(pcm->cclk)) { - dev_err(&pdev->dev, "failed to get audio-bus\n"); - ret = PTR_ERR(pcm->cclk); - goto err1; + dev_err(&pdev->dev, "failed to get audio-bus clock\n"); + return PTR_ERR(pcm->cclk); } clk_prepare_enable(pcm->cclk); /* record our pcm structure for later use in the callbacks */ dev_set_drvdata(&pdev->dev, pcm); - if (!request_mem_region(mem_res->start, - resource_size(mem_res), "samsung-pcm")) { - dev_err(&pdev->dev, "Unable to request register region\n"); - ret = -EBUSY; - goto err2; - } - - pcm->regs = ioremap(mem_res->start, 0x100); - if (pcm->regs == NULL) { - dev_err(&pdev->dev, "cannot ioremap registers\n"); - ret = -ENXIO; - goto err3; - } - pcm->pclk = devm_clk_get(&pdev->dev, "pcm"); if (IS_ERR(pcm->pclk)) { - dev_err(&pdev->dev, "failed to get pcm_clock\n"); - ret = -ENOENT; - goto err4; + dev_err(&pdev->dev, "failed to get pcm clock\n"); + ret = PTR_ERR(pcm->pclk); + goto err_dis_cclk; } clk_prepare_enable(pcm->pclk); - s3c_pcm_stereo_in[pdev->id].dma_addr = mem_res->start - + S3C_PCM_RXFIFO; - s3c_pcm_stereo_out[pdev->id].dma_addr = mem_res->start - + S3C_PCM_TXFIFO; + s3c_pcm_stereo_in[pdev->id].addr = mem_res->start + S3C_PCM_RXFIFO; + s3c_pcm_stereo_out[pdev->id].addr = mem_res->start + S3C_PCM_TXFIFO; filter = NULL; if (pcm_pdata) { - s3c_pcm_stereo_in[pdev->id].slave = pcm_pdata->dma_capture; - s3c_pcm_stereo_out[pdev->id].slave = pcm_pdata->dma_playback; + s3c_pcm_stereo_in[pdev->id].filter_data = pcm_pdata->dma_capture; + s3c_pcm_stereo_out[pdev->id].filter_data = pcm_pdata->dma_playback; filter = pcm_pdata->dma_filter; } pcm->dma_capture = &s3c_pcm_stereo_in[pdev->id]; pcm->dma_playback = &s3c_pcm_stereo_out[pdev->id]; + ret = samsung_asoc_dma_platform_register(&pdev->dev, filter, + NULL, NULL); + if (ret) { + dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret); + goto err_dis_pclk; + } + pm_runtime_enable(&pdev->dev); ret = devm_snd_soc_register_component(&pdev->dev, &s3c_pcm_component, &s3c_pcm_dai[pdev->id], 1); if (ret != 0) { dev_err(&pdev->dev, "failed to get register DAI: %d\n", ret); - goto err5; - } - - ret = samsung_asoc_dma_platform_register(&pdev->dev, filter, - NULL, NULL); - if (ret) { - dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret); - goto err5; + goto err_dis_pm; } return 0; -err5: +err_dis_pm: + pm_runtime_disable(&pdev->dev); +err_dis_pclk: clk_disable_unprepare(pcm->pclk); -err4: - iounmap(pcm->regs); -err3: - release_mem_region(mem_res->start, resource_size(mem_res)); -err2: +err_dis_cclk: clk_disable_unprepare(pcm->cclk); -err1: return ret; } static int s3c_pcm_dev_remove(struct platform_device *pdev) { struct s3c_pcm_info *pcm = &s3c_pcm[pdev->id]; - struct resource *mem_res; pm_runtime_disable(&pdev->dev); - - iounmap(pcm->regs); - - mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(mem_res->start, resource_size(mem_res)); - clk_disable_unprepare(pcm->cclk); clk_disable_unprepare(pcm->pclk); diff --git a/sound/soc/samsung/regs-ac97.h b/sound/soc/samsung/regs-ac97.h deleted file mode 100644 index a71be45bbffc..000000000000 --- a/sound/soc/samsung/regs-ac97.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2006 Simtec Electronics <linux@simtec.co.uk> - * http://www.simtec.co.uk/products/SWLINUX/ - * - * 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. - * - * S3C2440 AC97 Controller -*/ - -#ifndef __SAMSUNG_REGS_AC97_H__ -#define __SAMSUNG_REGS_AC97_H__ - -#define S3C_AC97_GLBCTRL (0x00) - -#define S3C_AC97_GLBCTRL_CODECREADYIE (1<<22) -#define S3C_AC97_GLBCTRL_PCMOUTURIE (1<<21) -#define S3C_AC97_GLBCTRL_PCMINORIE (1<<20) -#define S3C_AC97_GLBCTRL_MICINORIE (1<<19) -#define S3C_AC97_GLBCTRL_PCMOUTTIE (1<<18) -#define S3C_AC97_GLBCTRL_PCMINTIE (1<<17) -#define S3C_AC97_GLBCTRL_MICINTIE (1<<16) -#define S3C_AC97_GLBCTRL_PCMOUTTM_OFF (0<<12) -#define S3C_AC97_GLBCTRL_PCMOUTTM_PIO (1<<12) -#define S3C_AC97_GLBCTRL_PCMOUTTM_DMA (2<<12) -#define S3C_AC97_GLBCTRL_PCMOUTTM_MASK (3<<12) -#define S3C_AC97_GLBCTRL_PCMINTM_OFF (0<<10) -#define S3C_AC97_GLBCTRL_PCMINTM_PIO (1<<10) -#define S3C_AC97_GLBCTRL_PCMINTM_DMA (2<<10) -#define S3C_AC97_GLBCTRL_PCMINTM_MASK (3<<10) -#define S3C_AC97_GLBCTRL_MICINTM_OFF (0<<8) -#define S3C_AC97_GLBCTRL_MICINTM_PIO (1<<8) -#define S3C_AC97_GLBCTRL_MICINTM_DMA (2<<8) -#define S3C_AC97_GLBCTRL_MICINTM_MASK (3<<8) -#define S3C_AC97_GLBCTRL_TRANSFERDATAENABLE (1<<3) -#define S3C_AC97_GLBCTRL_ACLINKON (1<<2) -#define S3C_AC97_GLBCTRL_WARMRESET (1<<1) -#define S3C_AC97_GLBCTRL_COLDRESET (1<<0) - -#define S3C_AC97_GLBSTAT (0x04) - -#define S3C_AC97_GLBSTAT_CODECREADY (1<<22) -#define S3C_AC97_GLBSTAT_PCMOUTUR (1<<21) -#define S3C_AC97_GLBSTAT_PCMINORI (1<<20) -#define S3C_AC97_GLBSTAT_MICINORI (1<<19) -#define S3C_AC97_GLBSTAT_PCMOUTTI (1<<18) -#define S3C_AC97_GLBSTAT_PCMINTI (1<<17) -#define S3C_AC97_GLBSTAT_MICINTI (1<<16) -#define S3C_AC97_GLBSTAT_MAINSTATE_IDLE (0<<0) -#define S3C_AC97_GLBSTAT_MAINSTATE_INIT (1<<0) -#define S3C_AC97_GLBSTAT_MAINSTATE_READY (2<<0) -#define S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE (3<<0) -#define S3C_AC97_GLBSTAT_MAINSTATE_LP (4<<0) -#define S3C_AC97_GLBSTAT_MAINSTATE_WARM (5<<0) - -#define S3C_AC97_CODEC_CMD (0x08) - -#define S3C_AC97_CODEC_CMD_READ (1<<23) - -#define S3C_AC97_STAT (0x0c) -#define S3C_AC97_PCM_ADDR (0x10) -#define S3C_AC97_PCM_DATA (0x18) -#define S3C_AC97_MIC_DATA (0x1C) - -#endif /* __SAMSUNG_REGS_AC97_H__ */ diff --git a/sound/soc/samsung/s3c-i2s-v2.c b/sound/soc/samsung/s3c-i2s-v2.c index bf8ae79b0fd2..644f186fd35c 100644 --- a/sound/soc/samsung/s3c-i2s-v2.c +++ b/sound/soc/samsung/s3c-i2s-v2.c @@ -24,7 +24,6 @@ #include "regs-i2s-v2.h" #include "s3c-i2s-v2.h" -#include "dma.h" #undef S3C_IIS_V2_SUPPORTED @@ -302,7 +301,7 @@ static int s3c_i2sv2_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct s3c_i2sv2_info *i2s = to_info(dai); - struct s3c_dma_params *dma_data; + struct snd_dmaengine_dai_dma_data *dma_data; u32 iismod; pr_debug("Entered %s\n", __func__); diff --git a/sound/soc/samsung/s3c-i2s-v2.h b/sound/soc/samsung/s3c-i2s-v2.h index d0684145ed1f..182d80564e37 100644 --- a/sound/soc/samsung/s3c-i2s-v2.h +++ b/sound/soc/samsung/s3c-i2s-v2.h @@ -60,8 +60,8 @@ struct s3c_i2sv2_info { unsigned char master; - struct s3c_dma_params *dma_playback; - struct s3c_dma_params *dma_capture; + struct snd_dmaengine_dai_dma_data *dma_playback; + struct snd_dmaengine_dai_dma_data *dma_capture; u32 suspend_iismod; u32 suspend_iiscon; diff --git a/sound/soc/samsung/s3c2412-i2s.c b/sound/soc/samsung/s3c2412-i2s.c index d45dffb297d8..6d0b8897fa6c 100644 --- a/sound/soc/samsung/s3c2412-i2s.c +++ b/sound/soc/samsung/s3c2412-i2s.c @@ -34,14 +34,12 @@ #include <linux/platform_data/asoc-s3c.h> -static struct s3c_dma_params s3c2412_i2s_pcm_stereo_out = { - .ch_name = "tx", - .dma_size = 4, +static struct snd_dmaengine_dai_dma_data s3c2412_i2s_pcm_stereo_out = { + .addr_width = 4, }; -static struct s3c_dma_params s3c2412_i2s_pcm_stereo_in = { - .ch_name = "rx", - .dma_size = 4, +static struct snd_dmaengine_dai_dma_data s3c2412_i2s_pcm_stereo_in = { + .addr_width = 4, }; static struct s3c_i2sv2_info s3c2412_i2s; @@ -52,8 +50,8 @@ static int s3c2412_i2s_probe(struct snd_soc_dai *dai) pr_debug("Entered %s\n", __func__); - samsung_asoc_init_dma_data(dai, &s3c2412_i2s_pcm_stereo_out, - &s3c2412_i2s_pcm_stereo_in); + snd_soc_dai_init_dma_data(dai, &s3c2412_i2s_pcm_stereo_out, + &s3c2412_i2s_pcm_stereo_in); ret = s3c_i2sv2_probe(dai, &s3c2412_i2s, S3C2410_PA_IIS); if (ret) @@ -163,24 +161,24 @@ static int s3c2412_iis_dev_probe(struct platform_device *pdev) if (IS_ERR(s3c2412_i2s.regs)) return PTR_ERR(s3c2412_i2s.regs); - s3c2412_i2s_pcm_stereo_out.dma_addr = res->start + S3C2412_IISTXD; - s3c2412_i2s_pcm_stereo_out.slave = pdata->dma_playback; - s3c2412_i2s_pcm_stereo_in.dma_addr = res->start + S3C2412_IISRXD; - s3c2412_i2s_pcm_stereo_in.slave = pdata->dma_capture; + s3c2412_i2s_pcm_stereo_out.addr = res->start + S3C2412_IISTXD; + s3c2412_i2s_pcm_stereo_out.filter_data = pdata->dma_playback; + s3c2412_i2s_pcm_stereo_in.addr = res->start + S3C2412_IISRXD; + s3c2412_i2s_pcm_stereo_in.filter_data = pdata->dma_capture; - ret = s3c_i2sv2_register_component(&pdev->dev, -1, - &s3c2412_i2s_component, - &s3c2412_i2s_dai); + ret = samsung_asoc_dma_platform_register(&pdev->dev, + pdata->dma_filter, + NULL, NULL); if (ret) { - pr_err("failed to register the dai\n"); + pr_err("failed to register the DMA: %d\n", ret); return ret; } - ret = samsung_asoc_dma_platform_register(&pdev->dev, - pdata->dma_filter, - NULL, NULL); + ret = s3c_i2sv2_register_component(&pdev->dev, -1, + &s3c2412_i2s_component, + &s3c2412_i2s_dai); if (ret) - pr_err("failed to register the DMA: %d\n", ret); + pr_err("failed to register the dai\n"); return ret; } diff --git a/sound/soc/samsung/s3c24xx-i2s.c b/sound/soc/samsung/s3c24xx-i2s.c index 3e76f2a75a24..07f5091b33e8 100644 --- a/sound/soc/samsung/s3c24xx-i2s.c +++ b/sound/soc/samsung/s3c24xx-i2s.c @@ -30,16 +30,12 @@ #include "dma.h" #include "s3c24xx-i2s.h" -#include <linux/platform_data/asoc-s3c.h> - -static struct s3c_dma_params s3c24xx_i2s_pcm_stereo_out = { - .ch_name = "tx", - .dma_size = 2, +static struct snd_dmaengine_dai_dma_data s3c24xx_i2s_pcm_stereo_out = { + .addr_width = 2, }; -static struct s3c_dma_params s3c24xx_i2s_pcm_stereo_in = { - .ch_name = "rx", - .dma_size = 2, +static struct snd_dmaengine_dai_dma_data s3c24xx_i2s_pcm_stereo_in = { + .addr_width = 2, }; struct s3c24xx_i2s_info { @@ -58,8 +54,6 @@ static void s3c24xx_snd_txctrl(int on) u32 iiscon; u32 iismod; - pr_debug("Entered %s\n", __func__); - iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON); iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON); iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); @@ -103,8 +97,6 @@ static void s3c24xx_snd_rxctrl(int on) u32 iiscon; u32 iismod; - pr_debug("Entered %s\n", __func__); - iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON); iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON); iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); @@ -151,8 +143,6 @@ static int s3c24xx_snd_lrsync(void) u32 iiscon; int timeout = 50; /* 5ms */ - pr_debug("Entered %s\n", __func__); - while (1) { iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON); if (iiscon & S3C2410_IISCON_LRINDEX) @@ -171,8 +161,6 @@ static int s3c24xx_snd_lrsync(void) */ static inline int s3c24xx_snd_is_clkmaster(void) { - pr_debug("Entered %s\n", __func__); - return (readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & S3C2410_IISMOD_SLAVE) ? 0:1; } @@ -184,8 +172,6 @@ static int s3c24xx_i2s_set_fmt(struct snd_soc_dai *cpu_dai, { u32 iismod; - pr_debug("Entered %s\n", __func__); - iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); pr_debug("hw_params r: IISMOD: %x \n", iismod); @@ -213,6 +199,7 @@ static int s3c24xx_i2s_set_fmt(struct snd_soc_dai *cpu_dai, writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD); pr_debug("hw_params w: IISMOD: %x \n", iismod); + return 0; } @@ -223,8 +210,6 @@ static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_dmaengine_dai_dma_data *dma_data; u32 iismod; - pr_debug("Entered %s\n", __func__); - dma_data = snd_soc_dai_get_dma_data(dai, substream); /* Working copies of register */ @@ -246,6 +231,7 @@ static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream, writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD); pr_debug("hw_params w: IISMOD: %x\n", iismod); + return 0; } @@ -254,8 +240,6 @@ static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd, { int ret = 0; - pr_debug("Entered %s\n", __func__); - switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: @@ -297,8 +281,6 @@ static int s3c24xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, { u32 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); - pr_debug("Entered %s\n", __func__); - iismod &= ~S3C2440_IISMOD_MPLL; switch (clk_id) { @@ -323,8 +305,6 @@ static int s3c24xx_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai, { u32 reg; - pr_debug("Entered %s\n", __func__); - switch (div_id) { case S3C24XX_DIV_BCLK: reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~S3C2410_IISMOD_FS_MASK; @@ -358,10 +338,8 @@ EXPORT_SYMBOL_GPL(s3c24xx_i2s_get_clockrate); static int s3c24xx_i2s_probe(struct snd_soc_dai *dai) { - pr_debug("Entered %s\n", __func__); - - samsung_asoc_init_dma_data(dai, &s3c24xx_i2s_pcm_stereo_out, - &s3c24xx_i2s_pcm_stereo_in); + snd_soc_dai_init_dma_data(dai, &s3c24xx_i2s_pcm_stereo_out, + &s3c24xx_i2s_pcm_stereo_in); s3c24xx_i2s.iis_clk = devm_clk_get(dai->dev, "iis"); if (IS_ERR(s3c24xx_i2s.iis_clk)) { @@ -385,8 +363,6 @@ static int s3c24xx_i2s_probe(struct snd_soc_dai *dai) #ifdef CONFIG_PM static int s3c24xx_i2s_suspend(struct snd_soc_dai *cpu_dai) { - pr_debug("Entered %s\n", __func__); - s3c24xx_i2s.iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON); s3c24xx_i2s.iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); s3c24xx_i2s.iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON); @@ -399,7 +375,6 @@ static int s3c24xx_i2s_suspend(struct snd_soc_dai *cpu_dai) static int s3c24xx_i2s_resume(struct snd_soc_dai *cpu_dai) { - pr_debug("Entered %s\n", __func__); clk_prepare_enable(s3c24xx_i2s.iis_clk); writel(s3c24xx_i2s.iiscon, s3c24xx_i2s.regs + S3C2410_IISCON); @@ -414,7 +389,6 @@ static int s3c24xx_i2s_resume(struct snd_soc_dai *cpu_dai) #define s3c24xx_i2s_resume NULL #endif - #define S3C24XX_I2S_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 | \ @@ -451,41 +425,28 @@ static const struct snd_soc_component_driver s3c24xx_i2s_component = { static int s3c24xx_iis_dev_probe(struct platform_device *pdev) { - int ret = 0; struct resource *res; - struct s3c_audio_pdata *pdata = dev_get_platdata(&pdev->dev); - - if (!pdata) { - dev_err(&pdev->dev, "missing platform data"); - return -ENXIO; - } + int ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "Can't get IO resource.\n"); - return -ENOENT; - } s3c24xx_i2s.regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(s3c24xx_i2s.regs)) return PTR_ERR(s3c24xx_i2s.regs); - s3c24xx_i2s_pcm_stereo_out.dma_addr = res->start + S3C2410_IISFIFO; - s3c24xx_i2s_pcm_stereo_out.slave = pdata->dma_playback; - s3c24xx_i2s_pcm_stereo_in.dma_addr = res->start + S3C2410_IISFIFO; - s3c24xx_i2s_pcm_stereo_in.slave = pdata->dma_capture; + s3c24xx_i2s_pcm_stereo_out.addr = res->start + S3C2410_IISFIFO; + s3c24xx_i2s_pcm_stereo_in.addr = res->start + S3C2410_IISFIFO; - ret = devm_snd_soc_register_component(&pdev->dev, - &s3c24xx_i2s_component, &s3c24xx_i2s_dai, 1); + ret = samsung_asoc_dma_platform_register(&pdev->dev, NULL, + NULL, NULL); if (ret) { - pr_err("failed to register the dai\n"); + dev_err(&pdev->dev, "Failed to register the DMA: %d\n", ret); return ret; } - ret = samsung_asoc_dma_platform_register(&pdev->dev, - pdata->dma_filter, - NULL, NULL); + ret = devm_snd_soc_register_component(&pdev->dev, + &s3c24xx_i2s_component, &s3c24xx_i2s_dai, 1); if (ret) - pr_err("failed to register the dma: %d\n", ret); + dev_err(&pdev->dev, "Failed to register the DAI\n"); return ret; } diff --git a/sound/soc/samsung/s3c24xx_uda134x.c b/sound/soc/samsung/s3c24xx_uda134x.c index 92e88bca386e..81a78940967c 100644 --- a/sound/soc/samsung/s3c24xx_uda134x.c +++ b/sound/soc/samsung/s3c24xx_uda134x.c @@ -19,9 +19,15 @@ #include <sound/s3c24xx_uda134x.h> #include "regs-iis.h" - #include "s3c24xx-i2s.h" +struct s3c24xx_uda134x { + struct clk *xtal; + struct clk *pclk; + struct mutex clk_lock; + int clk_users; +}; + /* #define ENFORCE_RATES 1 */ /* Unfortunately the S3C24XX in master mode has a limited capacity of @@ -36,15 +42,6 @@ possible an error will be returned. */ -static struct clk *xtal; -static struct clk *pclk; -/* this is need because we don't have a place where to keep the - * pointers to the clocks in each substream. We get the clocks only - * when we are actually using them so we don't block stuff like - * frequency change or oscillator power-off */ -static int clk_users; -static DEFINE_MUTEX(clk_lock); - static unsigned int rates[33 * 2]; #ifdef ENFORCE_RATES static struct snd_pcm_hw_constraint_list hw_constraints_rates = { @@ -54,31 +51,27 @@ static struct snd_pcm_hw_constraint_list hw_constraints_rates = { }; #endif -static struct platform_device *s3c24xx_uda134x_snd_device; - static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct s3c24xx_uda134x *priv = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_dai *cpu_dai = rtd->cpu_dai; -#ifdef ENFORCE_RATES - struct snd_pcm_runtime *runtime = substream->runtime; -#endif int ret = 0; - mutex_lock(&clk_lock); - pr_debug("%s %d\n", __func__, clk_users); - if (clk_users == 0) { - xtal = clk_get(&s3c24xx_uda134x_snd_device->dev, "xtal"); - if (IS_ERR(xtal)) { - printk(KERN_ERR "%s cannot get xtal\n", __func__); - ret = PTR_ERR(xtal); + mutex_lock(&priv->clk_lock); + + if (priv->clk_users == 0) { + priv->xtal = clk_get(rtd->dev, "xtal"); + if (IS_ERR(priv->xtal)) { + dev_err(rtd->dev, "%s cannot get xtal\n", __func__); + ret = PTR_ERR(priv->xtal); } else { - pclk = clk_get(cpu_dai->dev, "iis"); - if (IS_ERR(pclk)) { - printk(KERN_ERR "%s cannot get pclk\n", - __func__); - clk_put(xtal); - ret = PTR_ERR(pclk); + priv->pclk = clk_get(cpu_dai->dev, "iis"); + if (IS_ERR(priv->pclk)) { + dev_err(rtd->dev, "%s cannot get pclk\n", + __func__); + clk_put(priv->xtal); + ret = PTR_ERR(priv->pclk); } } if (!ret) { @@ -87,23 +80,24 @@ static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream) for (i = 0; i < 2; i++) { int fs = i ? 256 : 384; - rates[i*33] = clk_get_rate(xtal) / fs; + rates[i*33] = clk_get_rate(priv->xtal) / fs; for (j = 1; j < 33; j++) - rates[i*33 + j] = clk_get_rate(pclk) / + rates[i*33 + j] = clk_get_rate(priv->pclk) / (j * fs); } } } - clk_users += 1; - mutex_unlock(&clk_lock); + priv->clk_users += 1; + mutex_unlock(&priv->clk_lock); + if (!ret) { #ifdef ENFORCE_RATES - ret = snd_pcm_hw_constraint_list(runtime, 0, + ret = snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); if (ret < 0) - printk(KERN_ERR "%s cannot set constraints\n", - __func__); + dev_err(rtd->dev, "%s cannot set constraints\n", + __func__); #endif } return ret; @@ -111,16 +105,18 @@ static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream) static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream) { - mutex_lock(&clk_lock); - pr_debug("%s %d\n", __func__, clk_users); - clk_users -= 1; - if (clk_users == 0) { - clk_put(xtal); - xtal = NULL; - clk_put(pclk); - pclk = NULL; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct s3c24xx_uda134x *priv = snd_soc_card_get_drvdata(rtd->card); + + mutex_lock(&priv->clk_lock); + priv->clk_users -= 1; + if (priv->clk_users == 0) { + clk_put(priv->xtal); + priv->xtal = NULL; + clk_put(priv->pclk); + priv->pclk = NULL; } - mutex_unlock(&clk_lock); + mutex_unlock(&priv->clk_lock); } static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream *substream, @@ -159,18 +155,19 @@ static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream *substream, clk_source = S3C24XX_CLKSRC_PCLK; div = bi % 33; } - pr_debug("%s desired rate %lu, %d\n", __func__, rate, bi); + + dev_dbg(rtd->dev, "%s desired rate %lu, %d\n", __func__, rate, bi); clk = (fs_mode == S3C2410_IISMOD_384FS ? 384 : 256) * rate; - pr_debug("%s will use: %s %s %d sysclk %d err %ld\n", __func__, - fs_mode == S3C2410_IISMOD_384FS ? "384FS" : "256FS", - clk_source == S3C24XX_CLKSRC_MPLL ? "MPLLin" : "PCLK", - div, clk, err); + + dev_dbg(rtd->dev, "%s will use: %s %s %d sysclk %d err %ld\n", __func__, + fs_mode == S3C2410_IISMOD_384FS ? "384FS" : "256FS", + clk_source == S3C24XX_CLKSRC_MPLL ? "MPLLin" : "PCLK", + div, clk, err); if ((err * 100 / rate) > 5) { - printk(KERN_ERR "S3C24XX_UDA134X: effective frequency " - "too different from desired (%ld%%)\n", - err * 100 / rate); + dev_err(rtd->dev, "effective frequency too different " + "from desired (%ld%%)\n", err * 100 / rate); return -EINVAL; } @@ -227,115 +224,35 @@ static struct snd_soc_card snd_soc_s3c24xx_uda134x = { .num_links = 1, }; -static struct s3c24xx_uda134x_platform_data *s3c24xx_uda134x_l3_pins; - -static void setdat(int v) -{ - gpio_set_value(s3c24xx_uda134x_l3_pins->l3_data, v > 0); -} - -static void setclk(int v) -{ - gpio_set_value(s3c24xx_uda134x_l3_pins->l3_clk, v > 0); -} - -static void setmode(int v) -{ - gpio_set_value(s3c24xx_uda134x_l3_pins->l3_mode, v > 0); -} - -/* FIXME - This must be codec platform data but in which board file ?? */ -static struct uda134x_platform_data s3c24xx_uda134x = { - .l3 = { - .setdat = setdat, - .setclk = setclk, - .setmode = setmode, - .data_hold = 1, - .data_setup = 1, - .clock_high = 1, - .mode_hold = 1, - .mode = 1, - .mode_setup = 1, - }, -}; - -static int s3c24xx_uda134x_setup_pin(int pin, char *fun) -{ - if (gpio_request(pin, "s3c24xx_uda134x") < 0) { - printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: " - "l3 %s pin already in use", fun); - return -EBUSY; - } - gpio_direction_output(pin, 0); - return 0; -} - static int s3c24xx_uda134x_probe(struct platform_device *pdev) { + struct snd_soc_card *card = &snd_soc_s3c24xx_uda134x; + struct s3c24xx_uda134x *priv; int ret; - printk(KERN_INFO "S3C24XX_UDA134X SoC Audio driver\n"); + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; - s3c24xx_uda134x_l3_pins = pdev->dev.platform_data; - if (s3c24xx_uda134x_l3_pins == NULL) { - printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: " - "unable to find platform data\n"); - return -ENODEV; - } - s3c24xx_uda134x.power = s3c24xx_uda134x_l3_pins->power; - s3c24xx_uda134x.model = s3c24xx_uda134x_l3_pins->model; - - if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data, - "data") < 0) - return -EBUSY; - if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk, - "clk") < 0) { - gpio_free(s3c24xx_uda134x_l3_pins->l3_data); - return -EBUSY; - } - if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode, - "mode") < 0) { - gpio_free(s3c24xx_uda134x_l3_pins->l3_data); - gpio_free(s3c24xx_uda134x_l3_pins->l3_clk); - return -EBUSY; - } + mutex_init(&priv->clk_lock); - s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1); - if (!s3c24xx_uda134x_snd_device) { - printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: " - "Unable to register\n"); - return -ENOMEM; - } + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, priv); - platform_set_drvdata(s3c24xx_uda134x_snd_device, - &snd_soc_s3c24xx_uda134x); - platform_device_add_data(s3c24xx_uda134x_snd_device, &s3c24xx_uda134x, sizeof(s3c24xx_uda134x)); - ret = platform_device_add(s3c24xx_uda134x_snd_device); - if (ret) { - printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n"); - platform_device_put(s3c24xx_uda134x_snd_device); - } + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) + dev_err(&pdev->dev, "failed to register card: %d\n", ret); return ret; } -static int s3c24xx_uda134x_remove(struct platform_device *pdev) -{ - platform_device_unregister(s3c24xx_uda134x_snd_device); - gpio_free(s3c24xx_uda134x_l3_pins->l3_data); - gpio_free(s3c24xx_uda134x_l3_pins->l3_clk); - gpio_free(s3c24xx_uda134x_l3_pins->l3_mode); - return 0; -} - static struct platform_driver s3c24xx_uda134x_driver = { .probe = s3c24xx_uda134x_probe, - .remove = s3c24xx_uda134x_remove, .driver = { .name = "s3c24xx_uda134x", }, }; - module_platform_driver(s3c24xx_uda134x_driver); MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>"); diff --git a/sound/soc/samsung/smdk2443_wm9710.c b/sound/soc/samsung/smdk2443_wm9710.c deleted file mode 100644 index c390aad68cfb..000000000000 --- a/sound/soc/samsung/smdk2443_wm9710.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * smdk2443_wm9710.c -- SoC audio for smdk2443 - * - * Copyright 2007 Wolfson Microelectronics PLC. - * Author: Graeme Gregory - * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.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; either version 2 of the License, or (at your - * option) any later version. - * - */ - -#include <linux/module.h> -#include <sound/soc.h> - -static struct snd_soc_card smdk2443; - -static struct snd_soc_dai_link smdk2443_dai[] = { -{ - .name = "AC97", - .stream_name = "AC97 HiFi", - .cpu_dai_name = "samsung-ac97", - .codec_dai_name = "ac97-hifi", - .codec_name = "ac97-codec", - .platform_name = "samsung-ac97", -}, -}; - -static struct snd_soc_card smdk2443 = { - .name = "SMDK2443", - .owner = THIS_MODULE, - .dai_link = smdk2443_dai, - .num_links = ARRAY_SIZE(smdk2443_dai), -}; - -static struct platform_device *smdk2443_snd_ac97_device; - -static int __init smdk2443_init(void) -{ - int ret; - - smdk2443_snd_ac97_device = platform_device_alloc("soc-audio", -1); - if (!smdk2443_snd_ac97_device) - return -ENOMEM; - - platform_set_drvdata(smdk2443_snd_ac97_device, &smdk2443); - ret = platform_device_add(smdk2443_snd_ac97_device); - - if (ret) - platform_device_put(smdk2443_snd_ac97_device); - - return ret; -} - -static void __exit smdk2443_exit(void) -{ - platform_device_unregister(smdk2443_snd_ac97_device); -} - -module_init(smdk2443_init); -module_exit(smdk2443_exit); - -/* Module information */ -MODULE_AUTHOR("Graeme Gregory, graeme.gregory@wolfsonmicro.com, www.wolfsonmicro.com"); -MODULE_DESCRIPTION("ALSA SoC WM9710 SMDK2443"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/samsung/smdk_wm8580.c b/sound/soc/samsung/smdk_wm8580.c index 548bfd993788..de724ce7b955 100644 --- a/sound/soc/samsung/smdk_wm8580.c +++ b/sound/soc/samsung/smdk_wm8580.c @@ -14,8 +14,6 @@ #include <sound/soc.h> #include <sound/pcm_params.h> -#include <asm/mach-types.h> - #include "../codecs/wm8580.h" #include "i2s.h" @@ -147,7 +145,6 @@ static int smdk_wm8580_init_paiftx(struct snd_soc_pcm_runtime *rtd) enum { PRI_PLAYBACK = 0, PRI_CAPTURE, - SEC_PLAYBACK, }; #define SMDK_DAI_FMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \ @@ -157,7 +154,7 @@ static struct snd_soc_dai_link smdk_dai[] = { [PRI_PLAYBACK] = { /* Primary Playback i/f */ .name = "WM8580 PAIF RX", .stream_name = "Playback", - .cpu_dai_name = "samsung-i2s.0", + .cpu_dai_name = "samsung-i2s.2", .codec_dai_name = "wm8580-hifi-playback", .platform_name = "samsung-i2s.0", .codec_name = "wm8580.0-001b", @@ -167,7 +164,7 @@ static struct snd_soc_dai_link smdk_dai[] = { [PRI_CAPTURE] = { /* Primary Capture i/f */ .name = "WM8580 PAIF TX", .stream_name = "Capture", - .cpu_dai_name = "samsung-i2s.0", + .cpu_dai_name = "samsung-i2s.2", .codec_dai_name = "wm8580-hifi-capture", .platform_name = "samsung-i2s.0", .codec_name = "wm8580.0-001b", @@ -175,23 +172,13 @@ static struct snd_soc_dai_link smdk_dai[] = { .init = smdk_wm8580_init_paiftx, .ops = &smdk_ops, }, - [SEC_PLAYBACK] = { /* Sec_Fifo Playback i/f */ - .name = "Sec_FIFO TX", - .stream_name = "Playback", - .cpu_dai_name = "samsung-i2s-sec", - .codec_dai_name = "wm8580-hifi-playback", - .platform_name = "samsung-i2s-sec", - .codec_name = "wm8580.0-001b", - .dai_fmt = SMDK_DAI_FMT, - .ops = &smdk_ops, - }, }; static struct snd_soc_card smdk = { .name = "SMDK-I2S", .owner = THIS_MODULE, .dai_link = smdk_dai, - .num_links = 2, + .num_links = ARRAY_SIZE(smdk_dai), .dapm_widgets = smdk_wm8580_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(smdk_wm8580_dapm_widgets), @@ -204,17 +191,6 @@ static struct platform_device *smdk_snd_device; static int __init smdk_audio_init(void) { int ret; - char *str; - - if (machine_is_smdkc100() - || machine_is_smdkv210() || machine_is_smdkc110()) { - smdk.num_links = 3; - } else if (machine_is_smdk6410()) { - str = (char *)smdk_dai[PRI_PLAYBACK].cpu_dai_name; - str[strlen(str) - 1] = '2'; - str = (char *)smdk_dai[PRI_CAPTURE].cpu_dai_name; - str[strlen(str) - 1] = '2'; - } smdk_snd_device = platform_device_alloc("soc-audio", -1); if (!smdk_snd_device) diff --git a/sound/soc/samsung/smdk_wm8580pcm.c b/sound/soc/samsung/smdk_wm8580pcm.c deleted file mode 100644 index 6deec5234c92..000000000000 --- a/sound/soc/samsung/smdk_wm8580pcm.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * sound/soc/samsung/smdk_wm8580pcm.c - * - * Copyright (c) 2011 Samsung Electronics Co. Ltd - * - * 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; either version 2 of the License, or (at your - * option) any later version. - */ -#include <linux/module.h> -#include <sound/soc.h> -#include <sound/pcm_params.h> -#include <sound/pcm.h> - -#include <asm/mach-types.h> - -#include "../codecs/wm8580.h" -#include "dma.h" -#include "pcm.h" - -/* - * Board Settings: - * o '1' means 'ON' - * o '0' means 'OFF' - * o 'X' means 'Don't care' - * - * SMDK6410 Base B/D: CFG1-0000, CFG2-1111 - * SMDKC110, SMDKV210: CFGB11-100100, CFGB12-0000 - */ - -#define SMDK_WM8580_EXT_OSC 12000000 -#define SMDK_WM8580_EXT_MCLK 4096000 -#define SMDK_WM8580_EXT_VOICE 2048000 - -static unsigned long mclk_freq; -static unsigned long xtal_freq; - -/* - * If MCLK clock directly gets from XTAL, we don't have to use PLL - * to make MCLK, but if XTAL clock source connects with other codec - * pin (like XTI), we should have to set codec's PLL to make MCLK. - * Because Samsung SoC does not support pcmcdclk output like I2S. - */ - -static int smdk_wm8580_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int rfs, ret; - - switch (params_rate(params)) { - case 8000: - break; - default: - printk(KERN_ERR "%s:%d Sampling Rate %u not supported!\n", - __func__, __LINE__, params_rate(params)); - return -EINVAL; - } - - rfs = mclk_freq / params_rate(params) / 2; - - if (mclk_freq == xtal_freq) { - ret = snd_soc_dai_set_sysclk(codec_dai, WM8580_CLKSRC_MCLK, - mclk_freq, SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK, - WM8580_CLKSRC_MCLK); - if (ret < 0) - return ret; - } else { - ret = snd_soc_dai_set_sysclk(codec_dai, WM8580_CLKSRC_PLLA, - mclk_freq, SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK, - WM8580_CLKSRC_PLLA); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_pll(codec_dai, WM8580_PLLA, 0, - xtal_freq, mclk_freq); - if (ret < 0) - return ret; - } - - /* Set PCM source clock on CPU */ - ret = snd_soc_dai_set_sysclk(cpu_dai, S3C_PCM_CLKSRC_MUX, - mclk_freq, SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - - /* Set SCLK_DIV for making bclk */ - ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_PCM_SCLK_PER_FS, rfs); - if (ret < 0) - return ret; - - return 0; -} - -static struct snd_soc_ops smdk_wm8580_pcm_ops = { - .hw_params = smdk_wm8580_pcm_hw_params, -}; - -#define SMDK_DAI_FMT (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF | \ - SND_SOC_DAIFMT_CBS_CFS) - -static struct snd_soc_dai_link smdk_dai[] = { - { - .name = "WM8580 PAIF PCM RX", - .stream_name = "Playback", - .cpu_dai_name = "samsung-pcm.0", - .codec_dai_name = "wm8580-hifi-playback", - .platform_name = "samsung-audio", - .codec_name = "wm8580.0-001b", - .dai_fmt = SMDK_DAI_FMT, - .ops = &smdk_wm8580_pcm_ops, - }, { - .name = "WM8580 PAIF PCM TX", - .stream_name = "Capture", - .cpu_dai_name = "samsung-pcm.0", - .codec_dai_name = "wm8580-hifi-capture", - .platform_name = "samsung-pcm.0", - .codec_name = "wm8580.0-001b", - .dai_fmt = SMDK_DAI_FMT, - .ops = &smdk_wm8580_pcm_ops, - }, -}; - -static struct snd_soc_card smdk_pcm = { - .name = "SMDK-PCM", - .owner = THIS_MODULE, - .dai_link = smdk_dai, - .num_links = 2, -}; - -/* - * After SMDKC110 Base Board's Rev is '0.1', 12MHz External OSC(X1) - * is absent (or not connected), so we connect EXT_VOICE_CLK(OSC4), - * 2.0484Mhz, directly with MCLK both Codec and SoC. - */ -static int snd_smdk_probe(struct platform_device *pdev) -{ - int ret = 0; - - xtal_freq = SMDK_WM8580_EXT_OSC; - mclk_freq = SMDK_WM8580_EXT_MCLK; - - if (machine_is_smdkc110() || machine_is_smdkv210()) - xtal_freq = mclk_freq = SMDK_WM8580_EXT_VOICE; - - smdk_pcm.dev = &pdev->dev; - ret = devm_snd_soc_register_card(&pdev->dev, &smdk_pcm); - if (ret) - dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret); - - return ret; -} - -static struct platform_driver snd_smdk_driver = { - .driver = { - .name = "samsung-smdk-pcm", - }, - .probe = snd_smdk_probe, -}; - -module_platform_driver(snd_smdk_driver); - -MODULE_AUTHOR("Sangbeom Kim, <sbkim73@samsung.com>"); -MODULE_DESCRIPTION("ALSA SoC SMDK WM8580 for PCM"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/samsung/smdk_wm8994pcm.c b/sound/soc/samsung/smdk_wm8994pcm.c index b1c89ec2d999..2e621496be8b 100644 --- a/sound/soc/samsung/smdk_wm8994pcm.c +++ b/sound/soc/samsung/smdk_wm8994pcm.c @@ -15,7 +15,6 @@ #include <sound/pcm_params.h> #include "../codecs/wm8994.h" -#include "dma.h" #include "pcm.h" /* diff --git a/sound/soc/samsung/smdk_wm9713.c b/sound/soc/samsung/smdk_wm9713.c deleted file mode 100644 index 0d20e4ed27aa..000000000000 --- a/sound/soc/samsung/smdk_wm9713.c +++ /dev/null @@ -1,108 +0,0 @@ -/* - * smdk_wm9713.c -- SoC audio for SMDK - * - * Copyright 2010 Samsung Electronics Co. Ltd. - * Author: Jaswinder Singh Brar <jassisinghbrar@gmail.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; either version 2 of the - * License, or (at your option) any later version. - * - */ - -#include <linux/module.h> -#include <sound/soc.h> - -static struct snd_soc_card smdk; - -/* - * Default CFG switch settings to use this driver: - * - * SMDK6410: Set CFG1 1-3 On, CFG2 1-4 Off - * SMDKC100: Set CFG6 1-3 On, CFG7 1 On - * SMDKC110: Set CFGB10 1-2 Off, CFGB12 1-3 On - * SMDKV210: Set CFGB10 1-2 Off, CFGB12 1-3 On - * SMDKV310: Set CFG2 1-2 Off, CFG4 All On, CFG7 All Off, CFG8 1-On - */ - -/* - Playback (HeadPhone):- - $ amixer sset 'Headphone' unmute - $ amixer sset 'Right Headphone Out Mux' 'Headphone' - $ amixer sset 'Left Headphone Out Mux' 'Headphone' - $ amixer sset 'Right HP Mixer PCM' unmute - $ amixer sset 'Left HP Mixer PCM' unmute - - Capture (LineIn):- - $ amixer sset 'Right Capture Source' 'Line' - $ amixer sset 'Left Capture Source' 'Line' -*/ - -static struct snd_soc_dai_link smdk_dai = { - .name = "AC97", - .stream_name = "AC97 PCM", - .platform_name = "samsung-ac97", - .cpu_dai_name = "samsung-ac97", - .codec_dai_name = "wm9713-hifi", - .codec_name = "wm9713-codec", -}; - -static struct snd_soc_card smdk = { - .name = "SMDK WM9713", - .owner = THIS_MODULE, - .dai_link = &smdk_dai, - .num_links = 1, -}; - -static struct platform_device *smdk_snd_wm9713_device; -static struct platform_device *smdk_snd_ac97_device; - -static int __init smdk_init(void) -{ - int ret; - - smdk_snd_wm9713_device = platform_device_alloc("wm9713-codec", -1); - if (!smdk_snd_wm9713_device) - return -ENOMEM; - - ret = platform_device_add(smdk_snd_wm9713_device); - if (ret) - goto err1; - - smdk_snd_ac97_device = platform_device_alloc("soc-audio", -1); - if (!smdk_snd_ac97_device) { - ret = -ENOMEM; - goto err2; - } - - platform_set_drvdata(smdk_snd_ac97_device, &smdk); - - ret = platform_device_add(smdk_snd_ac97_device); - if (ret) - goto err3; - - return 0; - -err3: - platform_device_put(smdk_snd_ac97_device); -err2: - platform_device_del(smdk_snd_wm9713_device); -err1: - platform_device_put(smdk_snd_wm9713_device); - return ret; -} - -static void __exit smdk_exit(void) -{ - platform_device_unregister(smdk_snd_ac97_device); - platform_device_unregister(smdk_snd_wm9713_device); -} - -module_init(smdk_init); -module_exit(smdk_exit); - -/* Module information */ -MODULE_AUTHOR("Jaswinder Singh Brar, jassisinghbrar@gmail.com"); -MODULE_DESCRIPTION("ALSA SoC SMDK+WM9713"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c index 0cb9c8567546..779504f54bc0 100644 --- a/sound/soc/samsung/spdif.c +++ b/sound/soc/samsung/spdif.c @@ -90,10 +90,10 @@ struct samsung_spdif_info { u32 saved_clkcon; u32 saved_con; u32 saved_cstas; - struct s3c_dma_params *dma_playback; + struct snd_dmaengine_dai_dma_data *dma_playback; }; -static struct s3c_dma_params spdif_stereo_out; +static struct snd_dmaengine_dai_dma_data spdif_stereo_out; static struct samsung_spdif_info spdif_info; static inline struct samsung_spdif_info *to_info(struct snd_soc_dai *cpu_dai) @@ -179,7 +179,7 @@ static int spdif_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct samsung_spdif_info *spdif = to_info(rtd->cpu_dai); void __iomem *regs = spdif->regs; - struct s3c_dma_params *dma_data; + struct snd_dmaengine_dai_dma_data *dma_data; u32 con, clkcon, cstas; unsigned long flags; int i, ratio; @@ -416,23 +416,13 @@ static int spdif_probe(struct platform_device *pdev) goto err3; } - dev_set_drvdata(&pdev->dev, spdif); - - ret = devm_snd_soc_register_component(&pdev->dev, - &samsung_spdif_component, &samsung_spdif_dai, 1); - if (ret != 0) { - dev_err(&pdev->dev, "fail to register dai\n"); - goto err4; - } - - spdif_stereo_out.dma_size = 2; - spdif_stereo_out.dma_addr = mem_res->start + DATA_OUTBUF; + spdif_stereo_out.addr_width = 2; + spdif_stereo_out.addr = mem_res->start + DATA_OUTBUF; filter = NULL; if (spdif_pdata) { - spdif_stereo_out.slave = spdif_pdata->dma_playback; + spdif_stereo_out.filter_data = spdif_pdata->dma_playback; filter = spdif_pdata->dma_filter; } - spdif->dma_playback = &spdif_stereo_out; ret = samsung_asoc_dma_platform_register(&pdev->dev, filter, @@ -442,6 +432,15 @@ static int spdif_probe(struct platform_device *pdev) goto err4; } + dev_set_drvdata(&pdev->dev, spdif); + + ret = devm_snd_soc_register_component(&pdev->dev, + &samsung_spdif_component, &samsung_spdif_dai, 1); + if (ret != 0) { + dev_err(&pdev->dev, "fail to register dai\n"); + goto err4; + } + return 0; err4: iounmap(spdif->regs); diff --git a/sound/soc/samsung/tm2_wm5110.c b/sound/soc/samsung/tm2_wm5110.c new file mode 100644 index 000000000000..5cdf7d19b87f --- /dev/null +++ b/sound/soc/samsung/tm2_wm5110.c @@ -0,0 +1,552 @@ +/* + * Copyright (C) 2015 - 2016 Samsung Electronics Co., Ltd. + * + * Authors: Inha Song <ideal.song@samsung.com> + * Sylwester Nawrocki <s.nawrocki@samsung.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; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/clk.h> +#include <linux/gpio.h> +#include <linux/module.h> +#include <linux/of.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "i2s.h" +#include "../codecs/wm5110.h" + +/* + * The source clock is XCLKOUT with its mux set to the external fixed rate + * oscillator (XXTI). + */ +#define MCLK_RATE 24000000U + +#define TM2_DAI_AIF1 0 +#define TM2_DAI_AIF2 1 + +struct tm2_machine_priv { + struct snd_soc_codec *codec; + unsigned int sysclk_rate; + struct gpio_desc *gpio_mic_bias; +}; + +static int tm2_start_sysclk(struct snd_soc_card *card) +{ + struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_codec *codec = priv->codec; + int ret; + + ret = snd_soc_codec_set_pll(codec, WM5110_FLL1_REFCLK, + ARIZONA_FLL_SRC_MCLK1, + MCLK_RATE, + priv->sysclk_rate); + if (ret < 0) { + dev_err(codec->dev, "Failed to set FLL1 source: %d\n", ret); + return ret; + } + + ret = snd_soc_codec_set_pll(codec, WM5110_FLL1, + ARIZONA_FLL_SRC_MCLK1, + MCLK_RATE, + priv->sysclk_rate); + if (ret < 0) { + dev_err(codec->dev, "Failed to start FLL1: %d\n", ret); + return ret; + } + + ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_SYSCLK, + ARIZONA_CLK_SRC_FLL1, + priv->sysclk_rate, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(codec->dev, "Failed to set SYSCLK source: %d\n", ret); + return ret; + } + + return 0; +} + +static int tm2_stop_sysclk(struct snd_soc_card *card) +{ + struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_codec *codec = priv->codec; + int ret; + + ret = snd_soc_codec_set_pll(codec, WM5110_FLL1, 0, 0, 0); + if (ret < 0) { + dev_err(codec->dev, "Failed to stop FLL1: %d\n", ret); + return ret; + } + + ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_SYSCLK, + ARIZONA_CLK_SRC_FLL1, 0, 0); + if (ret < 0) { + dev_err(codec->dev, "Failed to stop SYSCLK: %d\n", ret); + return ret; + } + + return 0; +} + +static int tm2_aif1_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(rtd->card); + + switch (params_rate(params)) { + case 4000: + case 8000: + case 12000: + case 16000: + case 24000: + case 32000: + case 48000: + case 96000: + case 192000: + /* Highest possible SYSCLK frequency: 147.456MHz */ + priv->sysclk_rate = 147456000U; + break; + case 11025: + case 22050: + case 44100: + case 88200: + case 176400: + /* Highest possible SYSCLK frequency: 135.4752 MHz */ + priv->sysclk_rate = 135475200U; + break; + default: + dev_err(codec->dev, "Not supported sample rate: %d\n", + params_rate(params)); + return -EINVAL; + } + + return tm2_start_sysclk(rtd->card); +} + +static struct snd_soc_ops tm2_aif1_ops = { + .hw_params = tm2_aif1_hw_params, +}; + +static int tm2_aif2_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + unsigned int asyncclk_rate; + int ret; + + switch (params_rate(params)) { + case 8000: + case 12000: + case 16000: + /* Highest possible ASYNCCLK frequency: 49.152MHz */ + asyncclk_rate = 49152000U; + break; + case 11025: + /* Highest possible ASYNCCLK frequency: 45.1584 MHz */ + asyncclk_rate = 45158400U; + break; + default: + dev_err(codec->dev, "Not supported sample rate: %d\n", + params_rate(params)); + return -EINVAL; + } + + ret = snd_soc_codec_set_pll(codec, WM5110_FLL2_REFCLK, + ARIZONA_FLL_SRC_MCLK1, + MCLK_RATE, + asyncclk_rate); + if (ret < 0) { + dev_err(codec->dev, "Failed to set FLL2 source: %d\n", ret); + return ret; + } + + ret = snd_soc_codec_set_pll(codec, WM5110_FLL2, + ARIZONA_FLL_SRC_MCLK1, + MCLK_RATE, + asyncclk_rate); + if (ret < 0) { + dev_err(codec->dev, "Failed to start FLL2: %d\n", ret); + return ret; + } + + ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_ASYNCCLK, + ARIZONA_CLK_SRC_FLL2, + asyncclk_rate, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(codec->dev, "Failed to set ASYNCCLK source: %d\n", ret); + return ret; + } + + return 0; +} + +static int tm2_aif2_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + int ret; + + /* disable FLL2 */ + ret = snd_soc_codec_set_pll(codec, WM5110_FLL2, ARIZONA_FLL_SRC_MCLK1, + 0, 0); + if (ret < 0) + dev_err(codec->dev, "Failed to stop FLL2: %d\n", ret); + + return ret; +} + +static struct snd_soc_ops tm2_aif2_ops = { + .hw_params = tm2_aif2_hw_params, + .hw_free = tm2_aif2_hw_free, +}; + +static int tm2_mic_bias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_card *card = w->dapm->card; + struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + gpiod_set_value_cansleep(priv->gpio_mic_bias, 1); + break; + case SND_SOC_DAPM_POST_PMD: + gpiod_set_value_cansleep(priv->gpio_mic_bias, 0); + break; + } + + return 0; +} + +static int tm2_set_bias_level(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm, + enum snd_soc_bias_level level) +{ + struct snd_soc_pcm_runtime *rtd; + + rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name); + + if (dapm->dev != rtd->codec_dai->dev) + return 0; + + switch (level) { + case SND_SOC_BIAS_STANDBY: + if (card->dapm.bias_level == SND_SOC_BIAS_OFF) + tm2_start_sysclk(card); + break; + case SND_SOC_BIAS_OFF: + tm2_stop_sysclk(card); + break; + default: + break; + } + + return 0; +} + +static struct snd_soc_aux_dev tm2_speaker_amp_dev; + +static int tm2_late_probe(struct snd_soc_card *card) +{ + struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_dai_link_component dlc = { 0 }; + unsigned int ch_map[] = { 0, 1 }; + struct snd_soc_dai *amp_pdm_dai; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai *aif1_dai; + struct snd_soc_dai *aif2_dai; + int ret; + + rtd = snd_soc_get_pcm_runtime(card, card->dai_link[TM2_DAI_AIF1].name); + aif1_dai = rtd->codec_dai; + priv->codec = rtd->codec; + + ret = snd_soc_dai_set_sysclk(aif1_dai, ARIZONA_CLK_SYSCLK, 0, 0); + if (ret < 0) { + dev_err(aif1_dai->dev, "Failed to set SYSCLK: %d\n", ret); + return ret; + } + + rtd = snd_soc_get_pcm_runtime(card, card->dai_link[TM2_DAI_AIF2].name); + aif2_dai = rtd->codec_dai; + + ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0); + if (ret < 0) { + dev_err(aif2_dai->dev, "Failed to set ASYNCCLK: %d\n", ret); + return ret; + } + + dlc.of_node = tm2_speaker_amp_dev.codec_of_node; + amp_pdm_dai = snd_soc_find_dai(&dlc); + if (!amp_pdm_dai) + return -ENODEV; + + /* Set the MAX98504 V/I sense PDM Tx DAI channel mapping */ + ret = snd_soc_dai_set_channel_map(amp_pdm_dai, ARRAY_SIZE(ch_map), + ch_map, 0, NULL); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_tdm_slot(amp_pdm_dai, 0x3, 0x0, 2, 16); + if (ret < 0) + return ret; + + return 0; +} + +static const struct snd_kcontrol_new tm2_controls[] = { + SOC_DAPM_PIN_SWITCH("HP"), + SOC_DAPM_PIN_SWITCH("SPK"), + SOC_DAPM_PIN_SWITCH("RCV"), + SOC_DAPM_PIN_SWITCH("VPS"), + SOC_DAPM_PIN_SWITCH("HDMI"), + + SOC_DAPM_PIN_SWITCH("Main Mic"), + SOC_DAPM_PIN_SWITCH("Sub Mic"), + SOC_DAPM_PIN_SWITCH("Third Mic"), + + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +const struct snd_soc_dapm_widget tm2_dapm_widgets[] = { + SND_SOC_DAPM_HP("HP", NULL), + SND_SOC_DAPM_SPK("SPK", NULL), + SND_SOC_DAPM_SPK("RCV", NULL), + SND_SOC_DAPM_LINE("VPS", NULL), + SND_SOC_DAPM_LINE("HDMI", NULL), + + SND_SOC_DAPM_MIC("Main Mic", tm2_mic_bias), + SND_SOC_DAPM_MIC("Sub Mic", NULL), + SND_SOC_DAPM_MIC("Third Mic", NULL), + + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + +static const struct snd_soc_component_driver tm2_component = { + .name = "tm2-audio", +}; + +static struct snd_soc_dai_driver tm2_ext_dai[] = { + { + .name = "Voice call", + .playback = { + .channels_min = 1, + .channels_max = 4, + .rate_min = 8000, + .rate_max = 48000, + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .channels_min = 1, + .channels_max = 4, + .rate_min = 8000, + .rate_max = 48000, + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + }, + { + .name = "Bluetooth", + .playback = { + .channels_min = 1, + .channels_max = 4, + .rate_min = 8000, + .rate_max = 16000, + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 16000, + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + }, +}; + +static struct snd_soc_dai_link tm2_dai_links[] = { + { + .name = "WM5110 AIF1", + .stream_name = "HiFi Primary", + .codec_dai_name = "wm5110-aif1", + .ops = &tm2_aif1_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, + }, { + .name = "WM5110 Voice", + .stream_name = "Voice call", + .codec_dai_name = "wm5110-aif2", + .ops = &tm2_aif2_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, + .ignore_suspend = 1, + }, { + .name = "WM5110 BT", + .stream_name = "Bluetooth", + .codec_dai_name = "wm5110-aif3", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, + .ignore_suspend = 1, + } +}; + +static struct snd_soc_card tm2_card = { + .owner = THIS_MODULE, + + .dai_link = tm2_dai_links, + .num_links = ARRAY_SIZE(tm2_dai_links), + .controls = tm2_controls, + .num_controls = ARRAY_SIZE(tm2_controls), + .dapm_widgets = tm2_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tm2_dapm_widgets), + .aux_dev = &tm2_speaker_amp_dev, + .num_aux_devs = 1, + + .late_probe = tm2_late_probe, + .set_bias_level = tm2_set_bias_level, +}; + +static int tm2_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct snd_soc_card *card = &tm2_card; + struct tm2_machine_priv *priv; + struct device_node *cpu_dai_node, *codec_dai_node; + int ret, i; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + snd_soc_card_set_drvdata(card, priv); + card->dev = dev; + + priv->gpio_mic_bias = devm_gpiod_get(dev, "mic-bias", + GPIOF_OUT_INIT_LOW); + if (IS_ERR(priv->gpio_mic_bias)) { + dev_err(dev, "Failed to get mic bias gpio\n"); + return PTR_ERR(priv->gpio_mic_bias); + } + + ret = snd_soc_of_parse_card_name(card, "model"); + if (ret < 0) { + dev_err(dev, "Card name is not specified\n"); + return ret; + } + + ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing"); + if (ret < 0) { + dev_err(dev, "Audio routing is not specified or invalid\n"); + return ret; + } + + card->aux_dev[0].codec_of_node = of_parse_phandle(dev->of_node, + "audio-amplifier", 0); + if (!card->aux_dev[0].codec_of_node) { + dev_err(dev, "audio-amplifier property invalid or missing\n"); + return -EINVAL; + } + + cpu_dai_node = of_parse_phandle(dev->of_node, "i2s-controller", 0); + if (!cpu_dai_node) { + dev_err(dev, "i2s-controllers property invalid or missing\n"); + ret = -EINVAL; + goto amp_node_put; + } + + codec_dai_node = of_parse_phandle(dev->of_node, "audio-codec", 0); + if (!codec_dai_node) { + dev_err(dev, "audio-codec property invalid or missing\n"); + ret = -EINVAL; + goto cpu_dai_node_put; + } + + for (i = 0; i < card->num_links; i++) { + card->dai_link[i].cpu_dai_name = NULL; + card->dai_link[i].cpu_name = NULL; + card->dai_link[i].platform_name = NULL; + card->dai_link[i].codec_of_node = codec_dai_node; + card->dai_link[i].cpu_of_node = cpu_dai_node; + card->dai_link[i].platform_of_node = cpu_dai_node; + } + + ret = devm_snd_soc_register_component(dev, &tm2_component, + tm2_ext_dai, ARRAY_SIZE(tm2_ext_dai)); + if (ret < 0) { + dev_err(dev, "Failed to register component: %d\n", ret); + goto codec_dai_node_put; + } + + ret = devm_snd_soc_register_card(dev, card); + if (ret < 0) { + dev_err(dev, "Failed to register card: %d\n", ret); + goto codec_dai_node_put; + } + +codec_dai_node_put: + of_node_put(codec_dai_node); +cpu_dai_node_put: + of_node_put(cpu_dai_node); +amp_node_put: + of_node_put(card->aux_dev[0].codec_of_node); + return ret; +} + +static int tm2_pm_prepare(struct device *dev) +{ + struct snd_soc_card *card = dev_get_drvdata(dev); + + return tm2_stop_sysclk(card); +} + +static void tm2_pm_complete(struct device *dev) +{ + struct snd_soc_card *card = dev_get_drvdata(dev); + + tm2_start_sysclk(card); +} + +const struct dev_pm_ops tm2_pm_ops = { + .prepare = tm2_pm_prepare, + .suspend = snd_soc_suspend, + .resume = snd_soc_resume, + .complete = tm2_pm_complete, + .freeze = snd_soc_suspend, + .thaw = snd_soc_resume, + .poweroff = snd_soc_poweroff, + .restore = snd_soc_resume, +}; + +static const struct of_device_id tm2_of_match[] = { + { .compatible = "samsung,tm2-audio" }, + { }, +}; +MODULE_DEVICE_TABLE(of, tm2_of_match); + +static struct platform_driver tm2_driver = { + .driver = { + .name = "tm2-audio", + .pm = &tm2_pm_ops, + .of_match_table = tm2_of_match, + }, + .probe = tm2_probe, +}; +module_platform_driver(tm2_driver); + +MODULE_AUTHOR("Inha Song <ideal.song@samsung.com>"); +MODULE_DESCRIPTION("ALSA SoC Exynos TM2 Audio Support"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 9311f119feb5..147ebecfed94 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -1,5 +1,5 @@ menu "SoC Audio support for SuperH" - depends on SUPERH || ARCH_SHMOBILE + depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST config SND_SOC_PCM_SH7760 tristate "SoC Audio support for Renesas SH7760" @@ -37,17 +37,12 @@ config SND_SOC_SH4_SIU config SND_SOC_RCAR tristate "R-Car series SRU/SCU/SSIU/SSI support" depends on COMMON_CLK + depends on OF || COMPILE_TEST select SND_SIMPLE_CARD select REGMAP_MMIO help This option enables R-Car SRU/SCU/SSIU/SSI sound support -config SND_SOC_RSRC_CARD - tristate "Renesas Sampling Rate Convert Sound Card" - select SND_SIMPLE_CARD_UTILS - help - This option enables simple sound if you need sampling rate convert - ## ## Boards ## diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index a89ddf758695..9c3d5aed99d1 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile @@ -1,5 +1,2 @@ snd-soc-rcar-objs := core.o gen.o dma.o adg.o ssi.o ssiu.o src.o ctu.o mix.o dvc.o cmd.o obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o - -snd-soc-rsrc-card-objs := rsrc-card.o -obj-$(CONFIG_SND_SOC_RSRC_CARD) += snd-soc-rsrc-card.o diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 2145957d0229..85a33ac0a5c4 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -34,6 +34,9 @@ struct rsnd_adg { struct clk_onecell_data onecell; struct rsnd_mod mod; u32 flags; + u32 ckr; + u32 rbga; + u32 rbgb; int rbga_rate_for_441khz; /* RBGA */ int rbgb_rate_for_48khz; /* RBGB */ @@ -316,9 +319,11 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate) struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); struct rsnd_adg *adg = rsnd_priv_to_adg(priv); struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_mod *adg_mod = rsnd_mod_get(adg); struct clk *clk; int i; u32 data; + u32 ckr = 0; int sel_table[] = { [CLKA] = 0x1, [CLKB] = 0x2, @@ -360,15 +365,14 @@ found_clock: rsnd_adg_set_ssi_clk(ssi_mod, data); if (!(adg_mode_flags(adg) & LRCLK_ASYNC)) { - struct rsnd_mod *adg_mod = rsnd_mod_get(adg); - u32 ckr = 0; - if (0 == (rate % 8000)) ckr = 0x80000000; - - rsnd_mod_bset(adg_mod, SSICKR, 0x80000000, ckr); } + rsnd_mod_bset(adg_mod, BRGCKR, 0x80FF0000, adg->ckr | ckr); + rsnd_mod_write(adg_mod, BRRA, adg->rbga); + rsnd_mod_write(adg_mod, BRRB, adg->rbgb); + dev_dbg(dev, "ADG: %s[%d] selects 0x%x for %d\n", rsnd_mod_name(ssi_mod), rsnd_mod_id(ssi_mod), data, rate); @@ -376,6 +380,25 @@ found_clock: return 0; } +void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable) +{ + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct device *dev = rsnd_priv_to_dev(priv); + struct clk *clk; + int i, ret; + + for_each_rsnd_clk(clk, adg, i) { + ret = 0; + if (enable) + ret = clk_prepare_enable(clk); + else + clk_disable_unprepare(clk); + + if (ret < 0) + dev_warn(dev, "can't use clk %d\n", i); + } +} + static void rsnd_adg_get_clkin(struct rsnd_priv *priv, struct rsnd_adg *adg) { @@ -387,27 +410,21 @@ static void rsnd_adg_get_clkin(struct rsnd_priv *priv, [CLKC] = "clk_c", [CLKI] = "clk_i", }; - int i, ret; + int i; for (i = 0; i < CLKMAX; i++) { clk = devm_clk_get(dev, clk_name[i]); adg->clk[i] = IS_ERR(clk) ? NULL : clk; } - for_each_rsnd_clk(clk, adg, i) { - ret = clk_prepare_enable(clk); - if (ret < 0) - dev_warn(dev, "can't use clk %d\n", i); - + for_each_rsnd_clk(clk, adg, i) dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk)); - } } static void rsnd_adg_get_clkout(struct rsnd_priv *priv, struct rsnd_adg *adg) { struct clk *clk; - struct rsnd_mod *adg_mod = rsnd_mod_get(adg); struct device *dev = rsnd_priv_to_dev(priv); struct device_node *np = dev->of_node; u32 ckr, rbgx, rbga, rbgb; @@ -532,13 +549,13 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv, } } - rsnd_mod_bset(adg_mod, SSICKR, 0x80FF0000, ckr); - rsnd_mod_write(adg_mod, BRRA, rbga); - rsnd_mod_write(adg_mod, BRRB, rbgb); + adg->ckr = ckr; + adg->rbga = rbga; + adg->rbgb = rbgb; for_each_rsnd_clkout(clk, adg, i) dev_dbg(dev, "clkout %d : %p : %ld\n", i, clk, clk_get_rate(clk)); - dev_dbg(dev, "SSICKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n", + dev_dbg(dev, "BRGCKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n", ckr, rbga, rbgb); } @@ -565,16 +582,12 @@ int rsnd_adg_probe(struct rsnd_priv *priv) priv->adg = adg; + rsnd_adg_clk_enable(priv); + return 0; } void rsnd_adg_remove(struct rsnd_priv *priv) { - struct rsnd_adg *adg = rsnd_priv_to_adg(priv); - struct clk *clk; - int i; - - for_each_rsnd_clk(clk, adg, i) { - clk_disable_unprepare(clk); - } + rsnd_adg_clk_disable(priv); } diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 3351a701c60e..99b5b0835c1e 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -110,6 +110,7 @@ MODULE_DEVICE_TABLE(of, rsnd_of_match); /* * rsnd_mod functions */ +#ifdef DEBUG void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type) { if (mod->type != type) { @@ -120,6 +121,7 @@ void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type) rsnd_mod_name(mod), rsnd_mod_id(mod)); } } +#endif char *rsnd_mod_name(struct rsnd_mod *mod) { @@ -304,7 +306,7 @@ u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io) */ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { - struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); + struct rsnd_mod *ssiu = rsnd_io_to_mod_ssiu(io); struct rsnd_mod *target; struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); u32 val = 0x76543210; @@ -313,11 +315,11 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) if (rsnd_io_is_play(io)) { struct rsnd_mod *src = rsnd_io_to_mod_src(io); - target = src ? src : ssi; + target = src ? src : ssiu; } else { struct rsnd_mod *cmd = rsnd_io_to_mod_cmd(io); - target = cmd ? cmd : ssi; + target = cmd ? cmd : ssiu; } mask <<= runtime->channels * 4; @@ -346,32 +348,28 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) /* * rsnd_dai functions */ -#define rsnd_mod_call(idx, io, func, param...) \ -({ \ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \ - struct rsnd_mod *mod = (io)->mod[idx]; \ - struct device *dev = rsnd_priv_to_dev(priv); \ - u32 *status = mod->get_status(io, mod, idx); \ - u32 mask = 0xF << __rsnd_mod_shift_##func; \ - u8 val = (*status >> __rsnd_mod_shift_##func) & 0xF; \ - u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \ - int ret = 0; \ - int call = (val == __rsnd_mod_call_##func) && (mod)->ops->func; \ - if (add == 0xF) \ - call = 0; \ - else \ - *status = (*status & ~mask) + \ - (add << __rsnd_mod_shift_##func); \ - dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \ - rsnd_mod_name(mod), rsnd_mod_id(mod), \ - *status, call ? #func : ""); \ - if (call) \ - ret = (mod)->ops->func(mod, io, param); \ - if (ret) \ - dev_dbg(dev, "%s[%d] : rsnd_mod_call error %d\n", \ - rsnd_mod_name(mod), rsnd_mod_id(mod), ret); \ - ret; \ -}) +struct rsnd_mod *rsnd_mod_next(int *iterator, + struct rsnd_dai_stream *io, + enum rsnd_mod_type *array, + int array_size) +{ + struct rsnd_mod *mod; + enum rsnd_mod_type type; + int max = array ? array_size : RSND_MOD_MAX; + + for (; *iterator < max; (*iterator)++) { + type = (array) ? array[*iterator] : *iterator; + mod = io->mod[type]; + if (!mod) + continue; + + (*iterator)++; + + return mod; + } + + return NULL; +} static enum rsnd_mod_type rsnd_mod_sequence[][RSND_MOD_MAX] = { { @@ -407,19 +405,49 @@ static enum rsnd_mod_type rsnd_mod_sequence[][RSND_MOD_MAX] = { }, }; -#define rsnd_dai_call(fn, io, param...) \ -({ \ - struct rsnd_mod *mod; \ - int type, is_play = rsnd_io_is_play(io); \ - int ret = 0, i; \ - for (i = 0; i < RSND_MOD_MAX; i++) { \ - type = rsnd_mod_sequence[is_play][i]; \ - mod = (io)->mod[type]; \ - if (!mod) \ - continue; \ - ret |= rsnd_mod_call(type, io, fn, param); \ - } \ - ret; \ +static int rsnd_status_update(u32 *status, + int shift, int add, int timing) +{ + u32 mask = 0xF << shift; + u8 val = (*status >> shift) & 0xF; + u8 next_val = (val + add) & 0xF; + int func_call = (val == timing); + + if (next_val == 0xF) /* underflow case */ + func_call = 0; + else + *status = (*status & ~mask) + (next_val << shift); + + return func_call; +} + +#define rsnd_dai_call(fn, io, param...) \ +({ \ + struct rsnd_priv *priv = rsnd_io_to_priv(io); \ + struct device *dev = rsnd_priv_to_dev(priv); \ + struct rsnd_mod *mod; \ + int is_play = rsnd_io_is_play(io); \ + int ret = 0, i; \ + enum rsnd_mod_type *types = rsnd_mod_sequence[is_play]; \ + for_each_rsnd_mod_arrays(i, mod, io, types, RSND_MOD_MAX) { \ + int tmp = 0; \ + u32 *status = mod->get_status(io, mod, types[i]); \ + int func_call = rsnd_status_update(status, \ + __rsnd_mod_shift_##fn, \ + __rsnd_mod_add_##fn, \ + __rsnd_mod_call_##fn); \ + dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \ + rsnd_mod_name(mod), rsnd_mod_id(mod), *status, \ + (func_call && (mod)->ops->fn) ? #fn : ""); \ + if (func_call && (mod)->ops->fn) \ + tmp = (mod)->ops->fn(mod, io, param); \ + if (tmp) \ + dev_err(dev, "%s[%d] : %s error %d\n", \ + rsnd_mod_name(mod), rsnd_mod_id(mod), \ + #fn, tmp); \ + ret |= tmp; \ + } \ + ret; \ }) int rsnd_dai_connect(struct rsnd_mod *mod, @@ -574,6 +602,7 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, switch (cmd) { case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: rsnd_dai_stream_init(io, substream); ret = rsnd_dai_call(init, io, priv); @@ -590,6 +619,7 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, break; case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: ret = rsnd_dai_call(irq, io, priv, 0); ret |= rsnd_dai_call(stop, io, priv); @@ -686,7 +716,33 @@ static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai, return 0; } +static int rsnd_soc_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); + + /* + * call rsnd_dai_call without spinlock + */ + return rsnd_dai_call(nolock_start, io, priv); +} + +static void rsnd_soc_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); + + /* + * call rsnd_dai_call without spinlock + */ + rsnd_dai_call(nolock_stop, io, priv); +} + static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { + .startup = rsnd_soc_dai_startup, + .shutdown = rsnd_soc_dai_shutdown, .trigger = rsnd_soc_dai_trigger, .set_fmt = rsnd_soc_dai_set_fmt, .set_tdm_slot = rsnd_soc_set_dai_tdm_slot, @@ -974,10 +1030,8 @@ static int __rsnd_kctrl_new(struct rsnd_mod *mod, return -ENOMEM; ret = snd_ctl_add(card, kctrl); - if (ret < 0) { - snd_ctl_free_one(kctrl); + if (ret < 0) return ret; - } cfg->update = update; cfg->card = card; @@ -989,7 +1043,11 @@ static int __rsnd_kctrl_new(struct rsnd_mod *mod, void _rsnd_kctrl_remove(struct rsnd_kctrl_cfg *cfg) { - snd_ctl_remove(cfg->card, cfg->kctrl); + if (cfg->card && cfg->kctrl) + snd_ctl_remove(cfg->card, cfg->kctrl); + + cfg->card = NULL; + cfg->kctrl = NULL; } int rsnd_kctrl_new_m(struct rsnd_mod *mod, @@ -1066,8 +1124,8 @@ static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd) return snd_pcm_lib_preallocate_pages_for_all( rtd->pcm, - SNDRV_DMA_TYPE_DEV, - rtd->card->snd_card->dev, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), PREALLOC_BUFFER, PREALLOC_BUFFER_MAX); } @@ -1088,6 +1146,7 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv, ret = rsnd_dai_call(probe, io, priv); if (ret == -EAGAIN) { struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io); + struct rsnd_mod *mod; int i; /* @@ -1107,8 +1166,8 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv, * remove all mod from io * and, re connect ssi */ - for (i = 0; i < RSND_MOD_MAX; i++) - rsnd_dai_disconnect((io)->mod[i], io, i); + for_each_rsnd_mod(i, mod, io) + rsnd_dai_disconnect(mod, io, i); rsnd_dai_connect(ssi_mod, io, RSND_MOD_SSI); /* @@ -1247,9 +1306,33 @@ static int rsnd_remove(struct platform_device *pdev) return ret; } +static int rsnd_suspend(struct device *dev) +{ + struct rsnd_priv *priv = dev_get_drvdata(dev); + + rsnd_adg_clk_disable(priv); + + return 0; +} + +static int rsnd_resume(struct device *dev) +{ + struct rsnd_priv *priv = dev_get_drvdata(dev); + + rsnd_adg_clk_enable(priv); + + return 0; +} + +static struct dev_pm_ops rsnd_pm_ops = { + .suspend = rsnd_suspend, + .resume = rsnd_resume, +}; + static struct platform_driver rsnd_driver = { .driver = { .name = "rcar_sound", + .pm = &rsnd_pm_ops, .of_match_table = rsnd_of_match, }, .probe = rsnd_probe, diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index 6bc93cbb3049..1f405c833867 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -25,6 +25,10 @@ struct rsnd_dmaen { struct dma_chan *chan; + dma_addr_t dma_buf; + unsigned int dma_len; + unsigned int dma_period; + unsigned int dma_cnt; }; struct rsnd_dmapp { @@ -34,6 +38,8 @@ struct rsnd_dmapp { struct rsnd_dma { struct rsnd_mod mod; + struct rsnd_mod *mod_from; + struct rsnd_mod *mod_to; dma_addr_t src_addr; dma_addr_t dst_addr; union { @@ -56,10 +62,38 @@ struct rsnd_dma_ctrl { /* * Audio DMAC */ +#define rsnd_dmaen_sync(dmaen, io, i) __rsnd_dmaen_sync(dmaen, io, i, 1) +#define rsnd_dmaen_unsync(dmaen, io, i) __rsnd_dmaen_sync(dmaen, io, i, 0) +static void __rsnd_dmaen_sync(struct rsnd_dmaen *dmaen, struct rsnd_dai_stream *io, + int i, int sync) +{ + struct device *dev = dmaen->chan->device->dev; + enum dma_data_direction dir; + int is_play = rsnd_io_is_play(io); + dma_addr_t buf; + int len, max; + size_t period; + + len = dmaen->dma_len; + period = dmaen->dma_period; + max = len / period; + i = i % max; + buf = dmaen->dma_buf + (period * i); + + dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + + if (sync) + dma_sync_single_for_device(dev, buf, period, dir); + else + dma_sync_single_for_cpu(dev, buf, period, dir); +} + static void __rsnd_dmaen_complete(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_dma *dma = rsnd_mod_to_dma(mod); + struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); bool elapsed = false; unsigned long flags; @@ -76,9 +110,22 @@ static void __rsnd_dmaen_complete(struct rsnd_mod *mod, */ spin_lock_irqsave(&priv->lock, flags); - if (rsnd_io_is_working(io)) + if (rsnd_io_is_working(io)) { + rsnd_dmaen_unsync(dmaen, io, dmaen->dma_cnt); + + /* + * Next period is already started. + * Let's sync Next Next period + * see + * rsnd_dmaen_start() + */ + rsnd_dmaen_sync(dmaen, io, dmaen->dma_cnt + 2); + elapsed = rsnd_dai_pointer_update(io, io->byte_per_period); + dmaen->dma_cnt++; + } + spin_unlock_irqrestore(&priv->lock, flags); if (elapsed) @@ -92,6 +139,20 @@ static void rsnd_dmaen_complete(void *data) rsnd_mod_interrupt(mod, __rsnd_dmaen_complete); } +static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_dai_stream *io, + struct rsnd_mod *mod_from, + struct rsnd_mod *mod_to) +{ + if ((!mod_from && !mod_to) || + (mod_from && mod_to)) + return NULL; + + if (mod_from) + return rsnd_mod_dma_req(io, mod_from); + else + return rsnd_mod_dma_req(io, mod_to); +} + static int rsnd_dmaen_stop(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) @@ -99,7 +160,66 @@ static int rsnd_dmaen_stop(struct rsnd_mod *mod, struct rsnd_dma *dma = rsnd_mod_to_dma(mod); struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); - dmaengine_terminate_all(dmaen->chan); + if (dmaen->chan) { + int is_play = rsnd_io_is_play(io); + + dmaengine_terminate_all(dmaen->chan); + dma_unmap_single(dmaen->chan->device->dev, + dmaen->dma_buf, dmaen->dma_len, + is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + } + + return 0; +} + +static int rsnd_dmaen_nolock_stop(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct rsnd_dma *dma = rsnd_mod_to_dma(mod); + struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); + + /* + * DMAEngine release uses mutex lock. + * Thus, it shouldn't be called under spinlock. + * Let's call it under nolock_start + */ + if (dmaen->chan) + dma_release_channel(dmaen->chan); + + dmaen->chan = NULL; + + return 0; +} + +static int rsnd_dmaen_nolock_start(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct rsnd_dma *dma = rsnd_mod_to_dma(mod); + struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); + struct device *dev = rsnd_priv_to_dev(priv); + + if (dmaen->chan) { + dev_err(dev, "it already has dma channel\n"); + return -EIO; + } + + /* + * DMAEngine request uses mutex lock. + * Thus, it shouldn't be called under spinlock. + * Let's call it under nolock_start + */ + dmaen->chan = rsnd_dmaen_request_channel(io, + dma->mod_from, + dma->mod_to); + if (IS_ERR_OR_NULL(dmaen->chan)) { + int ret = PTR_ERR(dmaen->chan); + + dmaen->chan = NULL; + dev_err(dev, "can't get dma channel\n"); + return ret; + } return 0; } @@ -113,12 +233,41 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod, struct snd_pcm_substream *substream = io->substream; struct device *dev = rsnd_priv_to_dev(priv); struct dma_async_tx_descriptor *desc; + struct dma_slave_config cfg = {}; + dma_addr_t buf; + size_t len; + size_t period; int is_play = rsnd_io_is_play(io); + int i; + int ret; + + cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; + cfg.src_addr = dma->src_addr; + cfg.dst_addr = dma->dst_addr; + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + + dev_dbg(dev, "%s[%d] %pad -> %pad\n", + rsnd_mod_name(mod), rsnd_mod_id(mod), + &cfg.src_addr, &cfg.dst_addr); + + ret = dmaengine_slave_config(dmaen->chan, &cfg); + if (ret < 0) + return ret; + + len = snd_pcm_lib_buffer_bytes(substream); + period = snd_pcm_lib_period_bytes(substream); + buf = dma_map_single(dmaen->chan->device->dev, + substream->runtime->dma_area, + len, + is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + if (dma_mapping_error(dmaen->chan->device->dev, buf)) { + dev_err(dev, "dma map failed\n"); + return -EIO; + } desc = dmaengine_prep_dma_cyclic(dmaen->chan, - substream->runtime->dma_addr, - snd_pcm_lib_buffer_bytes(substream), - snd_pcm_lib_period_bytes(substream), + buf, len, period, is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); @@ -130,6 +279,19 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod, desc->callback = rsnd_dmaen_complete; desc->callback_param = rsnd_mod_get(dma); + dmaen->dma_buf = buf; + dmaen->dma_len = len; + dmaen->dma_period = period; + dmaen->dma_cnt = 0; + + /* + * synchronize this and next period + * see + * __rsnd_dmaen_complete() + */ + for (i = 0; i < 2; i++) + rsnd_dmaen_sync(dmaen, io, i); + if (dmaengine_submit(desc) < 0) { dev_err(dev, "dmaengine_submit() fail\n"); return -EIO; @@ -143,124 +305,55 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod, struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, struct rsnd_mod *mod, char *name) { - struct dma_chan *chan; + struct dma_chan *chan = NULL; struct device_node *np; int i = 0; for_each_child_of_node(of_node, np) { - if (i == rsnd_mod_id(mod)) - break; + if (i == rsnd_mod_id(mod) && (!chan)) + chan = of_dma_request_slave_channel(np, name); i++; } - chan = of_dma_request_slave_channel(np, name); - - of_node_put(np); + /* It should call of_node_put(), since, it is rsnd_xxx_of_node() */ of_node_put(of_node); return chan; } -static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_dai_stream *io, - struct rsnd_mod *mod_from, - struct rsnd_mod *mod_to) -{ - if ((!mod_from && !mod_to) || - (mod_from && mod_to)) - return NULL; - - if (mod_from) - return rsnd_mod_dma_req(io, mod_from); - else - return rsnd_mod_dma_req(io, mod_to); -} - -static int rsnd_dmaen_remove(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct rsnd_dma *dma = rsnd_mod_to_dma(mod); - struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); - - if (dmaen->chan) - dma_release_channel(dmaen->chan); - - dmaen->chan = NULL; - - return 0; -} - static int rsnd_dmaen_attach(struct rsnd_dai_stream *io, - struct rsnd_dma *dma, int id, + struct rsnd_dma *dma, struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) { - struct rsnd_mod *mod = rsnd_mod_get(dma); - struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); struct rsnd_priv *priv = rsnd_io_to_priv(io); struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); - struct device *dev = rsnd_priv_to_dev(priv); - struct dma_slave_config cfg = {}; - int is_play = rsnd_io_is_play(io); - int ret; - - if (dmaen->chan) { - dev_err(dev, "it already has dma channel\n"); - return -EIO; - } - - if (dev->of_node) { - dmaen->chan = rsnd_dmaen_request_channel(io, mod_from, mod_to); - } else { - dma_cap_mask_t mask; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); + struct dma_chan *chan; - dmaen->chan = dma_request_channel(mask, shdma_chan_filter, - (void *)(uintptr_t)id); - } - if (IS_ERR_OR_NULL(dmaen->chan)) { - dmaen->chan = NULL; - dev_err(dev, "can't get dma channel\n"); - goto rsnd_dma_channel_err; + /* try to get DMAEngine channel */ + chan = rsnd_dmaen_request_channel(io, mod_from, mod_to); + if (IS_ERR_OR_NULL(chan)) { + /* + * DMA failed. try to PIO mode + * see + * rsnd_ssi_fallback() + * rsnd_rdai_continuance_probe() + */ + return -EAGAIN; } - cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; - cfg.src_addr = dma->src_addr; - cfg.dst_addr = dma->dst_addr; - cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - - dev_dbg(dev, "%s[%d] %pad -> %pad\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), - &cfg.src_addr, &cfg.dst_addr); - - ret = dmaengine_slave_config(dmaen->chan, &cfg); - if (ret < 0) - goto rsnd_dma_attach_err; + dma_release_channel(chan); dmac->dmaen_num++; return 0; - -rsnd_dma_attach_err: - rsnd_dmaen_remove(mod, io, priv); -rsnd_dma_channel_err: - - /* - * DMA failed. try to PIO mode - * see - * rsnd_ssi_fallback() - * rsnd_rdai_continuance_probe() - */ - return -EAGAIN; } static struct rsnd_mod_ops rsnd_dmaen_ops = { .name = "audmac", + .nolock_start = rsnd_dmaen_nolock_start, + .nolock_stop = rsnd_dmaen_nolock_stop, .start = rsnd_dmaen_start, .stop = rsnd_dmaen_stop, - .remove = rsnd_dmaen_remove, }; /* @@ -394,7 +487,7 @@ static int rsnd_dmapp_start(struct rsnd_mod *mod, } static int rsnd_dmapp_attach(struct rsnd_dai_stream *io, - struct rsnd_dma *dma, int id, + struct rsnd_dma *dma, struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) { struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma); @@ -627,7 +720,7 @@ static void rsnd_dma_of_path(struct rsnd_mod *this, } int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod, - struct rsnd_mod **dma_mod, int id) + struct rsnd_mod **dma_mod) { struct rsnd_mod *mod_from = NULL; struct rsnd_mod *mod_to = NULL; @@ -636,7 +729,7 @@ int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod, struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_mod_ops *ops; enum rsnd_mod_type type; - int (*attach)(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id, + int (*attach)(struct rsnd_dai_stream *io, struct rsnd_dma *dma, struct rsnd_mod *mod_from, struct rsnd_mod *mod_to); int is_play = rsnd_io_is_play(io); int ret, dma_id; @@ -682,9 +775,6 @@ int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod, *dma_mod = rsnd_mod_get(dma); - dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1); - dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0); - ret = rsnd_mod_init(priv, *dma_mod, ops, NULL, rsnd_mod_get_status, type, dma_id); if (ret < 0) @@ -695,9 +785,14 @@ int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod, rsnd_mod_name(mod_from), rsnd_mod_id(mod_from), rsnd_mod_name(mod_to), rsnd_mod_id(mod_to)); - ret = attach(io, dma, id, mod_from, mod_to); + ret = attach(io, dma, mod_from, mod_to); if (ret < 0) return ret; + + dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1); + dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0); + dma->mod_from = mod_from; + dma->mod_to = mod_to; } ret = rsnd_dai_connect(*dma_mod, io, type); diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index 02d971f69eff..cf8f59cdd8d7 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -48,8 +48,6 @@ struct rsnd_dvc { #define rsnd_dvc_get(priv, id) ((struct rsnd_dvc *)(priv->dvc) + id) #define rsnd_dvc_nr(priv) ((priv)->dvc_nr) -#define rsnd_dvc_of_node(priv) \ - of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc") #define rsnd_mod_to_dvc(_mod) \ container_of((_mod), struct rsnd_dvc, mod) diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 7d2fdf8dd188..63b6d3c28021 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -211,6 +211,14 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv) RSND_GEN_S_REG(SSI_MODE1, 0x804), RSND_GEN_S_REG(SSI_MODE2, 0x808), RSND_GEN_S_REG(SSI_CONTROL, 0x810), + RSND_GEN_S_REG(SSI_SYS_STATUS0, 0x840), + RSND_GEN_S_REG(SSI_SYS_STATUS1, 0x844), + RSND_GEN_S_REG(SSI_SYS_STATUS2, 0x848), + RSND_GEN_S_REG(SSI_SYS_STATUS3, 0x84c), + RSND_GEN_S_REG(SSI_SYS_STATUS4, 0x880), + RSND_GEN_S_REG(SSI_SYS_STATUS5, 0x884), + RSND_GEN_S_REG(SSI_SYS_STATUS6, 0x888), + RSND_GEN_S_REG(SSI_SYS_STATUS7, 0x88c), /* FIXME: it needs SSI_MODE2/3 in the future */ RSND_GEN_M_REG(SSI_BUSIF_MODE, 0x0, 0x80), @@ -311,7 +319,7 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv) static const struct rsnd_regmap_field_conf conf_adg[] = { RSND_GEN_S_REG(BRRA, 0x00), RSND_GEN_S_REG(BRRB, 0x04), - RSND_GEN_S_REG(SSICKR, 0x08), + RSND_GEN_S_REG(BRGCKR, 0x08), RSND_GEN_S_REG(AUDIO_CLK_SEL0, 0x0c), RSND_GEN_S_REG(AUDIO_CLK_SEL1, 0x10), RSND_GEN_S_REG(AUDIO_CLK_SEL2, 0x14), @@ -362,7 +370,7 @@ static int rsnd_gen1_probe(struct rsnd_priv *priv) static const struct rsnd_regmap_field_conf conf_adg[] = { RSND_GEN_S_REG(BRRA, 0x00), RSND_GEN_S_REG(BRRB, 0x04), - RSND_GEN_S_REG(SSICKR, 0x08), + RSND_GEN_S_REG(BRGCKR, 0x08), RSND_GEN_S_REG(AUDIO_CLK_SEL0, 0x0c), RSND_GEN_S_REG(AUDIO_CLK_SEL1, 0x10), }; diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index a8f61d79333b..b90df77662df 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -43,17 +43,7 @@ * see gen1/gen2 for detail */ enum rsnd_reg { - /* SCU (SRC/SSIU/MIX/CTU/DVC) */ - RSND_REG_SSI_MODE, /* Gen2 only */ - RSND_REG_SSI_MODE0, - RSND_REG_SSI_MODE1, - RSND_REG_SSI_MODE2, - RSND_REG_SSI_CONTROL, - RSND_REG_SSI_CTRL, /* Gen2 only */ - RSND_REG_SSI_BUSIF_MODE, /* Gen2 only */ - RSND_REG_SSI_BUSIF_ADINR, /* Gen2 only */ - RSND_REG_SSI_BUSIF_DALIGN, /* Gen2 only */ - RSND_REG_SSI_INT_ENABLE, /* Gen2 only */ + /* SCU (MIX/CTU/DVC) */ RSND_REG_SRC_I_BUSIF_MODE, RSND_REG_SRC_O_BUSIF_MODE, RSND_REG_SRC_ROUTE_MODE0, @@ -63,29 +53,29 @@ enum rsnd_reg { RSND_REG_SRC_IFSCR, RSND_REG_SRC_IFSVR, RSND_REG_SRC_SRCCR, - RSND_REG_SRC_CTRL, /* Gen2 only */ - RSND_REG_SRC_BSDSR, /* Gen2 only */ - RSND_REG_SRC_BSISR, /* Gen2 only */ - RSND_REG_SRC_INT_ENABLE0, /* Gen2 only */ - RSND_REG_SRC_BUSIF_DALIGN, /* Gen2 only */ - RSND_REG_SRCIN_TIMSEL0, /* Gen2 only */ - RSND_REG_SRCIN_TIMSEL1, /* Gen2 only */ - RSND_REG_SRCIN_TIMSEL2, /* Gen2 only */ - RSND_REG_SRCIN_TIMSEL3, /* Gen2 only */ - RSND_REG_SRCIN_TIMSEL4, /* Gen2 only */ - RSND_REG_SRCOUT_TIMSEL0, /* Gen2 only */ - RSND_REG_SRCOUT_TIMSEL1, /* Gen2 only */ - RSND_REG_SRCOUT_TIMSEL2, /* Gen2 only */ - RSND_REG_SRCOUT_TIMSEL3, /* Gen2 only */ - RSND_REG_SRCOUT_TIMSEL4, /* Gen2 only */ + RSND_REG_SRC_CTRL, + RSND_REG_SRC_BSDSR, + RSND_REG_SRC_BSISR, + RSND_REG_SRC_INT_ENABLE0, + RSND_REG_SRC_BUSIF_DALIGN, + RSND_REG_SRCIN_TIMSEL0, + RSND_REG_SRCIN_TIMSEL1, + RSND_REG_SRCIN_TIMSEL2, + RSND_REG_SRCIN_TIMSEL3, + RSND_REG_SRCIN_TIMSEL4, + RSND_REG_SRCOUT_TIMSEL0, + RSND_REG_SRCOUT_TIMSEL1, + RSND_REG_SRCOUT_TIMSEL2, + RSND_REG_SRCOUT_TIMSEL3, + RSND_REG_SRCOUT_TIMSEL4, RSND_REG_SCU_SYS_STATUS0, - RSND_REG_SCU_SYS_STATUS1, /* Gen2 only */ + RSND_REG_SCU_SYS_STATUS1, RSND_REG_SCU_SYS_INT_EN0, - RSND_REG_SCU_SYS_INT_EN1, /* Gen2 only */ - RSND_REG_CMD_CTRL, /* Gen2 only */ - RSND_REG_CMD_BUSIF_DALIGN, /* Gen2 only */ + RSND_REG_SCU_SYS_INT_EN1, + RSND_REG_CMD_CTRL, + RSND_REG_CMD_BUSIF_DALIGN, RSND_REG_CMD_ROUTE_SLCT, - RSND_REG_CMDOUT_TIMSEL, /* Gen2 only */ + RSND_REG_CMDOUT_TIMSEL, RSND_REG_CTU_SWRSR, RSND_REG_CTU_CTUIR, RSND_REG_CTU_ADINR, @@ -147,18 +137,38 @@ enum rsnd_reg { RSND_REG_DVC_VOL6R, RSND_REG_DVC_VOL7R, RSND_REG_DVC_DVUER, - RSND_REG_DVC_VRCTR, /* Gen2 only */ - RSND_REG_DVC_VRPDR, /* Gen2 only */ - RSND_REG_DVC_VRDBR, /* Gen2 only */ + RSND_REG_DVC_VRCTR, + RSND_REG_DVC_VRPDR, + RSND_REG_DVC_VRDBR, /* ADG */ RSND_REG_BRRA, RSND_REG_BRRB, - RSND_REG_SSICKR, - RSND_REG_DIV_EN, /* Gen2 only */ + RSND_REG_BRGCKR, + RSND_REG_DIV_EN, RSND_REG_AUDIO_CLK_SEL0, RSND_REG_AUDIO_CLK_SEL1, - RSND_REG_AUDIO_CLK_SEL2, /* Gen2 only */ + RSND_REG_AUDIO_CLK_SEL2, + + /* SSIU */ + RSND_REG_SSI_MODE, + RSND_REG_SSI_MODE0, + RSND_REG_SSI_MODE1, + RSND_REG_SSI_MODE2, + RSND_REG_SSI_CONTROL, + RSND_REG_SSI_CTRL, + RSND_REG_SSI_BUSIF_MODE, + RSND_REG_SSI_BUSIF_ADINR, + RSND_REG_SSI_BUSIF_DALIGN, + RSND_REG_SSI_INT_ENABLE, + RSND_REG_SSI_SYS_STATUS0, + RSND_REG_SSI_SYS_STATUS1, + RSND_REG_SSI_SYS_STATUS2, + RSND_REG_SSI_SYS_STATUS3, + RSND_REG_SSI_SYS_STATUS4, + RSND_REG_SSI_SYS_STATUS5, + RSND_REG_SSI_SYS_STATUS6, + RSND_REG_SSI_SYS_STATUS7, /* SSI */ RSND_REG_SSICR, @@ -199,7 +209,7 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io); * R-Car DMA */ int rsnd_dma_attach(struct rsnd_dai_stream *io, - struct rsnd_mod *mod, struct rsnd_mod **dma_mod, int id); + struct rsnd_mod *mod, struct rsnd_mod **dma_mod); int rsnd_dma_probe(struct rsnd_priv *priv); struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, struct rsnd_mod *mod, char *name); @@ -259,6 +269,12 @@ struct rsnd_mod_ops { int (*fallback)(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv); + int (*nolock_start)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv); + int (*nolock_stop)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv); }; struct rsnd_dai_stream; @@ -278,7 +294,7 @@ struct rsnd_mod { * * 0xH0000CBA * - * A 0: probe 1: remove + * A 0: nolock_start 1: nolock_stop * B 0: init 1: quit * C 0: start 1: stop * @@ -288,19 +304,23 @@ struct rsnd_mod { * H 0: fallback * H 0: hw_params */ -#define __rsnd_mod_shift_probe 0 -#define __rsnd_mod_shift_remove 0 +#define __rsnd_mod_shift_nolock_start 0 +#define __rsnd_mod_shift_nolock_stop 0 #define __rsnd_mod_shift_init 4 #define __rsnd_mod_shift_quit 4 #define __rsnd_mod_shift_start 8 #define __rsnd_mod_shift_stop 8 +#define __rsnd_mod_shift_probe 28 /* always called */ +#define __rsnd_mod_shift_remove 28 /* always called */ #define __rsnd_mod_shift_irq 28 /* always called */ #define __rsnd_mod_shift_pcm_new 28 /* always called */ #define __rsnd_mod_shift_fallback 28 /* always called */ #define __rsnd_mod_shift_hw_params 28 /* always called */ -#define __rsnd_mod_add_probe 1 -#define __rsnd_mod_add_remove -1 +#define __rsnd_mod_add_probe 0 +#define __rsnd_mod_add_remove 0 +#define __rsnd_mod_add_nolock_start 1 +#define __rsnd_mod_add_nolock_stop -1 #define __rsnd_mod_add_init 1 #define __rsnd_mod_add_quit -1 #define __rsnd_mod_add_start 1 @@ -311,7 +331,7 @@ struct rsnd_mod { #define __rsnd_mod_add_hw_params 0 #define __rsnd_mod_call_probe 0 -#define __rsnd_mod_call_remove 1 +#define __rsnd_mod_call_remove 0 #define __rsnd_mod_call_init 0 #define __rsnd_mod_call_quit 1 #define __rsnd_mod_call_start 0 @@ -320,6 +340,8 @@ struct rsnd_mod { #define __rsnd_mod_call_pcm_new 0 #define __rsnd_mod_call_fallback 0 #define __rsnd_mod_call_hw_params 0 +#define __rsnd_mod_call_nolock_start 0 +#define __rsnd_mod_call_nolock_stop 1 #define rsnd_mod_to_priv(mod) ((mod)->priv) #define rsnd_mod_id(mod) ((mod) ? (mod)->id : -1) @@ -346,6 +368,18 @@ void rsnd_mod_interrupt(struct rsnd_mod *mod, u32 *rsnd_mod_get_status(struct rsnd_dai_stream *io, struct rsnd_mod *mod, enum rsnd_mod_type type); +struct rsnd_mod *rsnd_mod_next(int *iterator, + struct rsnd_dai_stream *io, + enum rsnd_mod_type *array, + int array_size); +#define for_each_rsnd_mod(iterator, pos, io) \ + for (iterator = 0; \ + (pos = rsnd_mod_next(&iterator, io, NULL, 0));) +#define for_each_rsnd_mod_arrays(iterator, pos, io, array, size) \ + for (iterator = 0; \ + (pos = rsnd_mod_next(&iterator, io, array, size));) +#define for_each_rsnd_mod_array(iterator, pos, io, array) \ + for_each_rsnd_mod_arrays(iterator, pos, io, array, ARRAY_SIZE(array)) void rsnd_parse_connect_common(struct rsnd_dai *rdai, struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id), @@ -365,6 +399,18 @@ int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io); int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io); /* + * DT + */ +#define rsnd_parse_of_node(priv, node) \ + of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, node) +#define RSND_NODE_DAI "rcar_sound,dai" +#define RSND_NODE_SSI "rcar_sound,ssi" +#define RSND_NODE_SRC "rcar_sound,src" +#define RSND_NODE_CTU "rcar_sound,ctu" +#define RSND_NODE_MIX "rcar_sound,mix" +#define RSND_NODE_DVC "rcar_sound,dvc" + +/* * R-Car sound DAI */ #define RSND_DAI_NAME_SIZE 16 @@ -382,6 +428,7 @@ struct rsnd_dai_stream { }; #define rsnd_io_to_mod(io, i) ((i) < RSND_MOD_MAX ? (io)->mod[(i)] : NULL) #define rsnd_io_to_mod_ssi(io) rsnd_io_to_mod((io), RSND_MOD_SSI) +#define rsnd_io_to_mod_ssiu(io) rsnd_io_to_mod((io), RSND_MOD_SSIU) #define rsnd_io_to_mod_ssip(io) rsnd_io_to_mod((io), RSND_MOD_SSIP) #define rsnd_io_to_mod_src(io) rsnd_io_to_mod((io), RSND_MOD_SRC) #define rsnd_io_to_mod_ctu(io) rsnd_io_to_mod((io), RSND_MOD_CTU) @@ -428,8 +475,7 @@ int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional); int rsnd_dai_connect(struct rsnd_mod *mod, struct rsnd_dai_stream *io, enum rsnd_mod_type type); -#define rsnd_dai_of_node(priv) \ - of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dai") +#define rsnd_dai_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_DAI) /* * R-Car Gen1/Gen2 @@ -453,6 +499,9 @@ int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod, unsigned int out_rate); int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod, struct rsnd_dai_stream *io); +#define rsnd_adg_clk_enable(priv) rsnd_adg_clk_control(priv, 1) +#define rsnd_adg_clk_disable(priv) rsnd_adg_clk_control(priv, 0) +void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable); /* * R-Car sound priv @@ -606,8 +655,7 @@ u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io); __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io)) int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); -#define rsnd_ssi_of_node(priv) \ - of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi") +#define rsnd_ssi_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_SSI) void rsnd_parse_connect_ssi(struct rsnd_dai *rdai, struct device_node *playback, struct device_node *capture); @@ -633,8 +681,7 @@ unsigned int rsnd_src_get_rate(struct rsnd_priv *priv, struct rsnd_dai_stream *io, int is_in); -#define rsnd_src_of_node(priv) \ - of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src") +#define rsnd_src_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_SRC) #define rsnd_parse_connect_src(rdai, playback, capture) \ rsnd_parse_connect_common(rdai, rsnd_src_mod_get, \ rsnd_src_of_node(rsnd_rdai_to_priv(rdai)), \ @@ -647,8 +694,7 @@ int rsnd_ctu_probe(struct rsnd_priv *priv); void rsnd_ctu_remove(struct rsnd_priv *priv); int rsnd_ctu_converted_channel(struct rsnd_mod *mod); struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id); -#define rsnd_ctu_of_node(priv) \ - of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ctu") +#define rsnd_ctu_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_CTU) #define rsnd_parse_connect_ctu(rdai, playback, capture) \ rsnd_parse_connect_common(rdai, rsnd_ctu_mod_get, \ rsnd_ctu_of_node(rsnd_rdai_to_priv(rdai)), \ @@ -660,8 +706,7 @@ struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id); int rsnd_mix_probe(struct rsnd_priv *priv); void rsnd_mix_remove(struct rsnd_priv *priv); struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id); -#define rsnd_mix_of_node(priv) \ - of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,mix") +#define rsnd_mix_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_MIX) #define rsnd_parse_connect_mix(rdai, playback, capture) \ rsnd_parse_connect_common(rdai, rsnd_mix_mod_get, \ rsnd_mix_of_node(rsnd_rdai_to_priv(rdai)), \ @@ -673,8 +718,7 @@ struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id); int rsnd_dvc_probe(struct rsnd_priv *priv); void rsnd_dvc_remove(struct rsnd_priv *priv); struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id); -#define rsnd_dvc_of_node(priv) \ - of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc") +#define rsnd_dvc_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_DVC) #define rsnd_parse_connect_dvc(rdai, playback, capture) \ rsnd_parse_connect_common(rdai, rsnd_dvc_mod_get, \ rsnd_dvc_of_node(rsnd_rdai_to_priv(rdai)), \ diff --git a/sound/soc/sh/rcar/rsrc-card.c b/sound/soc/sh/rcar/rsrc-card.c deleted file mode 100644 index fa37f842b62f..000000000000 --- a/sound/soc/sh/rcar/rsrc-card.c +++ /dev/null @@ -1,486 +0,0 @@ -/* - * Renesas Sampling Rate Convert Sound Card for DPCM - * - * Copyright (C) 2015 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * based on ${LINUX}/sound/soc/generic/simple-card.c - * - * 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/device.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/of_device.h> -#include <linux/platform_device.h> -#include <linux/string.h> -#include <sound/jack.h> -#include <sound/soc.h> -#include <sound/soc-dai.h> -#include <sound/simple_card_utils.h> - -struct rsrc_card_of_data { - const char *prefix; - const struct snd_soc_dapm_route *routes; - int num_routes; -}; - -static const struct snd_soc_dapm_route routes_ssi0_ak4642[] = { - {"ak4642 Playback", NULL, "DAI0 Playback"}, - {"DAI0 Capture", NULL, "ak4642 Capture"}, -}; - -static const struct rsrc_card_of_data routes_of_ssi0_ak4642 = { - .prefix = "ak4642", - .routes = routes_ssi0_ak4642, - .num_routes = ARRAY_SIZE(routes_ssi0_ak4642), -}; - -static const struct of_device_id rsrc_card_of_match[] = { - { .compatible = "renesas,rsrc-card,lager", .data = &routes_of_ssi0_ak4642 }, - { .compatible = "renesas,rsrc-card,koelsch", .data = &routes_of_ssi0_ak4642 }, - { .compatible = "renesas,rsrc-card", }, - {}, -}; -MODULE_DEVICE_TABLE(of, rsrc_card_of_match); - -#define IDX_CPU 0 -#define IDX_CODEC 1 -struct rsrc_card_priv { - struct snd_soc_card snd_card; - struct snd_soc_codec_conf codec_conf; - struct asoc_simple_dai *dai_props; - struct snd_soc_dai_link *dai_link; - u32 convert_rate; - u32 convert_channels; -}; - -#define rsrc_priv_to_dev(priv) ((priv)->snd_card.dev) -#define rsrc_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i)) -#define rsrc_priv_to_props(priv, i) ((priv)->dai_props + (i)) - -static int rsrc_card_startup(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct asoc_simple_dai *dai_props = - rsrc_priv_to_props(priv, rtd->num); - - return clk_prepare_enable(dai_props->clk); -} - -static void rsrc_card_shutdown(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct asoc_simple_dai *dai_props = - rsrc_priv_to_props(priv, rtd->num); - - clk_disable_unprepare(dai_props->clk); -} - -static struct snd_soc_ops rsrc_card_ops = { - .startup = rsrc_card_startup, - .shutdown = rsrc_card_shutdown, -}; - -static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd) -{ - struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *dai; - struct snd_soc_dai_link *dai_link; - struct asoc_simple_dai *dai_props; - int num = rtd->num; - int ret; - - dai_link = rsrc_priv_to_link(priv, num); - dai_props = rsrc_priv_to_props(priv, num); - dai = dai_link->dynamic ? - rtd->cpu_dai : - rtd->codec_dai; - - if (dai_props->sysclk) { - ret = snd_soc_dai_set_sysclk(dai, 0, dai_props->sysclk, 0); - if (ret && ret != -ENOTSUPP) { - dev_err(dai->dev, "set_sysclk error\n"); - goto err; - } - } - - if (dai_props->slots) { - ret = snd_soc_dai_set_tdm_slot(dai, - dai_props->tx_slot_mask, - dai_props->rx_slot_mask, - dai_props->slots, - dai_props->slot_width); - if (ret && ret != -ENOTSUPP) { - dev_err(dai->dev, "set_tdm_slot error\n"); - goto err; - } - } - - ret = 0; - -err: - return ret; -} - -static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) -{ - struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct snd_interval *rate = hw_param_interval(params, - SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *channels = hw_param_interval(params, - SNDRV_PCM_HW_PARAM_CHANNELS); - - if (priv->convert_rate) - rate->min = - rate->max = priv->convert_rate; - - if (priv->convert_channels) - channels->min = - channels->max = priv->convert_channels; - - return 0; -} - -static int rsrc_card_parse_links(struct device_node *np, - struct rsrc_card_priv *priv, - int idx, bool is_fe) -{ - struct device *dev = rsrc_priv_to_dev(priv); - struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx); - struct asoc_simple_dai *dai_props = rsrc_priv_to_props(priv, idx); - struct of_phandle_args args; - int ret; - - /* - * Get node via "sound-dai = <&phandle port>" - * it will be used as xxx_of_node on soc_bind_dai_link() - */ - ret = of_parse_phandle_with_args(np, "sound-dai", - "#sound-dai-cells", 0, &args); - if (ret) - return ret; - - /* Parse TDM slot */ - ret = snd_soc_of_parse_tdm_slot(np, - &dai_props->tx_slot_mask, - &dai_props->rx_slot_mask, - &dai_props->slots, - &dai_props->slot_width); - if (ret) - return ret; - - if (is_fe) { - /* BE is dummy */ - dai_link->codec_of_node = NULL; - dai_link->codec_dai_name = "snd-soc-dummy-dai"; - dai_link->codec_name = "snd-soc-dummy"; - - /* FE settings */ - dai_link->dynamic = 1; - dai_link->dpcm_merged_format = 1; - dai_link->cpu_of_node = args.np; - ret = snd_soc_of_get_dai_name(np, &dai_link->cpu_dai_name); - if (ret < 0) - return ret; - - ret = asoc_simple_card_set_dailink_name(dev, dai_link, - "fe.%s", - dai_link->cpu_dai_name); - if (ret < 0) - return ret; - - /* - * 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 (!args.args_count) - dai_link->cpu_dai_name = NULL; - } else { - const struct rsrc_card_of_data *of_data; - - of_data = of_device_get_match_data(dev); - - /* FE is dummy */ - dai_link->cpu_of_node = NULL; - dai_link->cpu_dai_name = "snd-soc-dummy-dai"; - dai_link->cpu_name = "snd-soc-dummy"; - - /* BE settings */ - dai_link->no_pcm = 1; - dai_link->be_hw_params_fixup = rsrc_card_be_hw_params_fixup; - dai_link->codec_of_node = args.np; - ret = snd_soc_of_get_dai_name(np, &dai_link->codec_dai_name); - if (ret < 0) - return ret; - - ret = asoc_simple_card_set_dailink_name(dev, dai_link, - "be.%s", - dai_link->codec_dai_name); - if (ret < 0) - return ret; - - /* additional name prefix */ - if (of_data) { - priv->codec_conf.of_node = dai_link->codec_of_node; - priv->codec_conf.name_prefix = of_data->prefix; - } else { - snd_soc_of_parse_audio_prefix(&priv->snd_card, - &priv->codec_conf, - dai_link->codec_of_node, - "audio-prefix"); - } - } - - /* Simple Card assumes platform == cpu */ - dai_link->platform_of_node = dai_link->cpu_of_node; - dai_link->dpcm_playback = 1; - dai_link->dpcm_capture = 1; - dai_link->ops = &rsrc_card_ops; - dai_link->init = rsrc_card_dai_init; - - return 0; -} - -static int rsrc_card_parse_clk(struct device_node *np, - struct rsrc_card_priv *priv, - int idx, bool is_fe) -{ - struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx); - struct asoc_simple_dai *dai_props = rsrc_priv_to_props(priv, idx); - struct clk *clk; - struct device_node *of_np = is_fe ? dai_link->cpu_of_node : - dai_link->codec_of_node; - u32 val; - - /* - * Parse dai->sysclk come from "clocks = <&xxx>" - * (if system has common clock) - * or "system-clock-frequency = <xxx>" - * or device's module clock. - */ - if (of_property_read_bool(np, "clocks")) { - clk = of_clk_get(np, 0); - if (IS_ERR(clk)) - return PTR_ERR(clk); - - dai_props->sysclk = clk_get_rate(clk); - dai_props->clk = clk; - } else if (!of_property_read_u32(np, "system-clock-frequency", &val)) { - dai_props->sysclk = val; - } else { - clk = of_clk_get(of_np, 0); - if (!IS_ERR(clk)) - dai_props->sysclk = clk_get_rate(clk); - } - - return 0; -} - -static int rsrc_card_dai_sub_link_of(struct device_node *node, - struct device_node *np, - struct rsrc_card_priv *priv, - int idx, bool is_fe) -{ - struct device *dev = rsrc_priv_to_dev(priv); - struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx); - struct asoc_simple_dai *dai_props = rsrc_priv_to_props(priv, idx); - int ret; - - ret = rsrc_card_parse_links(np, priv, idx, is_fe); - if (ret < 0) - return ret; - - ret = rsrc_card_parse_clk(np, priv, idx, is_fe); - if (ret < 0) - return ret; - - dev_dbg(dev, "\t%s / %04x / %d\n", - dai_link->name, - dai_link->dai_fmt, - dai_props->sysclk); - - return ret; -} - -static int rsrc_card_dai_link_of(struct device_node *node, - struct rsrc_card_priv *priv) -{ - struct device *dev = rsrc_priv_to_dev(priv); - struct snd_soc_dai_link *dai_link; - struct device_node *np; - unsigned int daifmt = 0; - int ret, i; - bool is_fe; - - /* find 1st codec */ - i = 0; - for_each_child_of_node(node, np) { - dai_link = rsrc_priv_to_link(priv, i); - - if (strcmp(np->name, "codec") == 0) { - ret = asoc_simple_card_parse_daifmt(dev, node, np, - NULL, &daifmt); - if (ret < 0) - return ret; - break; - } - i++; - } - - i = 0; - for_each_child_of_node(node, np) { - dai_link = rsrc_priv_to_link(priv, i); - dai_link->dai_fmt = daifmt; - - is_fe = false; - if (strcmp(np->name, "cpu") == 0) - is_fe = true; - - ret = rsrc_card_dai_sub_link_of(node, np, priv, i, is_fe); - if (ret < 0) - return ret; - i++; - } - - return 0; -} - -static int rsrc_card_parse_of(struct device_node *node, - struct rsrc_card_priv *priv, - struct device *dev) -{ - const struct rsrc_card_of_data *of_data = of_device_get_match_data(dev); - struct asoc_simple_dai *props; - struct snd_soc_dai_link *links; - int ret; - int num; - - if (!node) - return -EINVAL; - - num = of_get_child_count(node); - props = devm_kzalloc(dev, sizeof(*props) * num, GFP_KERNEL); - links = devm_kzalloc(dev, sizeof(*links) * num, GFP_KERNEL); - if (!props || !links) - return -ENOMEM; - - priv->dai_props = props; - priv->dai_link = links; - - /* Init snd_soc_card */ - priv->snd_card.owner = THIS_MODULE; - priv->snd_card.dev = dev; - priv->snd_card.dai_link = priv->dai_link; - priv->snd_card.num_links = num; - priv->snd_card.codec_conf = &priv->codec_conf; - priv->snd_card.num_configs = 1; - - if (of_data) { - priv->snd_card.of_dapm_routes = of_data->routes; - priv->snd_card.num_of_dapm_routes = of_data->num_routes; - } else { - snd_soc_of_parse_audio_routing(&priv->snd_card, - "audio-routing"); - } - - /* sampling rate convert */ - of_property_read_u32(node, "convert-rate", &priv->convert_rate); - - /* channels transfer */ - of_property_read_u32(node, "convert-channels", &priv->convert_channels); - - dev_dbg(dev, "New rsrc-audio-card: %s\n", - priv->snd_card.name ? priv->snd_card.name : ""); - dev_dbg(dev, "SRC : convert_rate %d\n", priv->convert_rate); - dev_dbg(dev, "CTU : convert_channels %d\n", priv->convert_channels); - - ret = rsrc_card_dai_link_of(node, priv); - if (ret < 0) - return ret; - - ret = asoc_simple_card_parse_card_name(&priv->snd_card, "card-"); - if (ret < 0) - return ret; - - return 0; -} - -/* Decrease the reference count of the device nodes */ -static int rsrc_card_unref(struct snd_soc_card *card) -{ - struct snd_soc_dai_link *dai_link; - int num_links; - - for (num_links = 0, dai_link = card->dai_link; - num_links < card->num_links; - num_links++, dai_link++) { - of_node_put(dai_link->cpu_of_node); - of_node_put(dai_link->codec_of_node); - } - return 0; -} - -static int rsrc_card_probe(struct platform_device *pdev) -{ - struct rsrc_card_priv *priv; - struct device_node *np = pdev->dev.of_node; - struct device *dev = &pdev->dev; - int ret; - - /* Allocate the private data */ - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - ret = rsrc_card_parse_of(np, priv, dev); - if (ret < 0) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "parse error %d\n", ret); - goto err; - } - - snd_soc_card_set_drvdata(&priv->snd_card, priv); - - ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card); - if (ret >= 0) - return ret; -err: - rsrc_card_unref(&priv->snd_card); - - return ret; -} - -static int rsrc_card_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - return rsrc_card_unref(card); -} - -static struct platform_driver rsrc_card = { - .driver = { - .name = "renesas-src-audio-card", - .of_match_table = rsrc_card_of_match, - }, - .probe = rsrc_card_probe, - .remove = rsrc_card_remove, -}; - -module_platform_driver(rsrc_card); - -MODULE_ALIAS("platform:renesas-src-audio-card"); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Renesas Sampling Rate Convert Sound Card"); -MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 969a5169de25..3a8f65bd1bf9 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -189,6 +189,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + int use_src = 0; u32 fin, fout; u32 ifscr, fsrate, adinr; u32 cr, route; @@ -214,6 +215,8 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, return; } + use_src = (fin != fout) | rsnd_src_sync_is_enabled(mod); + /* * SRC_ADINR */ @@ -225,7 +228,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, */ ifscr = 0; fsrate = 0; - if (fin != fout) { + if (use_src) { u64 n; ifscr = 1; @@ -239,7 +242,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, */ cr = 0x00011110; route = 0x0; - if (fin != fout) { + if (use_src) { route = 0x1; if (rsnd_src_sync_is_enabled(mod)) { @@ -327,8 +330,8 @@ static void rsnd_src_status_clear(struct rsnd_mod *mod) { u32 val = OUF_SRC(rsnd_mod_id(mod)); - rsnd_mod_bset(mod, SCU_SYS_STATUS0, val, val); - rsnd_mod_bset(mod, SCU_SYS_STATUS1, val, val); + rsnd_mod_write(mod, SCU_SYS_STATUS0, val); + rsnd_mod_write(mod, SCU_SYS_STATUS1, val); } static bool rsnd_src_error_occurred(struct rsnd_mod *mod) @@ -475,7 +478,7 @@ static int rsnd_src_probe_(struct rsnd_mod *mod, return ret; } - ret = rsnd_dma_attach(io, mod, &src->dma, 0); + ret = rsnd_dma_attach(io, mod, &src->dma); return ret; } diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 5f848f054745..411bda2387ad 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -417,11 +417,14 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod, int chan = params_channels(params); /* - * Already working. - * It will happen if SSI has parent/child connection. + * snd_pcm_ops::hw_params will be called *before* + * snd_soc_dai_ops::trigger. Thus, ssi->usrcnt is 0 + * in 1st call. */ - if (ssi->usrcnt > 1) { + if (ssi->usrcnt) { /* + * Already working. + * It will happen if SSI has parent/child connection. * it is error if child <-> parent SSI uses * different channels. */ @@ -644,10 +647,14 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod, if (ret < 0) return ret; - ret = devm_request_irq(dev, ssi->irq, - rsnd_ssi_interrupt, - IRQF_SHARED, - dev_name(dev), mod); + /* + * SSI might be called again as PIO fallback + * It is easy to manual handling for IRQ request/free + */ + ret = request_irq(ssi->irq, + rsnd_ssi_interrupt, + IRQF_SHARED, + dev_name(dev), mod); return ret; } @@ -669,7 +676,6 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - int dma_id = 0; /* not needed */ int ret; /* @@ -684,7 +690,7 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, return ret; /* SSI probe might be called many times in MUX multi path */ - ret = rsnd_dma_attach(io, mod, &ssi->dma, dma_id); + ret = rsnd_dma_attach(io, mod, &ssi->dma); return ret; } @@ -694,11 +700,9 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - struct device *dev = rsnd_priv_to_dev(priv); - int irq = ssi->irq; /* PIO will request IRQ again */ - devm_free_irq(dev, irq, mod); + free_irq(ssi->irq, mod); return 0; } @@ -928,7 +932,7 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) } ops = &rsnd_ssi_non_ops; - if (of_get_property(np, "pio-transfer", NULL)) + if (of_property_read_bool(np, "pio-transfer")) ops = &rsnd_ssi_pio_ops; else ops = &rsnd_ssi_dma_ops; diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 6f9b388ec5a8..4e817c8a18c0 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -33,6 +33,26 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod, u32 mask1, val1; u32 mask2, val2; + /* clear status */ + switch (id) { + case 0: + case 1: + case 2: + case 3: + case 4: + rsnd_mod_write(mod, SSI_SYS_STATUS0, 0xf << (id * 4)); + rsnd_mod_write(mod, SSI_SYS_STATUS2, 0xf << (id * 4)); + rsnd_mod_write(mod, SSI_SYS_STATUS4, 0xf << (id * 4)); + rsnd_mod_write(mod, SSI_SYS_STATUS6, 0xf << (id * 4)); + break; + case 9: + rsnd_mod_write(mod, SSI_SYS_STATUS1, 0xf << 4); + rsnd_mod_write(mod, SSI_SYS_STATUS3, 0xf << 4); + rsnd_mod_write(mod, SSI_SYS_STATUS5, 0xf << 4); + rsnd_mod_write(mod, SSI_SYS_STATUS7, 0xf << 4); + break; + } + /* * SSI_MODE0 */ diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c index bc4a55bb3fd9..6c8b0b0c56ec 100644 --- a/sound/soc/soc-ac97.c +++ b/sound/soc/soc-ac97.c @@ -116,7 +116,7 @@ static int snd_soc_ac97_gpio_direction_out(struct gpio_chip *chip, return snd_soc_update_bits(codec, AC97_GPIO_CFG, 1 << offset, 0); } -static struct gpio_chip snd_soc_ac97_gpio_chip = { +static const struct gpio_chip snd_soc_ac97_gpio_chip = { .label = "snd_soc_ac97", .owner = THIS_MODULE, .request = snd_soc_ac97_gpio_request, diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index bf7b52fce597..bfd71b873ca2 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -30,16 +30,26 @@ static int soc_compr_open(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; int ret = 0; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (cpu_dai->driver->cops && cpu_dai->driver->cops->startup) { + ret = cpu_dai->driver->cops->startup(cstream, cpu_dai); + if (ret < 0) { + dev_err(cpu_dai->dev, "Compress ASoC: can't open interface %s: %d\n", + cpu_dai->name, ret); + goto out; + } + } + if (platform->driver->compr_ops && platform->driver->compr_ops->open) { ret = platform->driver->compr_ops->open(cstream); if (ret < 0) { pr_err("compress asoc: can't open platform %s\n", platform->component.name); - goto out; + goto plat_err; } } @@ -60,6 +70,9 @@ static int soc_compr_open(struct snd_compr_stream *cstream) machine_err: if (platform->driver->compr_ops && platform->driver->compr_ops->free) platform->driver->compr_ops->free(cstream); +plat_err: + if (cpu_dai->driver->cops && cpu_dai->driver->cops->shutdown) + cpu_dai->driver->cops->shutdown(cstream, cpu_dai); out: mutex_unlock(&rtd->pcm_mutex); return ret; @@ -70,6 +83,7 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) struct snd_soc_pcm_runtime *fe = cstream->private_data; struct snd_pcm_substream *fe_substream = fe->pcm->streams[0].substream; struct snd_soc_platform *platform = fe->platform; + struct snd_soc_dai *cpu_dai = fe->cpu_dai; struct snd_soc_dpcm *dpcm; struct snd_soc_dapm_widget_list *list; int stream; @@ -82,12 +96,22 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + if (cpu_dai->driver->cops && cpu_dai->driver->cops->startup) { + ret = cpu_dai->driver->cops->startup(cstream, cpu_dai); + if (ret < 0) { + dev_err(cpu_dai->dev, "Compress ASoC: can't open interface %s: %d\n", + cpu_dai->name, ret); + goto out; + } + } + + if (platform->driver->compr_ops && platform->driver->compr_ops->open) { ret = platform->driver->compr_ops->open(cstream); if (ret < 0) { pr_err("compress asoc: can't open platform %s\n", platform->component.name); - goto out; + goto plat_err; } } @@ -144,6 +168,9 @@ fe_err: machine_err: if (platform->driver->compr_ops && platform->driver->compr_ops->free) platform->driver->compr_ops->free(cstream); +plat_err: + if (cpu_dai->driver->cops && cpu_dai->driver->cops->shutdown) + cpu_dai->driver->cops->shutdown(cstream, cpu_dai); out: fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; mutex_unlock(&fe->card->mutex); @@ -210,6 +237,9 @@ static int soc_compr_free(struct snd_compr_stream *cstream) if (platform->driver->compr_ops && platform->driver->compr_ops->free) platform->driver->compr_ops->free(cstream); + if (cpu_dai->driver->cops && cpu_dai->driver->cops->shutdown) + cpu_dai->driver->cops->shutdown(cstream, cpu_dai); + if (cstream->direction == SND_COMPRESS_PLAYBACK) { if (snd_soc_runtime_ignore_pmdown_time(rtd)) { snd_soc_dapm_stream_event(rtd, @@ -236,6 +266,7 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *fe = cstream->private_data; struct snd_soc_platform *platform = fe->platform; + struct snd_soc_dai *cpu_dai = fe->cpu_dai; struct snd_soc_dpcm *dpcm; int stream, ret; @@ -275,6 +306,9 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) if (platform->driver->compr_ops && platform->driver->compr_ops->free) platform->driver->compr_ops->free(cstream); + if (cpu_dai->driver->cops && cpu_dai->driver->cops->shutdown) + cpu_dai->driver->cops->shutdown(cstream, cpu_dai); + mutex_unlock(&fe->card->mutex); return 0; } @@ -285,6 +319,7 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; int ret = 0; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); @@ -295,6 +330,10 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) goto out; } + if (cpu_dai->driver->cops && cpu_dai->driver->cops->trigger) + cpu_dai->driver->cops->trigger(cstream, cmd, cpu_dai); + + switch (cmd) { case SNDRV_PCM_TRIGGER_START: snd_soc_dai_digital_mute(codec_dai, 0, cstream->direction); @@ -313,6 +352,7 @@ static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd) { struct snd_soc_pcm_runtime *fe = cstream->private_data; struct snd_soc_platform *platform = fe->platform; + struct snd_soc_dai *cpu_dai = fe->cpu_dai; int ret = 0, stream; if (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN || @@ -332,6 +372,12 @@ static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd) mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + if (cpu_dai->driver->cops && cpu_dai->driver->cops->trigger) { + ret = cpu_dai->driver->cops->trigger(cstream, cmd, cpu_dai); + if (ret < 0) + goto out; + } + if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) { ret = platform->driver->compr_ops->trigger(cstream, cmd); if (ret < 0) @@ -368,6 +414,7 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; int ret = 0; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); @@ -378,6 +425,12 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, * expectation is that platform and machine will configure everything * for this compress path, like configuring pcm port for codec */ + if (cpu_dai->driver->cops && cpu_dai->driver->cops->set_params) { + ret = cpu_dai->driver->cops->set_params(cstream, params, cpu_dai); + if (ret < 0) + goto err; + } + if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) { ret = platform->driver->compr_ops->set_params(cstream, params); if (ret < 0) @@ -416,6 +469,7 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, struct snd_soc_pcm_runtime *fe = cstream->private_data; struct snd_pcm_substream *fe_substream = fe->pcm->streams[0].substream; struct snd_soc_platform *platform = fe->platform; + struct snd_soc_dai *cpu_dai = fe->cpu_dai; int ret = 0, stream; if (cstream->direction == SND_COMPRESS_PLAYBACK) @@ -425,6 +479,12 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + if (cpu_dai->driver->cops && cpu_dai->driver->cops->set_params) { + ret = cpu_dai->driver->cops->set_params(cstream, params, cpu_dai); + if (ret < 0) + goto out; + } + if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) { ret = platform->driver->compr_ops->set_params(cstream, params); if (ret < 0) @@ -469,13 +529,21 @@ static int soc_compr_get_params(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; int ret = 0; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (cpu_dai->driver->cops && cpu_dai->driver->cops->get_params) { + ret = cpu_dai->driver->cops->get_params(cstream, params, cpu_dai); + if (ret < 0) + goto err; + } + if (platform->driver->compr_ops && platform->driver->compr_ops->get_params) ret = platform->driver->compr_ops->get_params(cstream, params); +err: mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -516,13 +584,21 @@ static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; int ret = 0; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (cpu_dai->driver->cops && cpu_dai->driver->cops->ack) { + ret = cpu_dai->driver->cops->ack(cstream, bytes, cpu_dai); + if (ret < 0) + goto err; + } + if (platform->driver->compr_ops && platform->driver->compr_ops->ack) ret = platform->driver->compr_ops->ack(cstream, bytes); +err: mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -533,9 +609,13 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream, struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; int ret = 0; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (cpu_dai->driver->cops && cpu_dai->driver->cops->pointer) + cpu_dai->driver->cops->pointer(cstream, tstamp, cpu_dai); + if (platform->driver->compr_ops && platform->driver->compr_ops->pointer) ret = platform->driver->compr_ops->pointer(cstream, tstamp); @@ -564,8 +644,15 @@ static int soc_compr_set_metadata(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; int ret = 0; + if (cpu_dai->driver->cops && cpu_dai->driver->cops->set_metadata) { + ret = cpu_dai->driver->cops->set_metadata(cstream, metadata, cpu_dai); + if (ret < 0) + return ret; + } + if (platform->driver->compr_ops && platform->driver->compr_ops->set_metadata) ret = platform->driver->compr_ops->set_metadata(cstream, metadata); @@ -577,8 +664,15 @@ static int soc_compr_get_metadata(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; int ret = 0; + if (cpu_dai->driver->cops && cpu_dai->driver->cops->get_metadata) { + ret = cpu_dai->driver->cops->get_metadata(cstream, metadata, cpu_dai); + if (ret < 0) + return ret; + } + if (platform->driver->compr_ops && platform->driver->compr_ops->get_metadata) ret = platform->driver->compr_ops->get_metadata(cstream, metadata); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4afa8dba5e98..baa1afa41e3d 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -626,7 +626,7 @@ static void codec2codec_close_delayed_work(struct work_struct *work) int snd_soc_suspend(struct device *dev) { struct snd_soc_card *card = dev_get_drvdata(dev); - struct snd_soc_codec *codec; + struct snd_soc_component *component; struct snd_soc_pcm_runtime *rtd; int i; @@ -702,39 +702,39 @@ int snd_soc_suspend(struct device *dev) dapm_mark_endpoints_dirty(card); snd_soc_dapm_sync(&card->dapm); - /* suspend all CODECs */ - list_for_each_entry(codec, &card->codec_dev_list, card_list) { - struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + /* suspend all COMPONENTs */ + list_for_each_entry(component, &card->component_dev_list, card_list) { + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); - /* If there are paths active then the CODEC will be held with + /* If there are paths active then the COMPONENT will be held with * bias _ON and should not be suspended. */ - if (!codec->suspended) { + if (!component->suspended) { switch (snd_soc_dapm_get_bias_level(dapm)) { case SND_SOC_BIAS_STANDBY: /* - * If the CODEC is capable of idle + * If the COMPONENT is capable of idle * bias off then being in STANDBY * means it's doing something, * otherwise fall through. */ if (dapm->idle_bias_off) { - dev_dbg(codec->dev, + dev_dbg(component->dev, "ASoC: idle_bias_off CODEC on over suspend\n"); break; } case SND_SOC_BIAS_OFF: - if (codec->driver->suspend) - codec->driver->suspend(codec); - codec->suspended = 1; - if (codec->component.regmap) - regcache_mark_dirty(codec->component.regmap); + if (component->suspend) + component->suspend(component); + component->suspended = 1; + if (component->regmap) + regcache_mark_dirty(component->regmap); /* deactivate pins to sleep state */ - pinctrl_pm_select_sleep_state(codec->dev); + pinctrl_pm_select_sleep_state(component->dev); break; default: - dev_dbg(codec->dev, - "ASoC: CODEC is on over suspend\n"); + dev_dbg(component->dev, + "ASoC: COMPONENT is on over suspend\n"); break; } } @@ -768,7 +768,7 @@ static void soc_resume_deferred(struct work_struct *work) struct snd_soc_card *card = container_of(work, struct snd_soc_card, deferred_resume_work); struct snd_soc_pcm_runtime *rtd; - struct snd_soc_codec *codec; + struct snd_soc_component *component; int i; /* our power state is still SNDRV_CTL_POWER_D3hot from suspend time, @@ -794,11 +794,11 @@ static void soc_resume_deferred(struct work_struct *work) cpu_dai->driver->resume(cpu_dai); } - list_for_each_entry(codec, &card->codec_dev_list, card_list) { - if (codec->suspended) { - if (codec->driver->resume) - codec->driver->resume(codec); - codec->suspended = 0; + list_for_each_entry(component, &card->component_dev_list, card_list) { + if (component->suspended) { + if (component->resume) + component->resume(component); + component->suspended = 0; } } @@ -972,6 +972,48 @@ struct snd_soc_dai *snd_soc_find_dai( } EXPORT_SYMBOL_GPL(snd_soc_find_dai); + +/** + * snd_soc_find_dai_link - Find a DAI link + * + * @card: soc card + * @id: DAI link ID to match + * @name: DAI link name to match, optional + * @stream name: DAI link stream name to match, optional + * + * This function will search all existing DAI links of the soc card to + * find the link of the same ID. Since DAI links may not have their + * unique ID, so name and stream name should also match if being + * specified. + * + * Return: pointer of DAI link, or NULL if not found. + */ +struct snd_soc_dai_link *snd_soc_find_dai_link(struct snd_soc_card *card, + int id, const char *name, + const char *stream_name) +{ + struct snd_soc_dai_link *link, *_link; + + lockdep_assert_held(&client_mutex); + + list_for_each_entry_safe(link, _link, &card->dai_link_list, list) { + if (link->id != id) + continue; + + if (name && (!link->name || strcmp(name, link->name))) + continue; + + if (stream_name && (!link->stream_name + || strcmp(stream_name, link->stream_name))) + continue; + + return link; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(snd_soc_find_dai_link); + static bool soc_is_dai_link_bound(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) { @@ -993,6 +1035,7 @@ static int soc_bind_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link_component cpu_dai_component; struct snd_soc_dai **codec_dais; struct snd_soc_platform *platform; + struct device_node *platform_of_node; const char *platform_name; int i; @@ -1042,9 +1085,12 @@ static int soc_bind_dai_link(struct snd_soc_card *card, /* find one from the set of registered platforms */ list_for_each_entry(platform, &platform_list, list) { + platform_of_node = platform->dev->of_node; + if (!platform_of_node && platform->dev->parent->of_node) + platform_of_node = platform->dev->parent->of_node; + if (dai_link->platform_of_node) { - if (platform->dev->of_node != - dai_link->platform_of_node) + if (platform_of_node != dai_link->platform_of_node) continue; } else { if (strcmp(platform->component.name, platform_name)) @@ -1072,9 +1118,7 @@ static void soc_remove_component(struct snd_soc_component *component) if (!component->card) return; - /* This is a HACK and will be removed soon */ - if (component->codec) - list_del(&component->codec->card_list); + list_del(&component->card_list); if (component->remove) component->remove(component); @@ -1443,10 +1487,7 @@ static int soc_probe_component(struct snd_soc_card *card, component->num_dapm_routes); list_add(&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); + list_add(&component->card_list, &card->component_dev_list); return 0; @@ -1706,7 +1747,9 @@ static int soc_bind_aux_dev(struct snd_soc_card *card, int num) } component->init = aux_dev->init; - list_add(&component->list_aux, &card->aux_comp_list); + component->auxiliary = 1; + list_add(&component->card_aux_list, &card->aux_comp_list); + return 0; err_defer: @@ -1716,13 +1759,14 @@ err_defer: static int soc_probe_aux_devices(struct snd_soc_card *card) { - struct snd_soc_component *comp; + struct snd_soc_component *comp, *tmp; int order; int ret; for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; order++) { - list_for_each_entry(comp, &card->aux_comp_list, list_aux) { + list_for_each_entry_safe(comp, tmp, &card->aux_comp_list, + card_aux_list) { if (comp->driver->probe_order == order) { ret = soc_probe_component(card, comp); if (ret < 0) { @@ -1731,6 +1775,7 @@ static int soc_probe_aux_devices(struct snd_soc_card *card) comp->name, ret); return ret; } + list_del(&comp->card_aux_list); } } } @@ -1746,11 +1791,14 @@ static void soc_remove_aux_devices(struct snd_soc_card *card) for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; order++) { list_for_each_entry_safe(comp, _comp, - &card->aux_comp_list, list_aux) { + &card->component_dev_list, card_list) { + + if (!comp->auxiliary) + continue; + if (comp->driver->remove_order == order) { soc_remove_component(comp); - /* remove it from the card's aux_comp_list */ - list_del(&comp->list_aux); + comp->auxiliary = 0; } } } @@ -2926,6 +2974,8 @@ static int snd_soc_component_initialize(struct snd_soc_component *component, component->driver = driver; component->probe = component->driver->probe; component->remove = component->driver->remove; + component->suspend = component->driver->suspend; + component->resume = component->driver->resume; dapm = &component->dapm; dapm->dev = dev; @@ -3275,6 +3325,20 @@ static void snd_soc_codec_drv_remove(struct snd_soc_component *component) codec->driver->remove(codec); } +static int snd_soc_codec_drv_suspend(struct snd_soc_component *component) +{ + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + + return codec->driver->suspend(codec); +} + +static int snd_soc_codec_drv_resume(struct snd_soc_component *component) +{ + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + + return codec->driver->resume(codec); +} + static int snd_soc_codec_drv_write(struct snd_soc_component *component, unsigned int reg, unsigned int val) { @@ -3332,23 +3396,14 @@ int snd_soc_register_codec(struct device *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->suspend) + codec->component.suspend = snd_soc_codec_drv_suspend; + if (codec_drv->resume) + codec->component.resume = snd_soc_codec_drv_resume; if (codec_drv->write) codec->component.write = snd_soc_codec_drv_write; if (codec_drv->read) @@ -3437,10 +3492,10 @@ found: EXPORT_SYMBOL_GPL(snd_soc_unregister_codec); /* Retrieve a card's name from device tree */ -int snd_soc_of_parse_card_name(struct snd_soc_card *card, - const char *propname) +int snd_soc_of_parse_card_name_from_node(struct snd_soc_card *card, + struct device_node *np, + const char *propname) { - struct device_node *np; int ret; if (!card->dev) { @@ -3448,7 +3503,8 @@ int snd_soc_of_parse_card_name(struct snd_soc_card *card, return -EINVAL; } - np = card->dev->of_node; + if (!np) + np = card->dev->of_node; ret = of_property_read_string_index(np, propname, 0, &card->name); /* @@ -3465,7 +3521,7 @@ int snd_soc_of_parse_card_name(struct snd_soc_card *card, return 0; } -EXPORT_SYMBOL_GPL(snd_soc_of_parse_card_name); +EXPORT_SYMBOL_GPL(snd_soc_of_parse_card_name_from_node); static const struct snd_soc_dapm_widget simple_widgets[] = { SND_SOC_DAPM_MIC("Microphone", NULL), @@ -3474,14 +3530,17 @@ static const struct snd_soc_dapm_widget simple_widgets[] = { SND_SOC_DAPM_SPK("Speaker", NULL), }; -int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, +int snd_soc_of_parse_audio_simple_widgets_from_node(struct snd_soc_card *card, + struct device_node *np, const char *propname) { - struct device_node *np = card->dev->of_node; struct snd_soc_dapm_widget *widgets; const char *template, *wname; int i, j, num_widgets, ret; + if (!np) + np = card->dev->of_node; + num_widgets = of_property_count_strings(np, propname); if (num_widgets < 0) { dev_err(card->dev, @@ -3552,7 +3611,7 @@ int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, return 0; } -EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets); +EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets_from_node); static int snd_soc_of_get_slot_mask(struct device_node *np, const char *prop_name, @@ -3608,15 +3667,18 @@ int snd_soc_of_parse_tdm_slot(struct device_node *np, } EXPORT_SYMBOL_GPL(snd_soc_of_parse_tdm_slot); -void snd_soc_of_parse_audio_prefix(struct snd_soc_card *card, +void snd_soc_of_parse_audio_prefix_from_node(struct snd_soc_card *card, + struct device_node *np, struct snd_soc_codec_conf *codec_conf, struct device_node *of_node, const char *propname) { - struct device_node *np = card->dev->of_node; const char *str; int ret; + if (!np) + np = card->dev->of_node; + ret = of_property_read_string(np, propname, &str); if (ret < 0) { /* no prefix is not error */ @@ -3626,16 +3688,19 @@ void snd_soc_of_parse_audio_prefix(struct snd_soc_card *card, codec_conf->of_node = of_node; codec_conf->name_prefix = str; } -EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_prefix); +EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_prefix_from_node); -int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, +int snd_soc_of_parse_audio_routing_from_node(struct snd_soc_card *card, + struct device_node *np, const char *propname) { - struct device_node *np = card->dev->of_node; int num_routes; struct snd_soc_dapm_route *routes; int i, ret; + if (!np) + np = card->dev->of_node; + num_routes = of_property_count_strings(np, propname); if (num_routes < 0 || num_routes & 1) { dev_err(card->dev, @@ -3682,7 +3747,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, return 0; } -EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_routing); +EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_routing_from_node); unsigned int snd_soc_of_parse_daifmt(struct device_node *np, const char *prefix, @@ -3732,7 +3797,7 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np, * SND_SOC_DAIFMT_CLOCK_MASK area */ snprintf(prop, sizeof(prop), "%scontinuous-clock", prefix); - if (of_get_property(np, prop, NULL)) + if (of_property_read_bool(np, prop)) format |= SND_SOC_DAIFMT_CONT; else format |= SND_SOC_DAIFMT_GATED; @@ -3797,7 +3862,7 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np, } EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt); -static int snd_soc_get_dai_name(struct of_phandle_args *args, +int snd_soc_get_dai_name(struct of_phandle_args *args, const char **dai_name) { struct snd_soc_component *pos; @@ -3849,6 +3914,7 @@ static int snd_soc_get_dai_name(struct of_phandle_args *args, mutex_unlock(&client_mutex); return ret; } +EXPORT_SYMBOL_GPL(snd_soc_get_dai_name); int snd_soc_of_get_dai_name(struct device_node *of_node, const char **dai_name) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index d908ff8f9755..27dd02e57b31 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -330,6 +330,11 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, case snd_soc_dapm_mixer_named_ctl: mc = (struct soc_mixer_control *)kcontrol->private_value; + if (mc->autodisable && snd_soc_volsw_is_stereo(mc)) + dev_warn(widget->dapm->dev, + "ASoC: Unsupported stereo autodisable control '%s'\n", + ctrl_name); + if (mc->autodisable) { struct snd_soc_dapm_widget template; @@ -723,7 +728,8 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, } /* set up initial codec paths */ -static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i) +static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i, + int nth_path) { struct soc_mixer_control *mc = (struct soc_mixer_control *) p->sink->kcontrol_news[i].private_value; @@ -736,7 +742,25 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i) if (reg != SND_SOC_NOPM) { soc_dapm_read(p->sink->dapm, reg, &val); - val = (val >> shift) & mask; + /* + * The nth_path argument allows this function to know + * which path of a kcontrol it is setting the initial + * status for. Ideally this would support any number + * of paths and channels. But since kcontrols only come + * in mono and stereo variants, we are limited to 2 + * channels. + * + * The following code assumes for stereo controls the + * first path is the left channel, and all remaining + * paths are the right channel. + */ + if (snd_soc_volsw_is_stereo(mc) && nth_path > 0) { + if (reg != mc->rreg) + soc_dapm_read(p->sink->dapm, mc->rreg, &val); + val = (val >> mc->rshift) & mask; + } else { + val = (val >> shift) & mask; + } if (invert) val = max - val; p->connect = !!val; @@ -749,13 +773,13 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i) static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_path *path, const char *control_name) { - int i; + int i, nth_path = 0; /* search for mixer kcontrol */ for (i = 0; i < path->sink->num_kcontrols; i++) { if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) { path->name = path->sink->kcontrol_news[i].name; - dapm_set_mixer_path_status(path, i); + dapm_set_mixer_path_status(path, i, nth_path++); return 0; } } @@ -823,6 +847,7 @@ static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w, case snd_soc_dapm_switch: case snd_soc_dapm_mixer: case snd_soc_dapm_pga: + case snd_soc_dapm_out_drv: wname_in_long_name = true; kcname_in_long_name = true; break; @@ -1169,7 +1194,7 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, * @custom_stop_condition: (optional) a function meant to stop the widget graph * walk based on custom logic. * - * Queries DAPM graph as to whether an valid audio stream path exists for + * Queries DAPM graph as to whether a valid audio stream path exists for * the initial stream specified by name. This takes into account * current mixer and mux kcontrol settings. Creates list of valid widgets. * @@ -1294,8 +1319,7 @@ static int dapm_widget_power_check(struct snd_soc_dapm_widget *w) return w->new_power; } -/* Generic check to see if a widget should be powered. - */ +/* Generic check to see if a widget should be powered. */ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w) { int in, out; @@ -1626,6 +1650,15 @@ static void dapm_widget_update(struct snd_soc_card *card) dev_err(w->dapm->dev, "ASoC: %s DAPM update failed: %d\n", w->name, ret); + if (update->has_second_set) { + ret = soc_dapm_update_bits(w->dapm, update->reg2, + update->mask2, update->val2); + if (ret < 0) + dev_err(w->dapm->dev, + "ASoC: %s DAPM update failed: %d\n", + w->name, ret); + } + for (wi = 0; wi < wlist->num_widgets; wi++) { w = wlist->widgets[wi]; @@ -1646,7 +1679,7 @@ static void dapm_pre_sequence_async(void *data, async_cookie_t cookie) struct snd_soc_dapm_context *d = data; int ret; - /* If we're off and we're not supposed to be go into STANDBY */ + /* If we're off and we're not supposed to go into STANDBY */ if (d->bias_level == SND_SOC_BIAS_OFF && d->target_bias_level != SND_SOC_BIAS_OFF) { if (d->dev) @@ -1798,7 +1831,7 @@ static bool dapm_idle_bias_off(struct snd_soc_dapm_context *dapm) * A complete path is a route that has valid endpoints i.e.:- * * o DAC to output pin. - * o Input Pin to ADC. + * o Input pin to ADC. * o Input pin to Output pin (bypass, sidetone) * o DAC to ADC (loopback). */ @@ -2114,7 +2147,7 @@ static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm) * soc_dapm_connect_path() - Connects or disconnects a path * @path: The path to update * @connect: The new connect state of the path. True if the path is connected, - * false if it is disconneted. + * false if it is disconnected. * @reason: The reason why the path changed (for debugging only) */ static void soc_dapm_connect_path(struct snd_soc_dapm_path *path, @@ -2177,7 +2210,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power); /* test and update the power status of a mixer or switch widget */ static int soc_dapm_mixer_update_power(struct snd_soc_card *card, - struct snd_kcontrol *kcontrol, int connect) + struct snd_kcontrol *kcontrol, + int connect, int rconnect) { struct snd_soc_dapm_path *path; int found = 0; @@ -2186,8 +2220,33 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card, /* find dapm widget path assoc with kcontrol */ dapm_kcontrol_for_each_path(path, kcontrol) { + /* + * Ideally this function should support any number of + * paths and channels. But since kcontrols only come + * in mono and stereo variants, we are limited to 2 + * channels. + * + * The following code assumes for stereo controls the + * first path (when 'found == 0') is the left channel, + * and all remaining paths (when 'found == 1') are the + * right channel. + * + * A stereo control is signified by a valid 'rconnect' + * value, either 0 for unconnected, or >= 0 for connected. + * This is chosen instead of using snd_soc_volsw_is_stereo, + * so that the behavior of snd_soc_dapm_mixer_update_power + * doesn't change even when the kcontrol passed in is + * stereo. + * + * It passes 'connect' as the path connect status for + * the left channel, and 'rconnect' for the right + * channel. + */ + if (found && rconnect >= 0) + soc_dapm_connect_path(path, rconnect, "mixer update"); + else + soc_dapm_connect_path(path, connect, "mixer update"); found = 1; - soc_dapm_connect_path(path, connect, "mixer update"); } if (found) @@ -2205,7 +2264,7 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); card->update = update; - ret = soc_dapm_mixer_update_power(card, kcontrol, connect); + ret = soc_dapm_mixer_update_power(card, kcontrol, connect, -1); card->update = NULL; mutex_unlock(&card->dapm_mutex); if (ret > 0) @@ -2233,7 +2292,7 @@ static ssize_t dapm_widget_show_component(struct snd_soc_component *cmpnt, if (w->dapm != dapm) continue; - /* only display widgets that burnm power */ + /* only display widgets that burn power */ switch (w->id) { case snd_soc_dapm_hp: case snd_soc_dapm_mic: @@ -2461,7 +2520,7 @@ static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w) switch (w->id) { case snd_soc_dapm_input: - /* On a fully routed card a input is never a source */ + /* On a fully routed card an input is never a source */ if (w->dapm->card->fully_routed) return; ep = SND_SOC_DAPM_EP_SOURCE; @@ -3030,30 +3089,46 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, int reg = mc->reg; unsigned int shift = mc->shift; int max = mc->max; + unsigned int width = fls(max); unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; - unsigned int val; + unsigned int reg_val, val, rval = 0; int ret = 0; - if (snd_soc_volsw_is_stereo(mc)) - dev_warn(dapm->dev, - "ASoC: Control '%s' is stereo, which is not supported\n", - kcontrol->id.name); - mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); if (dapm_kcontrol_is_powered(kcontrol) && reg != SND_SOC_NOPM) { - ret = soc_dapm_read(dapm, reg, &val); - val = (val >> shift) & mask; + ret = soc_dapm_read(dapm, reg, ®_val); + val = (reg_val >> shift) & mask; + + if (ret == 0 && reg != mc->rreg) + ret = soc_dapm_read(dapm, mc->rreg, ®_val); + + if (snd_soc_volsw_is_stereo(mc)) + rval = (reg_val >> mc->rshift) & mask; } else { - val = dapm_kcontrol_get_value(kcontrol); + reg_val = dapm_kcontrol_get_value(kcontrol); + val = reg_val & mask; + + if (snd_soc_volsw_is_stereo(mc)) + rval = (reg_val >> width) & mask; } mutex_unlock(&card->dapm_mutex); + if (ret) + return ret; + if (invert) ucontrol->value.integer.value[0] = max - val; else ucontrol->value.integer.value[0] = val; + if (snd_soc_volsw_is_stereo(mc)) { + if (invert) + ucontrol->value.integer.value[1] = max - rval; + else + ucontrol->value.integer.value[1] = rval; + } + return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw); @@ -3077,46 +3152,66 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, int reg = mc->reg; unsigned int shift = mc->shift; int max = mc->max; - unsigned int mask = (1 << fls(max)) - 1; + unsigned int width = fls(max); + unsigned int mask = (1 << width) - 1; unsigned int invert = mc->invert; - unsigned int val; - int connect, change, reg_change = 0; - struct snd_soc_dapm_update update; + unsigned int val, rval = 0; + int connect, rconnect = -1, change, reg_change = 0; + struct snd_soc_dapm_update update = { NULL }; int ret = 0; - if (snd_soc_volsw_is_stereo(mc)) - dev_warn(dapm->dev, - "ASoC: Control '%s' is stereo, which is not supported\n", - kcontrol->id.name); - val = (ucontrol->value.integer.value[0] & mask); connect = !!val; if (invert) val = max - val; + if (snd_soc_volsw_is_stereo(mc)) { + rval = (ucontrol->value.integer.value[1] & mask); + rconnect = !!rval; + if (invert) + rval = max - rval; + } + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - change = dapm_kcontrol_set_value(kcontrol, val); + /* This assumes field width < (bits in unsigned int / 2) */ + if (width > sizeof(unsigned int) * 8 / 2) + dev_warn(dapm->dev, + "ASoC: control %s field width limit exceeded\n", + kcontrol->id.name); + change = dapm_kcontrol_set_value(kcontrol, val | (rval << width)); if (reg != SND_SOC_NOPM) { - mask = mask << shift; val = val << shift; + rval = rval << mc->rshift; + + reg_change = soc_dapm_test_bits(dapm, reg, mask << shift, val); - reg_change = soc_dapm_test_bits(dapm, reg, mask, val); + if (snd_soc_volsw_is_stereo(mc)) + reg_change |= soc_dapm_test_bits(dapm, mc->rreg, + mask << mc->rshift, + rval); } if (change || reg_change) { if (reg_change) { + if (snd_soc_volsw_is_stereo(mc)) { + update.has_second_set = true; + update.reg2 = mc->rreg; + update.mask2 = mask << mc->rshift; + update.val2 = rval; + } update.kcontrol = kcontrol; update.reg = reg; - update.mask = mask; + update.mask = mask << shift; update.val = val; card->update = &update; } change |= reg_change; - ret = soc_dapm_mixer_update_power(card, kcontrol, connect); + ret = soc_dapm_mixer_update_power(card, kcontrol, connect, + rconnect); card->update = NULL; } @@ -3189,7 +3284,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, unsigned int *item = ucontrol->value.enumerated.item; unsigned int val, change, reg_change = 0; unsigned int mask; - struct snd_soc_dapm_update update; + struct snd_soc_dapm_update update = { NULL }; int ret = 0; if (item[0] >= e->items) @@ -3200,7 +3295,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, if (e->shift_l != e->shift_r) { if (item[1] > e->items) return -EINVAL; - val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_l; + val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r; mask |= e->mask << e->shift_r; } @@ -3445,7 +3540,7 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, w->endpoints[dir] = -1; } - /* machine layer set ups unconnected pins and insertions */ + /* machine layer sets up unconnected pins and insertions */ w->connected = 1; return w; } diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index 6cef3977507a..17eb14935577 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -263,7 +263,6 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform); const struct snd_dmaengine_pcm_config *config = pcm->config; struct device *dev = rtd->platform->dev; - struct snd_dmaengine_dai_dma_data *dma_data; struct snd_pcm_substream *substream; size_t prealloc_buffer_size; size_t max_buffer_size; @@ -278,19 +277,11 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) max_buffer_size = SIZE_MAX; } - for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) { substream = rtd->pcm->streams[i].substream; if (!substream) continue; - dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - - if (!pcm->chan[i] && - (pcm->flags & SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME)) - pcm->chan[i] = dma_request_slave_channel(dev, - dma_data->chan_name); - if (!pcm->chan[i] && (pcm->flags & SND_DMAENGINE_PCM_FLAG_COMPAT)) { pcm->chan[i] = dmaengine_pcm_compat_request_channel(rtd, substream); @@ -359,9 +350,7 @@ static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, const char *name; struct dma_chan *chan; - if ((pcm->flags & (SND_DMAENGINE_PCM_FLAG_NO_DT | - SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME)) || - !dev->of_node) + if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_DT) || !dev->of_node) return 0; if (config && config->dma_dev) { diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index a513a34a51d2..9fc1a7bb8b95 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -77,7 +77,7 @@ int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, item = snd_soc_enum_val_to_item(e, val); ucontrol->value.enumerated.item[0] = item; if (e->shift_l != e->shift_r) { - val = (reg_val >> e->shift_l) & e->mask; + val = (reg_val >> e->shift_r) & e->mask; item = snd_soc_enum_val_to_item(e, val); ucontrol->value.enumerated.item[1] = item; } diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 60d702f8b9f0..6aba14009c92 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1694,6 +1694,9 @@ static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream, struct snd_soc_pcm_runtime *rtd = be_substream->private_data; int i; + if (rtd->dai_link->be_hw_params_fixup) + continue; + if (soc_pcm_has_symmetry(be_substream)) be_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; @@ -1790,7 +1793,7 @@ int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream) continue; dev_dbg(be->dev, "ASoC: close BE %s\n", - dpcm->fe->dai_link->name); + be->dai_link->name); soc_pcm_close(be_substream); be_substream->runtime = NULL; @@ -1856,7 +1859,7 @@ int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) continue; dev_dbg(be->dev, "ASoC: hw_free BE %s\n", - dpcm->fe->dai_link->name); + be->dai_link->name); soc_pcm_hw_free(be_substream); @@ -1934,7 +1937,7 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) continue; dev_dbg(be->dev, "ASoC: hw_params BE %s\n", - dpcm->fe->dai_link->name); + be->dai_link->name); ret = soc_pcm_hw_params(be_substream, &dpcm->hw_params); if (ret < 0) { @@ -2014,7 +2017,7 @@ static int dpcm_do_trigger(struct snd_soc_dpcm *dpcm, int ret; dev_dbg(dpcm->be->dev, "ASoC: trigger BE %s cmd %d\n", - dpcm->fe->dai_link->name, cmd); + dpcm->be->dai_link->name, cmd); ret = soc_pcm_trigger(substream, cmd); if (ret < 0) @@ -2181,9 +2184,11 @@ static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd) break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: fe->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED; + break; } out: @@ -2229,7 +2234,7 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) continue; dev_dbg(be->dev, "ASoC: prepare BE %s\n", - dpcm->fe->dai_link->name); + be->dai_link->name); ret = soc_pcm_prepare(be_substream); if (ret < 0) { @@ -2879,7 +2884,7 @@ int snd_soc_platform_trigger(struct snd_pcm_substream *substream, EXPORT_SYMBOL_GPL(snd_soc_platform_trigger); #ifdef CONFIG_DEBUG_FS -static char *dpcm_state_string(enum snd_soc_dpcm_state state) +static const char *dpcm_state_string(enum snd_soc_dpcm_state state) { switch (state) { case SND_SOC_DPCM_STATE_NEW: diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index ee7f15aa46fc..fbfb1fab88d5 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -48,10 +48,69 @@ #define SOC_TPLG_PASS_PCM_DAI 4 #define SOC_TPLG_PASS_GRAPH 5 #define SOC_TPLG_PASS_PINS 6 +#define SOC_TPLG_PASS_BE_DAI 7 +#define SOC_TPLG_PASS_LINK 8 #define SOC_TPLG_PASS_START SOC_TPLG_PASS_MANIFEST -#define SOC_TPLG_PASS_END SOC_TPLG_PASS_PINS +#define SOC_TPLG_PASS_END SOC_TPLG_PASS_LINK +/* + * Old version of ABI structs, supported for backward compatibility. + */ + +/* Manifest v4 */ +struct snd_soc_tplg_manifest_v4 { + __le32 size; /* in bytes of this structure */ + __le32 control_elems; /* number of control elements */ + __le32 widget_elems; /* number of widget elements */ + __le32 graph_elems; /* number of graph elements */ + __le32 pcm_elems; /* number of PCM elements */ + __le32 dai_link_elems; /* number of DAI link elements */ + struct snd_soc_tplg_private priv; +} __packed; + +/* Stream Capabilities v4 */ +struct snd_soc_tplg_stream_caps_v4 { + __le32 size; /* in bytes of this structure */ + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + __le64 formats; /* supported formats SNDRV_PCM_FMTBIT_* */ + __le32 rates; /* supported rates SNDRV_PCM_RATE_* */ + __le32 rate_min; /* min rate */ + __le32 rate_max; /* max rate */ + __le32 channels_min; /* min channels */ + __le32 channels_max; /* max channels */ + __le32 periods_min; /* min number of periods */ + __le32 periods_max; /* max number of periods */ + __le32 period_size_min; /* min period size bytes */ + __le32 period_size_max; /* max period size bytes */ + __le32 buffer_size_min; /* min buffer size bytes */ + __le32 buffer_size_max; /* max buffer size bytes */ +} __packed; + +/* PCM v4 */ +struct snd_soc_tplg_pcm_v4 { + __le32 size; /* in bytes of this structure */ + char pcm_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + char dai_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + __le32 pcm_id; /* unique ID - used to match with DAI link */ + __le32 dai_id; /* unique ID - used to match */ + __le32 playback; /* supports playback mode */ + __le32 capture; /* supports capture mode */ + __le32 compress; /* 1 = compressed; 0 = PCM */ + struct snd_soc_tplg_stream stream[SND_SOC_TPLG_STREAM_CONFIG_MAX]; /* for DAI link */ + __le32 num_streams; /* number of streams */ + struct snd_soc_tplg_stream_caps_v4 caps[2]; /* playback and capture for DAI */ +} __packed; + +/* Physical link config v4 */ +struct snd_soc_tplg_link_config_v4 { + __le32 size; /* in bytes of this structure */ + __le32 id; /* unique ID - used to match */ + struct snd_soc_tplg_stream stream[SND_SOC_TPLG_STREAM_CONFIG_MAX]; /* supported configs playback and captrure */ + __le32 num_streams; /* number of streams */ +} __packed; + +/* topology context */ struct soc_tplg { const struct firmware *fw; @@ -427,33 +486,40 @@ static void remove_widget(struct snd_soc_component *comp, dobj->ops->widget_unload(comp, dobj); /* - * Dynamic Widgets either have 1 enum kcontrol or 1..N mixers. + * Dynamic Widgets either have 1..N enum kcontrols or mixers. * The enum may either have an array of values or strings. */ - if (dobj->widget.kcontrol_enum) { + if (dobj->widget.kcontrol_type == SND_SOC_TPLG_TYPE_ENUM) { /* enumerated widget mixer */ - struct soc_enum *se = - (struct soc_enum *)w->kcontrols[0]->private_value; + for (i = 0; i < w->num_kcontrols; i++) { + struct snd_kcontrol *kcontrol = w->kcontrols[i]; + struct soc_enum *se = + (struct soc_enum *)kcontrol->private_value; - snd_ctl_remove(card, w->kcontrols[0]); + snd_ctl_remove(card, kcontrol); - kfree(se->dobj.control.dvalues); - for (i = 0; i < se->items; i++) - kfree(se->dobj.control.dtexts[i]); + kfree(se->dobj.control.dvalues); + for (i = 0; i < se->items; i++) + kfree(se->dobj.control.dtexts[i]); - kfree(se); + kfree(se); + } kfree(w->kcontrol_news); } else { - /* non enumerated widget mixer */ + /* volume mixer or bytes controls */ for (i = 0; i < w->num_kcontrols; i++) { struct snd_kcontrol *kcontrol = w->kcontrols[i]; - struct soc_mixer_control *sm = - (struct soc_mixer_control *) kcontrol->private_value; - kfree(w->kcontrols[i]->tlv.p); + if (dobj->widget.kcontrol_type + == SND_SOC_TPLG_TYPE_MIXER) + kfree(kcontrol->tlv.p); - snd_ctl_remove(card, w->kcontrols[i]); - kfree(sm); + /* Private value is used as struct soc_mixer_control + * for volume mixers or soc_bytes_ext for bytes + * controls. + */ + kfree((void *)kcontrol->private_value); + snd_ctl_remove(card, kcontrol); } kfree(w->kcontrol_news); } @@ -473,6 +539,7 @@ static void remove_dai(struct snd_soc_component *comp, if (dobj->ops && dobj->ops->dai_unload) dobj->ops->dai_unload(comp, dobj); + kfree(dai_drv->name); list_del(&dobj->list); kfree(dai_drv); } @@ -490,6 +557,10 @@ static void remove_link(struct snd_soc_component *comp, if (dobj->ops && dobj->ops->link_unload) dobj->ops->link_unload(comp, dobj); + kfree(link->name); + kfree(link->stream_name); + kfree(link->cpu_dai_name); + list_del(&dobj->list); snd_soc_remove_dai_link(comp->card, link); kfree(link); @@ -1192,98 +1263,105 @@ err: } static struct snd_kcontrol_new *soc_tplg_dapm_widget_denum_create( - struct soc_tplg *tplg) + struct soc_tplg *tplg, int num_kcontrols) { struct snd_kcontrol_new *kc; struct snd_soc_tplg_enum_control *ec; struct soc_enum *se; - int i, err; - - ec = (struct snd_soc_tplg_enum_control *)tplg->pos; - tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) + - ec->priv.size); + int i, j, err; - /* validate kcontrol */ - if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == - SNDRV_CTL_ELEM_ID_NAME_MAXLEN) - return NULL; - - kc = kzalloc(sizeof(*kc), GFP_KERNEL); + kc = kcalloc(num_kcontrols, sizeof(*kc), GFP_KERNEL); if (kc == NULL) return NULL; - se = kzalloc(sizeof(*se), GFP_KERNEL); - if (se == NULL) - goto err; + for (i = 0; i < num_kcontrols; i++) { + ec = (struct snd_soc_tplg_enum_control *)tplg->pos; + /* validate kcontrol */ + if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == + SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + return NULL; - dev_dbg(tplg->dev, " adding DAPM widget enum control %s\n", - ec->hdr.name); + se = kzalloc(sizeof(*se), GFP_KERNEL); + if (se == NULL) + goto err; - kc->name = ec->hdr.name; - kc->private_value = (long)se; - kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; - kc->access = ec->hdr.access; + dev_dbg(tplg->dev, " adding DAPM widget enum control %s\n", + ec->hdr.name); - /* we only support FL/FR channel mapping atm */ - se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL); - se->shift_l = tplc_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FL); - se->shift_r = tplc_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FR); + kc[i].name = ec->hdr.name; + kc[i].private_value = (long)se; + kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kc[i].access = ec->hdr.access; - se->items = ec->items; - se->mask = ec->mask; - se->dobj.index = tplg->index; + /* we only support FL/FR channel mapping atm */ + se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL); + se->shift_l = tplc_chan_get_shift(tplg, ec->channel, + SNDRV_CHMAP_FL); + se->shift_r = tplc_chan_get_shift(tplg, ec->channel, + SNDRV_CHMAP_FR); - switch (ec->hdr.ops.info) { - case SND_SOC_TPLG_CTL_ENUM_VALUE: - case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: - err = soc_tplg_denum_create_values(se, ec); - if (err < 0) { - dev_err(tplg->dev, "ASoC: could not create values for %s\n", - ec->hdr.name); + se->items = ec->items; + se->mask = ec->mask; + se->dobj.index = tplg->index; + + switch (ec->hdr.ops.info) { + case SND_SOC_TPLG_CTL_ENUM_VALUE: + case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: + err = soc_tplg_denum_create_values(se, ec); + if (err < 0) { + dev_err(tplg->dev, "ASoC: could not create values for %s\n", + ec->hdr.name); + goto err_se; + } + /* fall through to create texts */ + case SND_SOC_TPLG_CTL_ENUM: + case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: + case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: + err = soc_tplg_denum_create_texts(se, ec); + if (err < 0) { + dev_err(tplg->dev, "ASoC: could not create texts for %s\n", + ec->hdr.name); + goto err_se; + } + break; + default: + dev_err(tplg->dev, "ASoC: invalid enum control type %d for %s\n", + ec->hdr.ops.info, ec->hdr.name); goto err_se; } - /* fall through to create texts */ - case SND_SOC_TPLG_CTL_ENUM: - case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: - case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: - err = soc_tplg_denum_create_texts(se, ec); + + /* map io handlers */ + err = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc[i], tplg); + if (err) { + soc_control_err(tplg, &ec->hdr, ec->hdr.name); + goto err_se; + } + + /* pass control to driver for optional further init */ + err = soc_tplg_init_kcontrol(tplg, &kc[i], + (struct snd_soc_tplg_ctl_hdr *)ec); if (err < 0) { - dev_err(tplg->dev, "ASoC: could not create texts for %s\n", + dev_err(tplg->dev, "ASoC: failed to init %s\n", ec->hdr.name); goto err_se; } - break; - default: - dev_err(tplg->dev, "ASoC: invalid enum control type %d for %s\n", - ec->hdr.ops.info, ec->hdr.name); - goto err_se; - } - /* map io handlers */ - err = soc_tplg_kcontrol_bind_io(&ec->hdr, kc, tplg); - if (err) { - soc_control_err(tplg, &ec->hdr, ec->hdr.name); - goto err_se; - } - - /* pass control to driver for optional further init */ - err = soc_tplg_init_kcontrol(tplg, kc, - (struct snd_soc_tplg_ctl_hdr *)ec); - if (err < 0) { - dev_err(tplg->dev, "ASoC: failed to init %s\n", - ec->hdr.name); - goto err_se; + tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) + + ec->priv.size); } return kc; err_se: - /* free values and texts */ - kfree(se->dobj.control.dvalues); - for (i = 0; i < ec->items; i++) - kfree(se->dobj.control.dtexts[i]); + for (; i >= 0; i--) { + /* free values and texts */ + se = (struct soc_enum *)kc[i].private_value; + kfree(se->dobj.control.dvalues); + for (j = 0; j < ec->items; j++) + kfree(se->dobj.control.dtexts[j]); - kfree(se); + kfree(se); + } err: kfree(kc); @@ -1365,6 +1443,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, struct snd_soc_dapm_widget template, *widget; struct snd_soc_tplg_ctl_hdr *control_hdr; struct snd_soc_card *card = tplg->comp->card; + unsigned int kcontrol_type; int ret = 0; if (strnlen(w->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == @@ -1405,6 +1484,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, tplg->pos += (sizeof(struct snd_soc_tplg_dapm_widget) + w->priv.size); if (w->num_kcontrols == 0) { + kcontrol_type = 0; template.num_kcontrols = 0; goto widget; } @@ -1420,6 +1500,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, case SND_SOC_TPLG_CTL_VOLSW_XR_SX: case SND_SOC_TPLG_CTL_RANGE: case SND_SOC_TPLG_DAPM_CTL_VOLSW: + kcontrol_type = SND_SOC_TPLG_TYPE_MIXER; /* volume mixer */ template.num_kcontrols = w->num_kcontrols; template.kcontrol_news = soc_tplg_dapm_widget_dmixer_create(tplg, @@ -1434,16 +1515,18 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: - template.dobj.widget.kcontrol_enum = 1; - template.num_kcontrols = 1; + kcontrol_type = SND_SOC_TPLG_TYPE_ENUM; /* enumerated mixer */ + template.num_kcontrols = w->num_kcontrols; template.kcontrol_news = - soc_tplg_dapm_widget_denum_create(tplg); + soc_tplg_dapm_widget_denum_create(tplg, + template.num_kcontrols); if (!template.kcontrol_news) { ret = -ENOMEM; goto hdr_err; } break; case SND_SOC_TPLG_CTL_BYTES: + kcontrol_type = SND_SOC_TPLG_TYPE_BYTES; /* bytes control */ template.num_kcontrols = w->num_kcontrols; template.kcontrol_news = soc_tplg_dapm_widget_dbytes_create(tplg, @@ -1475,10 +1558,12 @@ widget: if (widget == NULL) { dev_err(tplg->dev, "ASoC: failed to create widget %s controls\n", w->name); + ret = -ENOMEM; goto hdr_err; } widget->dobj.type = SND_SOC_DOBJ_WIDGET; + widget->dobj.widget.kcontrol_type = kcontrol_type; widget->dobj.ops = tplg->ops; widget->dobj.index = tplg->index; kfree(template.sname); @@ -1554,6 +1639,25 @@ static void set_stream_info(struct snd_soc_pcm_stream *stream, stream->rate_min = caps->rate_min; stream->rate_max = caps->rate_max; stream->formats = caps->formats; + stream->sig_bits = caps->sig_bits; +} + +static void set_dai_flags(struct snd_soc_dai_driver *dai_drv, + unsigned int flag_mask, unsigned int flags) +{ + if (flag_mask & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_RATES) + dai_drv->symmetric_rates = + flags & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_RATES ? 1 : 0; + + if (flag_mask & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_CHANNELS) + dai_drv->symmetric_channels = + flags & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_CHANNELS ? + 1 : 0; + + if (flag_mask & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_SAMPLEBITS) + dai_drv->symmetric_samplebits = + flags & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_SAMPLEBITS ? + 1 : 0; } static int soc_tplg_dai_create(struct soc_tplg *tplg, @@ -1568,7 +1672,8 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg, if (dai_drv == NULL) return -ENOMEM; - dai_drv->name = pcm->dai_name; + if (strlen(pcm->dai_name)) + dai_drv->name = kstrdup(pcm->dai_name, GFP_KERNEL); dai_drv->id = pcm->dai_id; if (pcm->playback) { @@ -1600,8 +1705,31 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg, return snd_soc_register_dai(tplg->comp, dai_drv); } +static void set_link_flags(struct snd_soc_dai_link *link, + unsigned int flag_mask, unsigned int flags) +{ + if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES) + link->symmetric_rates = + flags & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES ? 1 : 0; + + if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS) + link->symmetric_channels = + flags & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS ? + 1 : 0; + + if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS) + link->symmetric_samplebits = + flags & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS ? + 1 : 0; + + if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP) + link->ignore_suspend = + flags & SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP ? + 1 : 0; +} + /* create the FE DAI link */ -static int soc_tplg_link_create(struct soc_tplg *tplg, +static int soc_tplg_fe_link_create(struct soc_tplg *tplg, struct snd_soc_tplg_pcm *pcm) { struct snd_soc_dai_link *link; @@ -1611,11 +1739,15 @@ static int soc_tplg_link_create(struct soc_tplg *tplg, if (link == NULL) return -ENOMEM; - link->name = pcm->pcm_name; - link->stream_name = pcm->pcm_name; + if (strlen(pcm->pcm_name)) { + link->name = kstrdup(pcm->pcm_name, GFP_KERNEL); + link->stream_name = kstrdup(pcm->pcm_name, GFP_KERNEL); + } link->id = pcm->pcm_id; - link->cpu_dai_name = pcm->dai_name; + if (strlen(pcm->dai_name)) + link->cpu_dai_name = kstrdup(pcm->dai_name, GFP_KERNEL); + link->codec_name = "snd-soc-dummy"; link->codec_dai_name = "snd-soc-dummy-dai"; @@ -1623,6 +1755,8 @@ static int soc_tplg_link_create(struct soc_tplg *tplg, link->dynamic = 1; link->dpcm_playback = pcm->playback; link->dpcm_capture = pcm->capture; + if (pcm->flag_mask) + set_link_flags(link, pcm->flag_mask, pcm->flags); /* pass control to component driver for optional further init */ ret = soc_tplg_dai_link_load(tplg, link); @@ -1651,65 +1785,504 @@ static int soc_tplg_pcm_create(struct soc_tplg *tplg, if (ret < 0) return ret; - return soc_tplg_link_create(tplg, pcm); + return soc_tplg_fe_link_create(tplg, pcm); +} + +/* copy stream caps from the old version 4 of source */ +static void stream_caps_new_ver(struct snd_soc_tplg_stream_caps *dest, + struct snd_soc_tplg_stream_caps_v4 *src) +{ + dest->size = sizeof(*dest); + memcpy(dest->name, src->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + dest->formats = src->formats; + dest->rates = src->rates; + dest->rate_min = src->rate_min; + dest->rate_max = src->rate_max; + dest->channels_min = src->channels_min; + dest->channels_max = src->channels_max; + dest->periods_min = src->periods_min; + dest->periods_max = src->periods_max; + dest->period_size_min = src->period_size_min; + dest->period_size_max = src->period_size_max; + dest->buffer_size_min = src->buffer_size_min; + dest->buffer_size_max = src->buffer_size_max; +} + +/** + * pcm_new_ver - Create the new version of PCM from the old version. + * @tplg: topology context + * @src: older version of pcm as a source + * @pcm: latest version of pcm created from the source + * + * Support from vesion 4. User should free the returned pcm manually. + */ +static int pcm_new_ver(struct soc_tplg *tplg, + struct snd_soc_tplg_pcm *src, + struct snd_soc_tplg_pcm **pcm) +{ + struct snd_soc_tplg_pcm *dest; + struct snd_soc_tplg_pcm_v4 *src_v4; + int i; + + *pcm = NULL; + + if (src->size != sizeof(*src_v4)) { + dev_err(tplg->dev, "ASoC: invalid PCM size\n"); + return -EINVAL; + } + + dev_warn(tplg->dev, "ASoC: old version of PCM\n"); + src_v4 = (struct snd_soc_tplg_pcm_v4 *)src; + dest = kzalloc(sizeof(*dest), GFP_KERNEL); + if (!dest) + return -ENOMEM; + + dest->size = sizeof(*dest); /* size of latest abi version */ + memcpy(dest->pcm_name, src_v4->pcm_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + memcpy(dest->dai_name, src_v4->dai_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + dest->pcm_id = src_v4->pcm_id; + dest->dai_id = src_v4->dai_id; + dest->playback = src_v4->playback; + dest->capture = src_v4->capture; + dest->compress = src_v4->compress; + dest->num_streams = src_v4->num_streams; + for (i = 0; i < dest->num_streams; i++) + memcpy(&dest->stream[i], &src_v4->stream[i], + sizeof(struct snd_soc_tplg_stream)); + + for (i = 0; i < 2; i++) + stream_caps_new_ver(&dest->caps[i], &src_v4->caps[i]); + + *pcm = dest; + return 0; } static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg, struct snd_soc_tplg_hdr *hdr) { - struct snd_soc_tplg_pcm *pcm; + struct snd_soc_tplg_pcm *pcm, *_pcm; int count = hdr->count; - int i; + int i, err; + bool abi_match; if (tplg->pass != SOC_TPLG_PASS_PCM_DAI) return 0; + /* check the element size and count */ + pcm = (struct snd_soc_tplg_pcm *)tplg->pos; + if (pcm->size > sizeof(struct snd_soc_tplg_pcm) + || pcm->size < sizeof(struct snd_soc_tplg_pcm_v4)) { + dev_err(tplg->dev, "ASoC: invalid size %d for PCM elems\n", + pcm->size); + return -EINVAL; + } + if (soc_tplg_check_elem_count(tplg, - sizeof(struct snd_soc_tplg_pcm), count, + pcm->size, count, hdr->payload_size, "PCM DAI")) { dev_err(tplg->dev, "ASoC: invalid count %d for PCM DAI elems\n", count); return -EINVAL; } - /* create the FE DAIs and DAI links */ - pcm = (struct snd_soc_tplg_pcm *)tplg->pos; for (i = 0; i < count; i++) { - if (pcm->size != sizeof(*pcm)) { - dev_err(tplg->dev, "ASoC: invalid pcm size\n"); - return -EINVAL; + pcm = (struct snd_soc_tplg_pcm *)tplg->pos; + + /* check ABI version by size, create a new version of pcm + * if abi not match. + */ + if (pcm->size == sizeof(*pcm)) { + abi_match = true; + _pcm = pcm; + } else { + abi_match = false; + err = pcm_new_ver(tplg, pcm, &_pcm); } - soc_tplg_pcm_create(tplg, pcm); - pcm++; + /* create the FE DAIs and DAI links */ + soc_tplg_pcm_create(tplg, _pcm); + + /* offset by version-specific struct size and + * real priv data size + */ + tplg->pos += pcm->size + _pcm->priv.size; + + if (!abi_match) + kfree(_pcm); /* free the duplicated one */ } dev_dbg(tplg->dev, "ASoC: adding %d PCM DAIs\n", count); - tplg->pos += sizeof(struct snd_soc_tplg_pcm) * count; return 0; } -static int soc_tplg_manifest_load(struct soc_tplg *tplg, +/** + * set_link_hw_format - Set the HW audio format of the physical DAI link. + * @tplg: topology context + * @cfg: physical link configs. + * + * Topology context contains a list of supported HW formats (configs) and + * a default format ID for the physical link. This function will use this + * default ID to choose the HW format to set the link's DAI format for init. + */ +static void set_link_hw_format(struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg) +{ + struct snd_soc_tplg_hw_config *hw_config; + unsigned char bclk_master, fsync_master; + unsigned char invert_bclk, invert_fsync; + int i; + + for (i = 0; i < cfg->num_hw_configs; i++) { + hw_config = &cfg->hw_config[i]; + if (hw_config->id != cfg->default_hw_config_id) + continue; + + link->dai_fmt = hw_config->fmt & SND_SOC_DAIFMT_FORMAT_MASK; + + /* clock signal polarity */ + invert_bclk = hw_config->invert_bclk; + invert_fsync = hw_config->invert_fsync; + if (!invert_bclk && !invert_fsync) + link->dai_fmt |= SND_SOC_DAIFMT_NB_NF; + else if (!invert_bclk && invert_fsync) + link->dai_fmt |= SND_SOC_DAIFMT_NB_IF; + else if (invert_bclk && !invert_fsync) + link->dai_fmt |= SND_SOC_DAIFMT_IB_NF; + else + link->dai_fmt |= SND_SOC_DAIFMT_IB_IF; + + /* clock masters */ + bclk_master = hw_config->bclk_master; + fsync_master = hw_config->fsync_master; + if (!bclk_master && !fsync_master) + link->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; + else if (bclk_master && !fsync_master) + link->dai_fmt |= SND_SOC_DAIFMT_CBS_CFM; + else if (!bclk_master && fsync_master) + link->dai_fmt |= SND_SOC_DAIFMT_CBM_CFS; + else + link->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; + } +} + +/** + * link_new_ver - Create a new physical link config from the old + * version of source. + * @toplogy: topology context + * @src: old version of phyical link config as a source + * @link: latest version of physical link config created from the source + * + * Support from vesion 4. User need free the returned link config manually. + */ +static int link_new_ver(struct soc_tplg *tplg, + struct snd_soc_tplg_link_config *src, + struct snd_soc_tplg_link_config **link) +{ + struct snd_soc_tplg_link_config *dest; + struct snd_soc_tplg_link_config_v4 *src_v4; + int i; + + *link = NULL; + + if (src->size != sizeof(struct snd_soc_tplg_link_config_v4)) { + dev_err(tplg->dev, "ASoC: invalid physical link config size\n"); + return -EINVAL; + } + + dev_warn(tplg->dev, "ASoC: old version of physical link config\n"); + + src_v4 = (struct snd_soc_tplg_link_config_v4 *)src; + dest = kzalloc(sizeof(*dest), GFP_KERNEL); + if (!dest) + return -ENOMEM; + + dest->size = sizeof(*dest); + dest->id = src_v4->id; + dest->num_streams = src_v4->num_streams; + for (i = 0; i < dest->num_streams; i++) + memcpy(&dest->stream[i], &src_v4->stream[i], + sizeof(struct snd_soc_tplg_stream)); + + *link = dest; + return 0; +} + +/* Find and configure an existing physical DAI link */ +static int soc_tplg_link_config(struct soc_tplg *tplg, + struct snd_soc_tplg_link_config *cfg) +{ + struct snd_soc_dai_link *link; + const char *name, *stream_name; + size_t len; + int ret; + + len = strnlen(cfg->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + if (len == SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + return -EINVAL; + else if (len) + name = cfg->name; + else + name = NULL; + + len = strnlen(cfg->stream_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + if (len == SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + return -EINVAL; + else if (len) + stream_name = cfg->stream_name; + else + stream_name = NULL; + + link = snd_soc_find_dai_link(tplg->comp->card, cfg->id, + name, stream_name); + if (!link) { + dev_err(tplg->dev, "ASoC: physical link %s (id %d) not exist\n", + name, cfg->id); + return -EINVAL; + } + + /* hw format */ + if (cfg->num_hw_configs) + set_link_hw_format(link, cfg); + + /* flags */ + if (cfg->flag_mask) + set_link_flags(link, cfg->flag_mask, cfg->flags); + + /* pass control to component driver for optional further init */ + ret = soc_tplg_dai_link_load(tplg, link); + if (ret < 0) { + dev_err(tplg->dev, "ASoC: physical link loading failed\n"); + return ret; + } + + return 0; +} + + +/* Load physical link config elements from the topology context */ +static int soc_tplg_link_elems_load(struct soc_tplg *tplg, struct snd_soc_tplg_hdr *hdr) { - struct snd_soc_tplg_manifest *manifest; + struct snd_soc_tplg_link_config *link, *_link; + int count = hdr->count; + int i, ret; + bool abi_match; - if (tplg->pass != SOC_TPLG_PASS_MANIFEST) + if (tplg->pass != SOC_TPLG_PASS_LINK) { + tplg->pos += hdr->size + hdr->payload_size; return 0; + }; + + /* check the element size and count */ + link = (struct snd_soc_tplg_link_config *)tplg->pos; + if (link->size > sizeof(struct snd_soc_tplg_link_config) + || link->size < sizeof(struct snd_soc_tplg_link_config_v4)) { + dev_err(tplg->dev, "ASoC: invalid size %d for physical link elems\n", + link->size); + return -EINVAL; + } - manifest = (struct snd_soc_tplg_manifest *)tplg->pos; - if (manifest->size != sizeof(*manifest)) { + if (soc_tplg_check_elem_count(tplg, + link->size, count, + hdr->payload_size, "physical link config")) { + dev_err(tplg->dev, "ASoC: invalid count %d for physical link elems\n", + count); + return -EINVAL; + } + + /* config physical DAI links */ + for (i = 0; i < count; i++) { + link = (struct snd_soc_tplg_link_config *)tplg->pos; + if (link->size == sizeof(*link)) { + abi_match = true; + _link = link; + } else { + abi_match = false; + ret = link_new_ver(tplg, link, &_link); + if (ret < 0) + return ret; + } + + ret = soc_tplg_link_config(tplg, _link); + if (ret < 0) + return ret; + + /* offset by version-specific struct size and + * real priv data size + */ + tplg->pos += link->size + _link->priv.size; + + if (!abi_match) + kfree(_link); /* free the duplicated one */ + } + + return 0; +} + +/** + * soc_tplg_dai_config - Find and configure an existing physical DAI. + * @tplg: topology context + * @d: physical DAI configs. + * + * The physical dai should already be registered by the platform driver. + * The platform driver should specify the DAI name and ID for matching. + */ +static int soc_tplg_dai_config(struct soc_tplg *tplg, + struct snd_soc_tplg_dai *d) +{ + struct snd_soc_dai_link_component dai_component = {0}; + struct snd_soc_dai *dai; + struct snd_soc_dai_driver *dai_drv; + struct snd_soc_pcm_stream *stream; + struct snd_soc_tplg_stream_caps *caps; + int ret; + + dai_component.dai_name = d->dai_name; + dai = snd_soc_find_dai(&dai_component); + if (!dai) { + dev_err(tplg->dev, "ASoC: physical DAI %s not registered\n", + d->dai_name); + return -EINVAL; + } + + if (d->dai_id != dai->id) { + dev_err(tplg->dev, "ASoC: physical DAI %s id mismatch\n", + d->dai_name); + return -EINVAL; + } + + dai_drv = dai->driver; + if (!dai_drv) + return -EINVAL; + + if (d->playback) { + stream = &dai_drv->playback; + caps = &d->caps[SND_SOC_TPLG_STREAM_PLAYBACK]; + set_stream_info(stream, caps); + } + + if (d->capture) { + stream = &dai_drv->capture; + caps = &d->caps[SND_SOC_TPLG_STREAM_CAPTURE]; + set_stream_info(stream, caps); + } + + if (d->flag_mask) + set_dai_flags(dai_drv, d->flag_mask, d->flags); + + /* pass control to component driver for optional further init */ + ret = soc_tplg_dai_load(tplg, dai_drv); + if (ret < 0) { + dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n"); + return ret; + } + + return 0; +} + +/* load physical DAI elements */ +static int soc_tplg_dai_elems_load(struct soc_tplg *tplg, + struct snd_soc_tplg_hdr *hdr) +{ + struct snd_soc_tplg_dai *dai; + int count = hdr->count; + int i; + + if (tplg->pass != SOC_TPLG_PASS_BE_DAI) + return 0; + + /* config the existing BE DAIs */ + for (i = 0; i < count; i++) { + dai = (struct snd_soc_tplg_dai *)tplg->pos; + if (dai->size != sizeof(*dai)) { + dev_err(tplg->dev, "ASoC: invalid physical DAI size\n"); + return -EINVAL; + } + + soc_tplg_dai_config(tplg, dai); + tplg->pos += (sizeof(*dai) + dai->priv.size); + } + + dev_dbg(tplg->dev, "ASoC: Configure %d BE DAIs\n", count); + return 0; +} + +/** + * manifest_new_ver - Create a new version of manifest from the old version + * of source. + * @toplogy: topology context + * @src: old version of manifest as a source + * @manifest: latest version of manifest created from the source + * + * Support from vesion 4. Users need free the returned manifest manually. + */ +static int manifest_new_ver(struct soc_tplg *tplg, + struct snd_soc_tplg_manifest *src, + struct snd_soc_tplg_manifest **manifest) +{ + struct snd_soc_tplg_manifest *dest; + struct snd_soc_tplg_manifest_v4 *src_v4; + + *manifest = NULL; + + if (src->size != sizeof(*src_v4)) { dev_err(tplg->dev, "ASoC: invalid manifest size\n"); return -EINVAL; } - tplg->pos += sizeof(struct snd_soc_tplg_manifest); + dev_warn(tplg->dev, "ASoC: old version of manifest\n"); + + src_v4 = (struct snd_soc_tplg_manifest_v4 *)src; + dest = kzalloc(sizeof(*dest) + src_v4->priv.size, GFP_KERNEL); + if (!dest) + return -ENOMEM; + + dest->size = sizeof(*dest); /* size of latest abi version */ + dest->control_elems = src_v4->control_elems; + dest->widget_elems = src_v4->widget_elems; + dest->graph_elems = src_v4->graph_elems; + dest->pcm_elems = src_v4->pcm_elems; + dest->dai_link_elems = src_v4->dai_link_elems; + dest->priv.size = src_v4->priv.size; + if (dest->priv.size) + memcpy(dest->priv.data, src_v4->priv.data, + src_v4->priv.size); + + *manifest = dest; + return 0; +} +static int soc_tplg_manifest_load(struct soc_tplg *tplg, + struct snd_soc_tplg_hdr *hdr) +{ + struct snd_soc_tplg_manifest *manifest, *_manifest; + bool abi_match; + int err; + + if (tplg->pass != SOC_TPLG_PASS_MANIFEST) + return 0; + + manifest = (struct snd_soc_tplg_manifest *)tplg->pos; + + /* check ABI version by size, create a new manifest if abi not match */ + if (manifest->size == sizeof(*manifest)) { + abi_match = true; + _manifest = manifest; + } else { + abi_match = false; + err = manifest_new_ver(tplg, manifest, &_manifest); + if (err < 0) + return err; + } + + /* pass control to component driver for optional further init */ if (tplg->comp && tplg->ops && tplg->ops->manifest) - return tplg->ops->manifest(tplg->comp, manifest); + return tplg->ops->manifest(tplg->comp, _manifest); + + if (!abi_match) /* free the duplicated one */ + kfree(_manifest); - dev_err(tplg->dev, "ASoC: Firmware manifest not supported\n"); return 0; } @@ -1745,7 +2318,9 @@ static int soc_valid_header(struct soc_tplg *tplg, return -EINVAL; } - if (hdr->abi != SND_SOC_TPLG_ABI_VERSION) { + /* Support ABI from version 4 */ + if (hdr->abi > SND_SOC_TPLG_ABI_VERSION + || hdr->abi < SND_SOC_TPLG_ABI_VERSION_MIN) { dev_err(tplg->dev, "ASoC: pass %d invalid ABI version got 0x%x need 0x%x at offset 0x%lx size 0x%zx.\n", tplg->pass, hdr->abi, @@ -1793,6 +2368,12 @@ static int soc_tplg_load_header(struct soc_tplg *tplg, return soc_tplg_dapm_widget_elems_load(tplg, hdr); case SND_SOC_TPLG_TYPE_PCM: return soc_tplg_pcm_elems_load(tplg, hdr); + case SND_SOC_TPLG_TYPE_DAI: + return soc_tplg_dai_elems_load(tplg, hdr); + case SND_SOC_TPLG_TYPE_DAI_LINK: + case SND_SOC_TPLG_TYPE_BACKEND_LINK: + /* physical link configurations */ + return soc_tplg_link_elems_load(tplg, hdr); case SND_SOC_TPLG_TYPE_MANIFEST: return soc_tplg_manifest_load(tplg, hdr); default: diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index 53dd085d3ee2..644d9a9ebfbc 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -58,6 +58,205 @@ int snd_soc_params_to_bclk(struct snd_pcm_hw_params *params) } EXPORT_SYMBOL_GPL(snd_soc_params_to_bclk); +int snd_soc_component_enable_pin(struct snd_soc_component *component, + const char *pin) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + char *full_name; + int ret; + + if (!component->name_prefix) + return snd_soc_dapm_enable_pin(dapm, pin); + + full_name = kasprintf(GFP_KERNEL, "%s %s", component->name_prefix, pin); + if (!full_name) + return -ENOMEM; + + ret = snd_soc_dapm_enable_pin(dapm, full_name); + kfree(full_name); + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_component_enable_pin); + +int snd_soc_component_enable_pin_unlocked(struct snd_soc_component *component, + const char *pin) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + char *full_name; + int ret; + + if (!component->name_prefix) + return snd_soc_dapm_enable_pin_unlocked(dapm, pin); + + full_name = kasprintf(GFP_KERNEL, "%s %s", component->name_prefix, pin); + if (!full_name) + return -ENOMEM; + + ret = snd_soc_dapm_enable_pin_unlocked(dapm, full_name); + kfree(full_name); + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_component_enable_pin_unlocked); + +int snd_soc_component_disable_pin(struct snd_soc_component *component, + const char *pin) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + char *full_name; + int ret; + + if (!component->name_prefix) + return snd_soc_dapm_disable_pin(dapm, pin); + + full_name = kasprintf(GFP_KERNEL, "%s %s", component->name_prefix, pin); + if (!full_name) + return -ENOMEM; + + ret = snd_soc_dapm_disable_pin(dapm, full_name); + kfree(full_name); + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_component_disable_pin); + +int snd_soc_component_disable_pin_unlocked(struct snd_soc_component *component, + const char *pin) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + char *full_name; + int ret; + + if (!component->name_prefix) + return snd_soc_dapm_disable_pin_unlocked(dapm, pin); + + full_name = kasprintf(GFP_KERNEL, "%s %s", component->name_prefix, pin); + if (!full_name) + return -ENOMEM; + + ret = snd_soc_dapm_disable_pin_unlocked(dapm, full_name); + kfree(full_name); + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_component_disable_pin_unlocked); + +int snd_soc_component_nc_pin(struct snd_soc_component *component, + const char *pin) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + char *full_name; + int ret; + + if (!component->name_prefix) + return snd_soc_dapm_nc_pin(dapm, pin); + + full_name = kasprintf(GFP_KERNEL, "%s %s", component->name_prefix, pin); + if (!full_name) + return -ENOMEM; + + ret = snd_soc_dapm_nc_pin(dapm, full_name); + kfree(full_name); + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_component_nc_pin); + +int snd_soc_component_nc_pin_unlocked(struct snd_soc_component *component, + const char *pin) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + char *full_name; + int ret; + + if (!component->name_prefix) + return snd_soc_dapm_nc_pin_unlocked(dapm, pin); + + full_name = kasprintf(GFP_KERNEL, "%s %s", component->name_prefix, pin); + if (!full_name) + return -ENOMEM; + + ret = snd_soc_dapm_nc_pin_unlocked(dapm, full_name); + kfree(full_name); + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_component_nc_pin_unlocked); + +int snd_soc_component_get_pin_status(struct snd_soc_component *component, + const char *pin) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + char *full_name; + int ret; + + if (!component->name_prefix) + return snd_soc_dapm_get_pin_status(dapm, pin); + + full_name = kasprintf(GFP_KERNEL, "%s %s", component->name_prefix, pin); + if (!full_name) + return -ENOMEM; + + ret = snd_soc_dapm_get_pin_status(dapm, full_name); + kfree(full_name); + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_component_get_pin_status); + +int snd_soc_component_force_enable_pin(struct snd_soc_component *component, + const char *pin) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + char *full_name; + int ret; + + if (!component->name_prefix) + return snd_soc_dapm_force_enable_pin(dapm, pin); + + full_name = kasprintf(GFP_KERNEL, "%s %s", component->name_prefix, pin); + if (!full_name) + return -ENOMEM; + + ret = snd_soc_dapm_force_enable_pin(dapm, full_name); + kfree(full_name); + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_component_force_enable_pin); + +int snd_soc_component_force_enable_pin_unlocked( + struct snd_soc_component *component, + const char *pin) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + char *full_name; + int ret; + + if (!component->name_prefix) + return snd_soc_dapm_force_enable_pin_unlocked(dapm, pin); + + full_name = kasprintf(GFP_KERNEL, "%s %s", component->name_prefix, pin); + if (!full_name) + return -ENOMEM; + + ret = snd_soc_dapm_force_enable_pin_unlocked(dapm, full_name); + kfree(full_name); + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_component_force_enable_pin_unlocked); + static const struct snd_pcm_hardware dummy_dma_hardware = { /* Random values to keep userspace happy when checking constraints */ .info = SNDRV_PCM_INFO_INTERLEAVED | @@ -80,7 +279,7 @@ static int dummy_dma_open(struct snd_pcm_substream *substream) return 0; } -static struct snd_pcm_ops dummy_dma_ops = { +static const struct snd_pcm_ops dummy_dma_ops = { .open = dummy_dma_open, .ioctl = snd_pcm_lib_ioctl, }; diff --git a/sound/soc/sti/sti_uniperif.c b/sound/soc/sti/sti_uniperif.c index 488ef4ed8fba..98eb205a0b62 100644 --- a/sound/soc/sti/sti_uniperif.c +++ b/sound/soc/sti/sti_uniperif.c @@ -7,6 +7,7 @@ #include <linux/module.h> #include <linux/pinctrl/consumer.h> +#include <linux/delay.h> #include "uniperif.h" @@ -19,6 +20,106 @@ #define UNIPERIF_MAX_FRAME_SZ 0x20 #define UNIPERIF_ALLOWED_FRAME_SZ (0x08 | 0x10 | 0x18 | UNIPERIF_MAX_FRAME_SZ) +struct sti_uniperiph_dev_data { + unsigned int id; /* Nb available player instances */ + unsigned int version; /* player IP version */ + unsigned int stream; + const char *dai_names; + enum uniperif_type type; +}; + +static const struct sti_uniperiph_dev_data sti_uniplayer_hdmi = { + .id = 0, + .version = SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0, + .stream = SNDRV_PCM_STREAM_PLAYBACK, + .dai_names = "Uni Player #0 (HDMI)", + .type = SND_ST_UNIPERIF_TYPE_HDMI +}; + +static const struct sti_uniperiph_dev_data sti_uniplayer_pcm_out = { + .id = 1, + .version = SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0, + .stream = SNDRV_PCM_STREAM_PLAYBACK, + .dai_names = "Uni Player #1 (PCM OUT)", + .type = SND_ST_UNIPERIF_TYPE_PCM | SND_ST_UNIPERIF_TYPE_TDM, +}; + +static const struct sti_uniperiph_dev_data sti_uniplayer_dac = { + .id = 2, + .version = SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0, + .stream = SNDRV_PCM_STREAM_PLAYBACK, + .dai_names = "Uni Player #2 (DAC)", + .type = SND_ST_UNIPERIF_TYPE_PCM, +}; + +static const struct sti_uniperiph_dev_data sti_uniplayer_spdif = { + .id = 3, + .version = SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0, + .stream = SNDRV_PCM_STREAM_PLAYBACK, + .dai_names = "Uni Player #3 (SPDIF)", + .type = SND_ST_UNIPERIF_TYPE_SPDIF +}; + +static const struct sti_uniperiph_dev_data sti_unireader_pcm_in = { + .id = 0, + .version = SND_ST_UNIPERIF_VERSION_UNI_RDR_1_0, + .stream = SNDRV_PCM_STREAM_CAPTURE, + .dai_names = "Uni Reader #0 (PCM IN)", + .type = SND_ST_UNIPERIF_TYPE_PCM | SND_ST_UNIPERIF_TYPE_TDM, +}; + +static const struct sti_uniperiph_dev_data sti_unireader_hdmi_in = { + .id = 1, + .version = SND_ST_UNIPERIF_VERSION_UNI_RDR_1_0, + .stream = SNDRV_PCM_STREAM_CAPTURE, + .dai_names = "Uni Reader #1 (HDMI IN)", + .type = SND_ST_UNIPERIF_TYPE_PCM, +}; + +static const struct of_device_id snd_soc_sti_match[] = { + { .compatible = "st,stih407-uni-player-hdmi", + .data = &sti_uniplayer_hdmi + }, + { .compatible = "st,stih407-uni-player-pcm-out", + .data = &sti_uniplayer_pcm_out + }, + { .compatible = "st,stih407-uni-player-dac", + .data = &sti_uniplayer_dac + }, + { .compatible = "st,stih407-uni-player-spdif", + .data = &sti_uniplayer_spdif + }, + { .compatible = "st,stih407-uni-reader-pcm_in", + .data = &sti_unireader_pcm_in + }, + { .compatible = "st,stih407-uni-reader-hdmi", + .data = &sti_unireader_hdmi_in + }, + {}, +}; + +int sti_uniperiph_reset(struct uniperif *uni) +{ + int count = 10; + + /* Reset uniperipheral uni */ + SET_UNIPERIF_SOFT_RST_SOFT_RST(uni); + + if (uni->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) { + while (GET_UNIPERIF_SOFT_RST_SOFT_RST(uni) && count) { + udelay(5); + count--; + } + } + + if (!count) { + dev_err(uni->dev, "Failed to reset uniperif\n"); + return -EIO; + } + + return 0; +} + int sti_uniperiph_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) @@ -167,8 +268,8 @@ static int sti_uniperiph_dai_create_ctrl(struct snd_soc_dai *dai) * Uniperipheral instance ID */ ctrl = &uni->snd_ctrls[i]; - ctrl->index = uni->info->id; - ctrl->device = uni->info->id; + ctrl->index = uni->id; + ctrl->device = uni->id; } return snd_soc_add_dai_controls(dai, uni->snd_ctrls, uni->num_ctrls); @@ -186,7 +287,7 @@ int sti_uniperiph_dai_hw_params(struct snd_pcm_substream *substream, struct snd_dmaengine_dai_dma_data *dma_data; int transfer_size; - if (uni->info->type == SND_ST_UNIPERIF_TYPE_TDM) + if (uni->type == SND_ST_UNIPERIF_TYPE_TDM) /* transfer size = user frame size (in 32-bits FIFO cell) */ transfer_size = snd_soc_params_to_frame_size(params) / 32; else @@ -215,7 +316,7 @@ static int sti_uniperiph_dai_suspend(struct snd_soc_dai *dai) /* The uniperipheral should be in stopped state */ if (uni->state != UNIPERIF_STATE_STOPPED) { - dev_err(uni->dev, "%s: invalid uni state( %d)", + dev_err(uni->dev, "%s: invalid uni state( %d)\n", __func__, (int)uni->state); return -EBUSY; } @@ -223,7 +324,7 @@ static int sti_uniperiph_dai_suspend(struct snd_soc_dai *dai) /* Pinctrl: switch pinstate to sleep */ ret = pinctrl_pm_select_sleep_state(uni->dev); if (ret) - dev_err(uni->dev, "%s: failed to select pinctrl state", + dev_err(uni->dev, "%s: failed to select pinctrl state\n", __func__); return ret; @@ -235,7 +336,7 @@ static int sti_uniperiph_dai_resume(struct snd_soc_dai *dai) struct uniperif *uni = priv->dai_data.uni; int ret; - if (of_device_is_compatible(dai->dev->of_node, "st,sti-uni-player")) { + if (priv->dai_data.stream == SNDRV_PCM_STREAM_PLAYBACK) { ret = uni_player_resume(uni); if (ret) return ret; @@ -244,7 +345,7 @@ static int sti_uniperiph_dai_resume(struct snd_soc_dai *dai) /* pinctrl: switch pinstate to default */ ret = pinctrl_pm_select_default_state(uni->dev); if (ret) - dev_err(uni->dev, "%s: failed to select pinctrl state", + dev_err(uni->dev, "%s: failed to select pinctrl state\n", __func__); return ret; @@ -256,7 +357,7 @@ static int sti_uniperiph_dai_probe(struct snd_soc_dai *dai) struct sti_uniperiph_dai *dai_data = &priv->dai_data; /* DMA settings*/ - if (of_device_is_compatible(dai->dev->of_node, "st,sti-uni-player")) + if (priv->dai_data.stream == SNDRV_PCM_STREAM_PLAYBACK) snd_soc_dai_init_dma_data(dai, &dai_data->dma_data, NULL); else snd_soc_dai_init_dma_data(dai, NULL, &dai_data->dma_data); @@ -280,31 +381,39 @@ static const struct snd_soc_component_driver sti_uniperiph_dai_component = { static int sti_uniperiph_cpu_dai_of(struct device_node *node, struct sti_uniperiph_data *priv) { - const char *str; - int ret; struct device *dev = &priv->pdev->dev; struct sti_uniperiph_dai *dai_data = &priv->dai_data; struct snd_soc_dai_driver *dai = priv->dai; struct snd_soc_pcm_stream *stream; struct uniperif *uni; + const struct of_device_id *of_id; + const struct sti_uniperiph_dev_data *dev_data; + const char *mode; + int ret; + + /* Populate data structure depending on compatibility */ + of_id = of_match_node(snd_soc_sti_match, node); + if (!of_id->data) { + dev_err(dev, "data associated to device is missing\n"); + return -EINVAL; + } + dev_data = (struct sti_uniperiph_dev_data *)of_id->data; uni = devm_kzalloc(dev, sizeof(*uni), GFP_KERNEL); if (!uni) return -ENOMEM; + uni->id = dev_data->id; + uni->ver = dev_data->version; + *dai = sti_uniperiph_dai_template; - ret = of_property_read_string(node, "dai-name", &str); - if (ret < 0) { - dev_err(dev, "%s: dai name missing.\n", __func__); - return -EINVAL; - } - dai->name = str; + dai->name = dev_data->dai_names; /* Get resources */ uni->mem_region = platform_get_resource(priv->pdev, IORESOURCE_MEM, 0); if (!uni->mem_region) { - dev_err(dev, "Failed to get memory resource"); + dev_err(dev, "Failed to get memory resource\n"); return -ENODEV; } @@ -318,19 +427,33 @@ static int sti_uniperiph_cpu_dai_of(struct device_node *node, uni->irq = platform_get_irq(priv->pdev, 0); if (uni->irq < 0) { - dev_err(dev, "Failed to get IRQ resource"); + dev_err(dev, "Failed to get IRQ resource\n"); return -ENXIO; } + uni->type = dev_data->type; + + /* check if player should be configured for tdm */ + if (dev_data->type & SND_ST_UNIPERIF_TYPE_TDM) { + if (!of_property_read_string(node, "st,tdm-mode", &mode)) + uni->type = SND_ST_UNIPERIF_TYPE_TDM; + else + uni->type = SND_ST_UNIPERIF_TYPE_PCM; + } + dai_data->uni = uni; + dai_data->stream = dev_data->stream; - if (of_device_is_compatible(node, "st,sti-uni-player")) { - uni_player_init(priv->pdev, uni); + if (priv->dai_data.stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = uni_player_init(priv->pdev, uni); stream = &dai->playback; } else { - uni_reader_init(priv->pdev, uni); + ret = uni_reader_init(priv->pdev, uni); stream = &dai->capture; } + if (ret < 0) + return ret; + dai->ops = uni->dai_ops; stream->stream_name = dai->name; @@ -376,12 +499,6 @@ static int sti_uniperiph_probe(struct platform_device *pdev) &dmaengine_pcm_config, 0); } -static const struct of_device_id snd_soc_sti_match[] = { - { .compatible = "st,sti-uni-player", }, - { .compatible = "st,sti-uni-reader", }, - {}, -}; - static struct platform_driver sti_uniperiph_driver = { .driver = { .name = "sti-uniperiph-dai", diff --git a/sound/soc/sti/uniperif.h b/sound/soc/sti/uniperif.h index eb9933c62ad6..d487dd2ef016 100644 --- a/sound/soc/sti/uniperif.h +++ b/sound/soc/sti/uniperif.h @@ -1220,16 +1220,16 @@ #define UNIPERIF_FIFO_FRAMES 4 /* FDMA trigger limit in frames */ #define UNIPERIF_TYPE_IS_HDMI(p) \ - ((p)->info->type == SND_ST_UNIPERIF_TYPE_HDMI) + ((p)->type == SND_ST_UNIPERIF_TYPE_HDMI) #define UNIPERIF_TYPE_IS_PCM(p) \ - ((p)->info->type == SND_ST_UNIPERIF_TYPE_PCM) + ((p)->type == SND_ST_UNIPERIF_TYPE_PCM) #define UNIPERIF_TYPE_IS_SPDIF(p) \ - ((p)->info->type == SND_ST_UNIPERIF_TYPE_SPDIF) + ((p)->type == SND_ST_UNIPERIF_TYPE_SPDIF) #define UNIPERIF_TYPE_IS_IEC958(p) \ (UNIPERIF_TYPE_IS_HDMI(p) || \ UNIPERIF_TYPE_IS_SPDIF(p)) #define UNIPERIF_TYPE_IS_TDM(p) \ - ((p)->info->type == SND_ST_UNIPERIF_TYPE_TDM) + ((p)->type == SND_ST_UNIPERIF_TYPE_TDM) /* * Uniperipheral IP revisions @@ -1249,11 +1249,11 @@ enum uniperif_version { }; enum uniperif_type { - SND_ST_UNIPERIF_TYPE_NONE, - SND_ST_UNIPERIF_TYPE_HDMI, - SND_ST_UNIPERIF_TYPE_PCM, - SND_ST_UNIPERIF_TYPE_SPDIF, - SND_ST_UNIPERIF_TYPE_TDM + SND_ST_UNIPERIF_TYPE_NONE = 0x00, + SND_ST_UNIPERIF_TYPE_HDMI = 0x01, + SND_ST_UNIPERIF_TYPE_PCM = 0x02, + SND_ST_UNIPERIF_TYPE_SPDIF = 0x04, + SND_ST_UNIPERIF_TYPE_TDM = 0x08 }; enum uniperif_state { @@ -1278,12 +1278,6 @@ enum uniperif_word_pos { WORD_MAX }; -struct uniperif_info { - int id; /* instance value of the uniperipheral IP */ - enum uniperif_type type; - int underflow_enabled; /* Underflow recovery mode */ -}; - struct uniperif_iec958_settings { enum uniperif_iec958_encoding_mode encoding_mode; struct snd_aes_iec958 iec958; @@ -1298,8 +1292,10 @@ struct dai_tdm_slot { struct uniperif { /* System information */ - struct uniperif_info *info; + enum uniperif_type type; + int underflow_enabled; /* Underflow recovery mode */ struct device *dev; + int id; /* instance value of the uniperipheral IP */ int ver; /* IP version, used by register access macros */ struct regmap_field *clk_sel; struct regmap_field *valid_sel; @@ -1401,6 +1397,8 @@ static inline int sti_uniperiph_get_unip_tdm_frame_size(struct uniperif *uni) return (uni->tdm_slot.slots * uni->tdm_slot.slot_width / 8); } +int sti_uniperiph_reset(struct uniperif *uni); + int sti_uniperiph_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width); diff --git a/sound/soc/sti/uniperif_player.c b/sound/soc/sti/uniperif_player.c index 1ac2db205a0d..60ae31a303ab 100644 --- a/sound/soc/sti/uniperif_player.c +++ b/sound/soc/sti/uniperif_player.c @@ -6,8 +6,6 @@ */ #include <linux/clk.h> -#include <linux/delay.h> -#include <linux/io.h> #include <linux/mfd/syscon.h> #include <sound/asoundef.h> @@ -55,25 +53,6 @@ static const struct snd_pcm_hardware uni_player_pcm_hw = { .buffer_bytes_max = 256 * PAGE_SIZE }; -static inline int reset_player(struct uniperif *player) -{ - int count = 10; - - if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) { - while (GET_UNIPERIF_SOFT_RST_SOFT_RST(player) && count) { - udelay(5); - count--; - } - } - - if (!count) { - dev_err(player->dev, "Failed to reset uniperif"); - return -EIO; - } - - return 0; -} - /* * uni_player_irq_handler * In case of error audio stream is stopped; stop action is protected via PCM @@ -97,10 +76,10 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id) /* Check for fifo error (underrun) */ if (unlikely(status & UNIPERIF_ITS_FIFO_ERROR_MASK(player))) { - dev_err(player->dev, "FIFO underflow error detected"); + dev_err(player->dev, "FIFO underflow error detected\n"); /* Interrupt is just for information when underflow recovery */ - if (player->info->underflow_enabled) { + if (player->underflow_enabled) { /* Update state to underflow */ player->state = UNIPERIF_STATE_UNDERFLOW; @@ -119,7 +98,7 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id) /* Check for dma error (overrun) */ if (unlikely(status & UNIPERIF_ITS_DMA_ERROR_MASK(player))) { - dev_err(player->dev, "DMA error detected"); + dev_err(player->dev, "DMA error detected\n"); /* Disable interrupt so doesn't continually fire */ SET_UNIPERIF_ITM_BCLR_DMA_ERROR(player); @@ -134,12 +113,15 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id) /* Check for underflow recovery done */ if (unlikely(status & UNIPERIF_ITM_UNDERFLOW_REC_DONE_MASK(player))) { - if (!player->info->underflow_enabled) { - dev_err(player->dev, "unexpected Underflow recovering"); + if (!player->underflow_enabled) { + dev_err(player->dev, + "unexpected Underflow recovering\n"); return -EPERM; } /* Read the underflow recovery duration */ tmp = GET_UNIPERIF_STATUS_1_UNDERFLOW_DURATION(player); + dev_dbg(player->dev, "Underflow recovered (%d LR clocks max)\n", + tmp); /* Clear the underflow recovery duration */ SET_UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION(player); @@ -153,7 +135,7 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id) /* Check if underflow recovery failed */ if (unlikely(status & UNIPERIF_ITM_UNDERFLOW_REC_FAILED_MASK(player))) { - dev_err(player->dev, "Underflow recovery failed"); + dev_err(player->dev, "Underflow recovery failed\n"); /* Stop the player */ snd_pcm_stream_lock(player->substream); @@ -336,7 +318,7 @@ static int uni_player_prepare_iec958(struct uniperif *player, /* Oversampling must be multiple of 128 as iec958 frame is 32-bits */ if ((clk_div % 128) || (clk_div <= 0)) { - dev_err(player->dev, "%s: invalid clk_div %d", + dev_err(player->dev, "%s: invalid clk_div %d\n", __func__, clk_div); return -EINVAL; } @@ -359,7 +341,7 @@ static int uni_player_prepare_iec958(struct uniperif *player, SET_UNIPERIF_I2S_FMT_DATA_SIZE_24(player); break; default: - dev_err(player->dev, "format not supported"); + dev_err(player->dev, "format not supported\n"); return -EINVAL; } @@ -448,12 +430,12 @@ static int uni_player_prepare_pcm(struct uniperif *player, * for 16 bits must be a multiple of 64 */ if ((slot_width == 32) && (clk_div % 128)) { - dev_err(player->dev, "%s: invalid clk_div", __func__); + dev_err(player->dev, "%s: invalid clk_div\n", __func__); return -EINVAL; } if ((slot_width == 16) && (clk_div % 64)) { - dev_err(player->dev, "%s: invalid clk_div", __func__); + dev_err(player->dev, "%s: invalid clk_div\n", __func__); return -EINVAL; } @@ -471,7 +453,7 @@ static int uni_player_prepare_pcm(struct uniperif *player, SET_UNIPERIF_I2S_FMT_DATA_SIZE_16(player); break; default: - dev_err(player->dev, "subframe format not supported"); + dev_err(player->dev, "subframe format not supported\n"); return -EINVAL; } @@ -491,7 +473,7 @@ static int uni_player_prepare_pcm(struct uniperif *player, break; default: - dev_err(player->dev, "format not supported"); + dev_err(player->dev, "format not supported\n"); return -EINVAL; } @@ -504,7 +486,7 @@ static int uni_player_prepare_pcm(struct uniperif *player, /* Number of channelsmust be even*/ if ((runtime->channels % 2) || (runtime->channels < 2) || (runtime->channels > 10)) { - dev_err(player->dev, "%s: invalid nb of channels", __func__); + dev_err(player->dev, "%s: invalid nb of channels\n", __func__); return -EINVAL; } @@ -614,7 +596,11 @@ static int uni_player_ctl_iec958_put(struct snd_kcontrol *kcontrol, iec958->status[3] = ucontrol->value.iec958.status[3]; mutex_unlock(&player->ctrl_lock); - uni_player_set_channel_status(player, NULL); + if (player->substream && player->substream->runtime) + uni_player_set_channel_status(player, + player->substream->runtime); + else + uni_player_set_channel_status(player, NULL); return 0; } @@ -758,13 +744,13 @@ static int uni_player_prepare(struct snd_pcm_substream *substream, /* The player should be stopped */ if (player->state != UNIPERIF_STATE_STOPPED) { - dev_err(player->dev, "%s: invalid player state %d", __func__, + dev_err(player->dev, "%s: invalid player state %d\n", __func__, player->state); return -EINVAL; } /* Calculate transfer size (in fifo cells and bytes) for frame count */ - if (player->info->type == SND_ST_UNIPERIF_TYPE_TDM) { + if (player->type == SND_ST_UNIPERIF_TYPE_TDM) { /* transfer size = user frame size (in 32 bits FIFO cell) */ transfer_size = sti_uniperiph_get_user_frame_size(runtime) / 4; @@ -787,14 +773,15 @@ static int uni_player_prepare(struct snd_pcm_substream *substream, /* Trigger limit must be an even number */ if ((!trigger_limit % 2) || (trigger_limit != 1 && transfer_size % 2) || (trigger_limit > UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(player))) { - dev_err(player->dev, "invalid trigger limit %d", trigger_limit); + dev_err(player->dev, "invalid trigger limit %d\n", + trigger_limit); return -EINVAL; } SET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT(player, trigger_limit); /* Uniperipheral setup depends on player type */ - switch (player->info->type) { + switch (player->type) { case SND_ST_UNIPERIF_TYPE_HDMI: ret = uni_player_prepare_iec958(player, runtime); break; @@ -808,7 +795,7 @@ static int uni_player_prepare(struct snd_pcm_substream *substream, ret = uni_player_prepare_tdm(player, runtime); break; default: - dev_err(player->dev, "invalid player type"); + dev_err(player->dev, "invalid player type\n"); return -EINVAL; } @@ -848,16 +835,14 @@ static int uni_player_prepare(struct snd_pcm_substream *substream, SET_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(player); break; default: - dev_err(player->dev, "format not supported"); + dev_err(player->dev, "format not supported\n"); return -EINVAL; } SET_UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ(player, 0); - /* Reset uniperipheral player */ - SET_UNIPERIF_SOFT_RST_SOFT_RST(player); - return reset_player(player); + return sti_uniperiph_reset(player); } static int uni_player_start(struct uniperif *player) @@ -866,13 +851,13 @@ static int uni_player_start(struct uniperif *player) /* The player should be stopped */ if (player->state != UNIPERIF_STATE_STOPPED) { - dev_err(player->dev, "%s: invalid player state", __func__); + dev_err(player->dev, "%s: invalid player state\n", __func__); return -EINVAL; } ret = clk_prepare_enable(player->clk); if (ret) { - dev_err(player->dev, "%s: Failed to enable clock", __func__); + dev_err(player->dev, "%s: Failed to enable clock\n", __func__); return ret; } @@ -884,17 +869,16 @@ static int uni_player_start(struct uniperif *player) SET_UNIPERIF_ITM_BSET_FIFO_ERROR(player); /* Enable underflow recovery interrupts */ - if (player->info->underflow_enabled) { + if (player->underflow_enabled) { SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE(player); SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED(player); } - /* Reset uniperipheral player */ - SET_UNIPERIF_SOFT_RST_SOFT_RST(player); - - ret = reset_player(player); - if (ret < 0) + ret = sti_uniperiph_reset(player); + if (ret < 0) { + clk_disable_unprepare(player->clk); return ret; + } /* * Does not use IEC61937 features of the uniperipheral hardware. @@ -932,17 +916,14 @@ static int uni_player_stop(struct uniperif *player) /* The player should not be in stopped state */ if (player->state == UNIPERIF_STATE_STOPPED) { - dev_err(player->dev, "%s: invalid player state", __func__); + dev_err(player->dev, "%s: invalid player state\n", __func__); return -EINVAL; } /* Turn the player off */ SET_UNIPERIF_CTRL_OPERATION_OFF(player); - /* Soft reset the player */ - SET_UNIPERIF_SOFT_RST_SOFT_RST(player); - - ret = reset_player(player); + ret = sti_uniperiph_reset(player); if (ret < 0) return ret; @@ -967,7 +948,7 @@ int uni_player_resume(struct uniperif *player) ret = regmap_field_write(player->clk_sel, 1); if (ret) { dev_err(player->dev, - "%s: Failed to select freq synth clock", + "%s: Failed to select freq synth clock\n", __func__); return ret; } @@ -1021,8 +1002,8 @@ static int uni_player_parse_dt_audio_glue(struct platform_device *pdev, struct reg_field regfield[2] = { /* PCM_CLK_SEL */ REG_FIELD(SYS_CFG_AUDIO_GLUE, - 8 + player->info->id, - 8 + player->info->id), + 8 + player->id, + 8 + player->id), /* PCMP_VALID_SEL */ REG_FIELD(SYS_CFG_AUDIO_GLUE, 0, 1) }; @@ -1040,60 +1021,6 @@ static int uni_player_parse_dt_audio_glue(struct platform_device *pdev, return 0; } -static int uni_player_parse_dt(struct platform_device *pdev, - struct uniperif *player) -{ - struct uniperif_info *info; - struct device *dev = &pdev->dev; - struct device_node *pnode = pdev->dev.of_node; - const char *mode; - - /* Allocate memory for the info structure */ - info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - if (of_property_read_u32(pnode, "st,version", &player->ver) || - player->ver == SND_ST_UNIPERIF_VERSION_UNKNOWN) { - dev_err(dev, "Unknown uniperipheral version "); - return -EINVAL; - } - /* Underflow recovery is only supported on later ip revisions */ - if (player->ver >= SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) - info->underflow_enabled = 1; - - if (of_property_read_u32(pnode, "st,uniperiph-id", &info->id)) { - dev_err(dev, "uniperipheral id not defined"); - return -EINVAL; - } - - /* Read the device mode property */ - if (of_property_read_string(pnode, "st,mode", &mode)) { - dev_err(dev, "uniperipheral mode not defined"); - return -EINVAL; - } - - if (strcasecmp(mode, "hdmi") == 0) - info->type = SND_ST_UNIPERIF_TYPE_HDMI; - else if (strcasecmp(mode, "pcm") == 0) - info->type = SND_ST_UNIPERIF_TYPE_PCM; - else if (strcasecmp(mode, "spdif") == 0) - info->type = SND_ST_UNIPERIF_TYPE_SPDIF; - else if (strcasecmp(mode, "tdm") == 0) - info->type = SND_ST_UNIPERIF_TYPE_TDM; - else - info->type = SND_ST_UNIPERIF_TYPE_NONE; - - /* Save the info structure */ - player->info = info; - - /* Get PCM_CLK_SEL & PCMP_VALID_SEL from audio-glue-ctrl SoC reg */ - if (uni_player_parse_dt_audio_glue(pdev, player)) - return -EINVAL; - - return 0; -} - static const struct snd_soc_dai_ops uni_player_dai_ops = { .startup = uni_player_startup, .shutdown = uni_player_shutdown, @@ -1114,13 +1041,18 @@ int uni_player_init(struct platform_device *pdev, player->state = UNIPERIF_STATE_STOPPED; player->dai_ops = &uni_player_dai_ops; - ret = uni_player_parse_dt(pdev, player); + /* Get PCM_CLK_SEL & PCMP_VALID_SEL from audio-glue-ctrl SoC reg */ + ret = uni_player_parse_dt_audio_glue(pdev, player); if (ret < 0) { - dev_err(player->dev, "Failed to parse DeviceTree"); + dev_err(player->dev, "Failed to parse DeviceTree\n"); return ret; } + /* Underflow recovery is only supported on later ip revisions */ + if (player->ver >= SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) + player->underflow_enabled = 1; + if (UNIPERIF_TYPE_IS_TDM(player)) player->hw = &uni_tdm_hw; else @@ -1128,15 +1060,17 @@ int uni_player_init(struct platform_device *pdev, /* Get uniperif resource */ player->clk = of_clk_get(pdev->dev.of_node, 0); - if (IS_ERR(player->clk)) + if (IS_ERR(player->clk)) { + dev_err(player->dev, "Failed to get clock\n"); ret = PTR_ERR(player->clk); + } /* Select the frequency synthesizer clock */ if (player->clk_sel) { ret = regmap_field_write(player->clk_sel, 1); if (ret) { dev_err(player->dev, - "%s: Failed to select freq synth clock", + "%s: Failed to select freq synth clock\n", __func__); return ret; } @@ -1144,11 +1078,11 @@ int uni_player_init(struct platform_device *pdev, /* connect to I2S/TDM TX bus */ if (player->valid_sel && - (player->info->id == UNIPERIF_PLAYER_I2S_OUT)) { - ret = regmap_field_write(player->valid_sel, player->info->id); + (player->id == UNIPERIF_PLAYER_I2S_OUT)) { + ret = regmap_field_write(player->valid_sel, player->id); if (ret) { dev_err(player->dev, - "%s: unable to connect to tdm bus", __func__); + "%s: unable to connect to tdm bus\n", __func__); return ret; } } @@ -1156,8 +1090,10 @@ int uni_player_init(struct platform_device *pdev, ret = devm_request_irq(&pdev->dev, player->irq, uni_player_irq_handler, IRQF_SHARED, dev_name(&pdev->dev), player); - if (ret < 0) + if (ret < 0) { + dev_err(player->dev, "unable to request IRQ %d\n", player->irq); return ret; + } mutex_init(&player->ctrl_lock); diff --git a/sound/soc/sti/uniperif_reader.c b/sound/soc/sti/uniperif_reader.c index eb74a328c928..5992c6ab3833 100644 --- a/sound/soc/sti/uniperif_reader.c +++ b/sound/soc/sti/uniperif_reader.c @@ -5,14 +5,11 @@ * License terms: GNU General Public License (GPL), version 2 */ -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/io.h> - #include <sound/soc.h> #include "uniperif.h" +#define UNIPERIF_READER_I2S_IN 0 /* reader id connected to I2S/TDM TX bus */ /* * Note: snd_pcm_hardware is linked to DMA controller but is declared here to * integrate unireader capability in term of rate and supported channels @@ -51,7 +48,7 @@ static irqreturn_t uni_reader_irq_handler(int irq, void *dev_id) if (reader->state == UNIPERIF_STATE_STOPPED) { /* Unexpected IRQ: do nothing */ - dev_warn(reader->dev, "unexpected IRQ "); + dev_warn(reader->dev, "unexpected IRQ\n"); return IRQ_HANDLED; } @@ -61,7 +58,7 @@ static irqreturn_t uni_reader_irq_handler(int irq, void *dev_id) /* Check for fifo overflow error */ if (unlikely(status & UNIPERIF_ITS_FIFO_ERROR_MASK(reader))) { - dev_err(reader->dev, "FIFO error detected"); + dev_err(reader->dev, "FIFO error detected\n"); snd_pcm_stream_lock(reader->substream); snd_pcm_stop(reader->substream, SNDRV_PCM_STATE_XRUN); @@ -104,7 +101,7 @@ static int uni_reader_prepare_pcm(struct snd_pcm_runtime *runtime, SET_UNIPERIF_I2S_FMT_DATA_SIZE_16(reader); break; default: - dev_err(reader->dev, "subframe format not supported"); + dev_err(reader->dev, "subframe format not supported\n"); return -EINVAL; } @@ -124,14 +121,14 @@ static int uni_reader_prepare_pcm(struct snd_pcm_runtime *runtime, break; default: - dev_err(reader->dev, "format not supported"); + dev_err(reader->dev, "format not supported\n"); return -EINVAL; } /* Number of channels must be even */ if ((runtime->channels % 2) || (runtime->channels < 2) || (runtime->channels > 10)) { - dev_err(reader->dev, "%s: invalid nb of channels", __func__); + dev_err(reader->dev, "%s: invalid nb of channels\n", __func__); return -EINVAL; } @@ -185,17 +182,16 @@ static int uni_reader_prepare(struct snd_pcm_substream *substream, struct uniperif *reader = priv->dai_data.uni; struct snd_pcm_runtime *runtime = substream->runtime; int transfer_size, trigger_limit, ret; - int count = 10; /* The reader should be stopped */ if (reader->state != UNIPERIF_STATE_STOPPED) { - dev_err(reader->dev, "%s: invalid reader state %d", __func__, + dev_err(reader->dev, "%s: invalid reader state %d\n", __func__, reader->state); return -EINVAL; } /* Calculate transfer size (in fifo cells and bytes) for frame count */ - if (reader->info->type == SND_ST_UNIPERIF_TYPE_TDM) { + if (reader->type == SND_ST_UNIPERIF_TYPE_TDM) { /* transfer size = unip frame size (in 32 bits FIFO cell) */ transfer_size = sti_uniperiph_get_user_frame_size(runtime) / 4; @@ -218,7 +214,8 @@ static int uni_reader_prepare(struct snd_pcm_substream *substream, if ((!trigger_limit % 2) || (trigger_limit != 1 && transfer_size % 2) || (trigger_limit > UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(reader))) { - dev_err(reader->dev, "invalid trigger limit %d", trigger_limit); + dev_err(reader->dev, "invalid trigger limit %d\n", + trigger_limit); return -EINVAL; } @@ -245,7 +242,7 @@ static int uni_reader_prepare(struct snd_pcm_substream *substream, SET_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(reader); break; default: - dev_err(reader->dev, "format not supported"); + dev_err(reader->dev, "format not supported\n"); return -EINVAL; } @@ -280,31 +277,20 @@ static int uni_reader_prepare(struct snd_pcm_substream *substream, SET_UNIPERIF_ITM_BSET_MEM_BLK_READ(reader); /* Enable underflow recovery interrupts */ - if (reader->info->underflow_enabled) { + if (reader->underflow_enabled) { SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE(reader); SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED(reader); } /* Reset uniperipheral reader */ - SET_UNIPERIF_SOFT_RST_SOFT_RST(reader); - - while (GET_UNIPERIF_SOFT_RST_SOFT_RST(reader)) { - udelay(5); - count--; - } - if (!count) { - dev_err(reader->dev, "Failed to reset uniperif"); - return -EIO; - } - - return 0; + return sti_uniperiph_reset(reader); } static int uni_reader_start(struct uniperif *reader) { /* The reader should be stopped */ if (reader->state != UNIPERIF_STATE_STOPPED) { - dev_err(reader->dev, "%s: invalid reader state", __func__); + dev_err(reader->dev, "%s: invalid reader state\n", __func__); return -EINVAL; } @@ -324,7 +310,7 @@ static int uni_reader_stop(struct uniperif *reader) { /* The reader should not be in stopped state */ if (reader->state == UNIPERIF_STATE_STOPPED) { - dev_err(reader->dev, "%s: invalid reader state", __func__); + dev_err(reader->dev, "%s: invalid reader state\n", __func__); return -EINVAL; } @@ -394,41 +380,6 @@ static void uni_reader_shutdown(struct snd_pcm_substream *substream, } } -static int uni_reader_parse_dt(struct platform_device *pdev, - struct uniperif *reader) -{ - struct uniperif_info *info; - struct device_node *node = pdev->dev.of_node; - const char *mode; - - /* Allocate memory for the info structure */ - info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - if (of_property_read_u32(node, "st,version", &reader->ver) || - reader->ver == SND_ST_UNIPERIF_VERSION_UNKNOWN) { - dev_err(&pdev->dev, "Unknown uniperipheral version "); - return -EINVAL; - } - - /* Read the device mode property */ - if (of_property_read_string(node, "st,mode", &mode)) { - dev_err(&pdev->dev, "uniperipheral mode not defined"); - return -EINVAL; - } - - if (strcasecmp(mode, "tdm") == 0) - info->type = SND_ST_UNIPERIF_TYPE_TDM; - else - info->type = SND_ST_UNIPERIF_TYPE_PCM; - - /* Save the info structure */ - reader->info = info; - - return 0; -} - static const struct snd_soc_dai_ops uni_reader_dai_ops = { .startup = uni_reader_startup, .shutdown = uni_reader_shutdown, @@ -448,12 +399,6 @@ int uni_reader_init(struct platform_device *pdev, reader->state = UNIPERIF_STATE_STOPPED; reader->dai_ops = &uni_reader_dai_ops; - ret = uni_reader_parse_dt(pdev, reader); - if (ret < 0) { - dev_err(reader->dev, "Failed to parse DeviceTree"); - return ret; - } - if (UNIPERIF_TYPE_IS_TDM(reader)) reader->hw = &uni_tdm_hw; else @@ -463,7 +408,7 @@ int uni_reader_init(struct platform_device *pdev, uni_reader_irq_handler, IRQF_SHARED, dev_name(&pdev->dev), reader); if (ret < 0) { - dev_err(&pdev->dev, "Failed to request IRQ"); + dev_err(&pdev->dev, "Failed to request IRQ\n"); return -EBUSY; } diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig index 2a954bd01fd8..6c344e16aca4 100644 --- a/sound/soc/sunxi/Kconfig +++ b/sound/soc/sunxi/Kconfig @@ -1,4 +1,5 @@ menu "Allwinner SoC Audio support" + depends on ARCH_SUNXI || COMPILE_TEST config SND_SUN4I_CODEC tristate "Allwinner A10 Codec Support" @@ -8,6 +9,14 @@ config SND_SUN4I_CODEC Select Y or M to add support for the Codec embedded in the Allwinner A10 and affiliated SoCs. +config SND_SUN8I_CODEC_ANALOG + tristate "Allwinner sun8i Codec Analog Controls Support" + depends on MACH_SUN8I || COMPILE_TEST + select REGMAP + help + Say Y or M if you want to add support for the analog controls for + the codec embedded in newer Allwinner SoCs. + config SND_SUN4I_I2S tristate "Allwinner A10 I2S Support" select SND_SOC_GENERIC_DMAENGINE_PCM diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile index 604c7b842837..241c0df9ca0c 100644 --- a/sound/soc/sunxi/Makefile +++ b/sound/soc/sunxi/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o obj-$(CONFIG_SND_SUN4I_I2S) += sun4i-i2s.o obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o +obj-$(CONFIG_SND_SUN8I_CODEC_ANALOG) += sun8i-codec-analog.o diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index 44f170c73b06..848af01692a0 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -3,6 +3,7 @@ * Copyright 2014 Jon Smirl <jonsmirl@gmail.com> * Copyright 2015 Maxime Ripard <maxime.ripard@free-electrons.com> * Copyright 2015 Adam Sampson <ats@offog.org> + * Copyright 2016 Chen-Yu Tsai <wens@csie.org> * * Based on the Allwinner SDK driver, released under the GPL. * @@ -24,10 +25,12 @@ #include <linux/delay.h> #include <linux/slab.h> #include <linux/of.h> -#include <linux/of_platform.h> #include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/of_platform.h> #include <linux/clk.h> #include <linux/regmap.h> +#include <linux/reset.h> #include <linux/gpio/consumer.h> #include <sound/core.h> @@ -38,7 +41,7 @@ #include <sound/initval.h> #include <sound/dmaengine_pcm.h> -/* Codec DAC register offsets and bit fields */ +/* Codec DAC digital controls and FIFO registers */ #define SUN4I_CODEC_DAC_DPC (0x00) #define SUN4I_CODEC_DAC_DPC_EN_DA (31) #define SUN4I_CODEC_DAC_DPC_DVOL (12) @@ -55,6 +58,8 @@ #define SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH (0) #define SUN4I_CODEC_DAC_FIFOS (0x08) #define SUN4I_CODEC_DAC_TXDATA (0x0c) + +/* Codec DAC side analog signal controls */ #define SUN4I_CODEC_DAC_ACTL (0x10) #define SUN4I_CODEC_DAC_ACTL_DACAENR (31) #define SUN4I_CODEC_DAC_ACTL_DACAENL (30) @@ -69,7 +74,7 @@ #define SUN4I_CODEC_DAC_TUNE (0x14) #define SUN4I_CODEC_DAC_DEBUG (0x18) -/* Codec ADC register offsets and bit fields */ +/* Codec ADC digital controls and FIFO registers */ #define SUN4I_CODEC_ADC_FIFOC (0x1c) #define SUN4I_CODEC_ADC_FIFOC_ADC_FS (29) #define SUN4I_CODEC_ADC_FIFOC_EN_AD (28) @@ -81,6 +86,8 @@ #define SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH (0) #define SUN4I_CODEC_ADC_FIFOS (0x20) #define SUN4I_CODEC_ADC_RXDATA (0x24) + +/* Codec ADC side analog signal controls */ #define SUN4I_CODEC_ADC_ACTL (0x28) #define SUN4I_CODEC_ADC_ACTL_ADC_R_EN (31) #define SUN4I_CODEC_ADC_ACTL_ADC_L_EN (30) @@ -93,19 +100,141 @@ #define SUN4I_CODEC_ADC_ACTL_DDE (3) #define SUN4I_CODEC_ADC_DEBUG (0x2c) -/* Other various ADC registers */ +/* FIFO counters */ #define SUN4I_CODEC_DAC_TXCNT (0x30) #define SUN4I_CODEC_ADC_RXCNT (0x34) -#define SUN4I_CODEC_AC_SYS_VERI (0x38) -#define SUN4I_CODEC_AC_MIC_PHONE_CAL (0x3c) + +/* Calibration register (sun7i only) */ +#define SUN7I_CODEC_AC_DAC_CAL (0x38) + +/* Microphone controls (sun7i only) */ +#define SUN7I_CODEC_AC_MIC_PHONE_CAL (0x3c) + +/* + * sun6i specific registers + * + * sun6i shares the same digital control and FIFO registers as sun4i, + * but only the DAC digital controls are at the same offset. The others + * have been moved around to accommodate extra analog controls. + */ + +/* Codec DAC digital controls and FIFO registers */ +#define SUN6I_CODEC_ADC_FIFOC (0x10) +#define SUN6I_CODEC_ADC_FIFOC_EN_AD (28) +#define SUN6I_CODEC_ADC_FIFOS (0x14) +#define SUN6I_CODEC_ADC_RXDATA (0x18) + +/* Output mixer and gain controls */ +#define SUN6I_CODEC_OM_DACA_CTRL (0x20) +#define SUN6I_CODEC_OM_DACA_CTRL_DACAREN (31) +#define SUN6I_CODEC_OM_DACA_CTRL_DACALEN (30) +#define SUN6I_CODEC_OM_DACA_CTRL_RMIXEN (29) +#define SUN6I_CODEC_OM_DACA_CTRL_LMIXEN (28) +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC1 (23) +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2 (22) +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONE (21) +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONEP (20) +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR (19) +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR (18) +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL (17) +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC1 (16) +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC2 (15) +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONE (14) +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONEN (13) +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL (12) +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL (11) +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR (10) +#define SUN6I_CODEC_OM_DACA_CTRL_RHPIS (9) +#define SUN6I_CODEC_OM_DACA_CTRL_LHPIS (8) +#define SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE (7) +#define SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE (6) +#define SUN6I_CODEC_OM_DACA_CTRL_HPVOL (0) +#define SUN6I_CODEC_OM_PA_CTRL (0x24) +#define SUN6I_CODEC_OM_PA_CTRL_HPPAEN (31) +#define SUN6I_CODEC_OM_PA_CTRL_HPCOM_CTL (29) +#define SUN6I_CODEC_OM_PA_CTRL_COMPTEN (28) +#define SUN6I_CODEC_OM_PA_CTRL_MIC1G (15) +#define SUN6I_CODEC_OM_PA_CTRL_MIC2G (12) +#define SUN6I_CODEC_OM_PA_CTRL_LINEING (9) +#define SUN6I_CODEC_OM_PA_CTRL_PHONEG (6) +#define SUN6I_CODEC_OM_PA_CTRL_PHONEPG (3) +#define SUN6I_CODEC_OM_PA_CTRL_PHONENG (0) + +/* Microphone, line out and phone out controls */ +#define SUN6I_CODEC_MIC_CTRL (0x28) +#define SUN6I_CODEC_MIC_CTRL_HBIASEN (31) +#define SUN6I_CODEC_MIC_CTRL_MBIASEN (30) +#define SUN6I_CODEC_MIC_CTRL_MIC1AMPEN (28) +#define SUN6I_CODEC_MIC_CTRL_MIC1BOOST (25) +#define SUN6I_CODEC_MIC_CTRL_MIC2AMPEN (24) +#define SUN6I_CODEC_MIC_CTRL_MIC2BOOST (21) +#define SUN6I_CODEC_MIC_CTRL_MIC2SLT (20) +#define SUN6I_CODEC_MIC_CTRL_LINEOUTLEN (19) +#define SUN6I_CODEC_MIC_CTRL_LINEOUTREN (18) +#define SUN6I_CODEC_MIC_CTRL_LINEOUTLSRC (17) +#define SUN6I_CODEC_MIC_CTRL_LINEOUTRSRC (16) +#define SUN6I_CODEC_MIC_CTRL_LINEOUTVC (11) +#define SUN6I_CODEC_MIC_CTRL_PHONEPREG (8) + +/* ADC mixer controls */ +#define SUN6I_CODEC_ADC_ACTL (0x2c) +#define SUN6I_CODEC_ADC_ACTL_ADCREN (31) +#define SUN6I_CODEC_ADC_ACTL_ADCLEN (30) +#define SUN6I_CODEC_ADC_ACTL_ADCRG (27) +#define SUN6I_CODEC_ADC_ACTL_ADCLG (24) +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC1 (13) +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC2 (12) +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONE (11) +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONEP (10) +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_LINEINR (9) +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXR (8) +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXL (7) +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC1 (6) +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC2 (5) +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONE (4) +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONEN (3) +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_LINEINL (2) +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXL (1) +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXR (0) + +/* Analog performance tuning controls */ +#define SUN6I_CODEC_ADDA_TUNE (0x30) + +/* Calibration controls */ +#define SUN6I_CODEC_CALIBRATION (0x34) + +/* FIFO counters */ +#define SUN6I_CODEC_DAC_TXCNT (0x40) +#define SUN6I_CODEC_ADC_RXCNT (0x44) + +/* headset jack detection and button support registers */ +#define SUN6I_CODEC_HMIC_CTL (0x50) +#define SUN6I_CODEC_HMIC_DATA (0x54) + +/* TODO sun6i DAP (Digital Audio Processing) bits */ + +/* FIFO counters moved on A23 */ +#define SUN8I_A23_CODEC_DAC_TXCNT (0x1c) +#define SUN8I_A23_CODEC_ADC_RXCNT (0x20) + +/* TX FIFO moved on H3 */ +#define SUN8I_H3_CODEC_DAC_TXDATA (0x20) +#define SUN8I_H3_CODEC_DAC_DBG (0x48) +#define SUN8I_H3_CODEC_ADC_DBG (0x4c) + +/* TODO H3 DAP (Digital Audio Processing) bits */ struct sun4i_codec { struct device *dev; struct regmap *regmap; struct clk *clk_apb; struct clk *clk_module; + struct reset_control *rst; struct gpio_desc *gpio_pa; + /* ADC_FIFOC register is at different offset on different SoCs */ + struct regmap_field *reg_adc_fifoc; + struct snd_dmaengine_dai_dma_data capture_dma_data; struct snd_dmaengine_dai_dma_data playback_dma_data; }; @@ -134,16 +263,16 @@ static void sun4i_codec_stop_playback(struct sun4i_codec *scodec) static void sun4i_codec_start_capture(struct sun4i_codec *scodec) { /* Enable ADC DRQ */ - regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, - BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN), - BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN)); + regmap_field_update_bits(scodec->reg_adc_fifoc, + BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN), + BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN)); } static void sun4i_codec_stop_capture(struct sun4i_codec *scodec) { /* Disable ADC DRQ */ - regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, - BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN), 0); + regmap_field_update_bits(scodec->reg_adc_fifoc, + BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN), 0); } static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd, @@ -186,24 +315,29 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream, /* Flush RX FIFO */ - regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, - BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH), - BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH)); + regmap_field_update_bits(scodec->reg_adc_fifoc, + BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH), + BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH)); /* Set RX FIFO trigger level */ - regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, - 0xf << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL, - 0x7 << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL); + regmap_field_update_bits(scodec->reg_adc_fifoc, + 0xf << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL, + 0x7 << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL); /* * FIXME: Undocumented in the datasheet, but * Allwinner's code mentions that it is related * related to microphone gain */ - regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL, - 0x3 << 25, - 0x1 << 25); + if (of_device_is_compatible(scodec->dev->of_node, + "allwinner,sun4i-a10-codec") || + of_device_is_compatible(scodec->dev->of_node, + "allwinner,sun7i-a20-codec")) { + regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL, + 0x3 << 25, + 0x1 << 25); + } if (of_device_is_compatible(scodec->dev->of_node, "allwinner,sun7i-a20-codec")) @@ -213,9 +347,9 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream, 0x1 << 8); /* Fill most significant bits with valid data MSB */ - regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, - BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE), - BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE)); + regmap_field_update_bits(scodec->reg_adc_fifoc, + BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE), + BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE)); return 0; } @@ -342,18 +476,19 @@ static int sun4i_codec_hw_params_capture(struct sun4i_codec *scodec, unsigned int hwrate) { /* Set ADC sample rate */ - regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, - 7 << SUN4I_CODEC_ADC_FIFOC_ADC_FS, - hwrate << SUN4I_CODEC_ADC_FIFOC_ADC_FS); + regmap_field_update_bits(scodec->reg_adc_fifoc, + 7 << SUN4I_CODEC_ADC_FIFOC_ADC_FS, + hwrate << SUN4I_CODEC_ADC_FIFOC_ADC_FS); /* Set the number of channels we want to use */ if (params_channels(params) == 1) - regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, - BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN), - BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN)); + regmap_field_update_bits(scodec->reg_adc_fifoc, + BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN), + BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN)); else - regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, - BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN), 0); + regmap_field_update_bits(scodec->reg_adc_fifoc, + BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN), + 0); return 0; } @@ -502,14 +637,14 @@ static struct snd_soc_dai_driver sun4i_codec_dai = { }, }; -/*** Codec ***/ +/*** sun4i Codec ***/ static const struct snd_kcontrol_new sun4i_codec_pa_mute = SOC_DAPM_SINGLE("Switch", SUN4I_CODEC_DAC_ACTL, SUN4I_CODEC_DAC_ACTL_PA_MUTE, 1, 0); static DECLARE_TLV_DB_SCALE(sun4i_codec_pa_volume_scale, -6300, 100, 1); -static const struct snd_kcontrol_new sun4i_codec_widgets[] = { +static const struct snd_kcontrol_new sun4i_codec_controls[] = { SOC_SINGLE_TLV("Power Amplifier Volume", SUN4I_CODEC_DAC_ACTL, SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0, sun4i_codec_pa_volume_scale), @@ -628,12 +763,345 @@ static const struct snd_soc_dapm_route sun4i_codec_codec_dapm_routes[] = { }; static struct snd_soc_codec_driver sun4i_codec_codec = { - .controls = sun4i_codec_widgets, - .num_controls = ARRAY_SIZE(sun4i_codec_widgets), - .dapm_widgets = sun4i_codec_codec_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_codec_dapm_widgets), - .dapm_routes = sun4i_codec_codec_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(sun4i_codec_codec_dapm_routes), + .component_driver = { + .controls = sun4i_codec_controls, + .num_controls = ARRAY_SIZE(sun4i_codec_controls), + .dapm_widgets = sun4i_codec_codec_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_codec_dapm_widgets), + .dapm_routes = sun4i_codec_codec_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(sun4i_codec_codec_dapm_routes), + }, +}; + +/*** sun6i Codec ***/ + +/* mixer controls */ +static const struct snd_kcontrol_new sun6i_codec_mixer_controls[] = { + SOC_DAPM_DOUBLE("DAC Playback Switch", + SUN6I_CODEC_OM_DACA_CTRL, + SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL, + SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR, 1, 0), + SOC_DAPM_DOUBLE("DAC Reversed Playback Switch", + SUN6I_CODEC_OM_DACA_CTRL, + SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR, + SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL, 1, 0), + SOC_DAPM_DOUBLE("Line In Playback Switch", + SUN6I_CODEC_OM_DACA_CTRL, + SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL, + SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR, 1, 0), + SOC_DAPM_DOUBLE("Mic1 Playback Switch", + SUN6I_CODEC_OM_DACA_CTRL, + SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC1, + SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC1, 1, 0), + SOC_DAPM_DOUBLE("Mic2 Playback Switch", + SUN6I_CODEC_OM_DACA_CTRL, + SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC2, + SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2, 1, 0), +}; + +/* ADC mixer controls */ +static const struct snd_kcontrol_new sun6i_codec_adc_mixer_controls[] = { + SOC_DAPM_DOUBLE("Mixer Capture Switch", + SUN6I_CODEC_ADC_ACTL, + SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXL, + SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXR, 1, 0), + SOC_DAPM_DOUBLE("Mixer Reversed Capture Switch", + SUN6I_CODEC_ADC_ACTL, + SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXR, + SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXL, 1, 0), + SOC_DAPM_DOUBLE("Line In Capture Switch", + SUN6I_CODEC_ADC_ACTL, + SUN6I_CODEC_ADC_ACTL_LADCMIX_LINEINL, + SUN6I_CODEC_ADC_ACTL_RADCMIX_LINEINR, 1, 0), + SOC_DAPM_DOUBLE("Mic1 Capture Switch", + SUN6I_CODEC_ADC_ACTL, + SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC1, + SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC1, 1, 0), + SOC_DAPM_DOUBLE("Mic2 Capture Switch", + SUN6I_CODEC_ADC_ACTL, + SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC2, + SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC2, 1, 0), +}; + +/* headphone controls */ +static const char * const sun6i_codec_hp_src_enum_text[] = { + "DAC", "Mixer", +}; + +static SOC_ENUM_DOUBLE_DECL(sun6i_codec_hp_src_enum, + SUN6I_CODEC_OM_DACA_CTRL, + SUN6I_CODEC_OM_DACA_CTRL_LHPIS, + SUN6I_CODEC_OM_DACA_CTRL_RHPIS, + sun6i_codec_hp_src_enum_text); + +static const struct snd_kcontrol_new sun6i_codec_hp_src[] = { + SOC_DAPM_ENUM("Headphone Source Playback Route", + sun6i_codec_hp_src_enum), +}; + +/* microphone controls */ +static const char * const sun6i_codec_mic2_src_enum_text[] = { + "Mic2", "Mic3", +}; + +static SOC_ENUM_SINGLE_DECL(sun6i_codec_mic2_src_enum, + SUN6I_CODEC_MIC_CTRL, + SUN6I_CODEC_MIC_CTRL_MIC2SLT, + sun6i_codec_mic2_src_enum_text); + +static const struct snd_kcontrol_new sun6i_codec_mic2_src[] = { + SOC_DAPM_ENUM("Mic2 Amplifier Source Route", + sun6i_codec_mic2_src_enum), +}; + +/* line out controls */ +static const char * const sun6i_codec_lineout_src_enum_text[] = { + "Stereo", "Mono Differential", +}; + +static SOC_ENUM_DOUBLE_DECL(sun6i_codec_lineout_src_enum, + SUN6I_CODEC_MIC_CTRL, + SUN6I_CODEC_MIC_CTRL_LINEOUTLSRC, + SUN6I_CODEC_MIC_CTRL_LINEOUTRSRC, + sun6i_codec_lineout_src_enum_text); + +static const struct snd_kcontrol_new sun6i_codec_lineout_src[] = { + SOC_DAPM_ENUM("Line Out Source Playback Route", + sun6i_codec_lineout_src_enum), +}; + +/* volume / mute controls */ +static const DECLARE_TLV_DB_SCALE(sun6i_codec_dvol_scale, -7308, 116, 0); +static const DECLARE_TLV_DB_SCALE(sun6i_codec_hp_vol_scale, -6300, 100, 1); +static const DECLARE_TLV_DB_SCALE(sun6i_codec_out_mixer_pregain_scale, + -450, 150, 0); +static const DECLARE_TLV_DB_RANGE(sun6i_codec_lineout_vol_scale, + 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), + 2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0), +); +static const DECLARE_TLV_DB_RANGE(sun6i_codec_mic_gain_scale, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0), +); + +static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = { + SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC, + SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1, + sun6i_codec_dvol_scale), + SOC_SINGLE_TLV("Headphone Playback Volume", + SUN6I_CODEC_OM_DACA_CTRL, + SUN6I_CODEC_OM_DACA_CTRL_HPVOL, 0x3f, 0, + sun6i_codec_hp_vol_scale), + SOC_SINGLE_TLV("Line Out Playback Volume", + SUN6I_CODEC_MIC_CTRL, + SUN6I_CODEC_MIC_CTRL_LINEOUTVC, 0x1f, 0, + sun6i_codec_lineout_vol_scale), + SOC_DOUBLE("Headphone Playback Switch", + SUN6I_CODEC_OM_DACA_CTRL, + SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE, + SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE, 1, 0), + SOC_DOUBLE("Line Out Playback Switch", + SUN6I_CODEC_MIC_CTRL, + SUN6I_CODEC_MIC_CTRL_LINEOUTLEN, + SUN6I_CODEC_MIC_CTRL_LINEOUTREN, 1, 0), + /* Mixer pre-gains */ + SOC_SINGLE_TLV("Line In Playback Volume", + SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_LINEING, + 0x7, 0, sun6i_codec_out_mixer_pregain_scale), + SOC_SINGLE_TLV("Mic1 Playback Volume", + SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_MIC1G, + 0x7, 0, sun6i_codec_out_mixer_pregain_scale), + SOC_SINGLE_TLV("Mic2 Playback Volume", + SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_MIC2G, + 0x7, 0, sun6i_codec_out_mixer_pregain_scale), + + /* Microphone Amp boost gains */ + SOC_SINGLE_TLV("Mic1 Boost Volume", SUN6I_CODEC_MIC_CTRL, + SUN6I_CODEC_MIC_CTRL_MIC1BOOST, 0x7, 0, + sun6i_codec_mic_gain_scale), + SOC_SINGLE_TLV("Mic2 Boost Volume", SUN6I_CODEC_MIC_CTRL, + SUN6I_CODEC_MIC_CTRL_MIC2BOOST, 0x7, 0, + sun6i_codec_mic_gain_scale), + SOC_DOUBLE_TLV("ADC Capture Volume", + SUN6I_CODEC_ADC_ACTL, SUN6I_CODEC_ADC_ACTL_ADCLG, + SUN6I_CODEC_ADC_ACTL_ADCRG, 0x7, 0, + sun6i_codec_out_mixer_pregain_scale), +}; + +static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = { + /* Microphone inputs */ + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("MIC3"), + + /* Microphone Bias */ + SND_SOC_DAPM_SUPPLY("HBIAS", SUN6I_CODEC_MIC_CTRL, + SUN6I_CODEC_MIC_CTRL_HBIASEN, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MBIAS", SUN6I_CODEC_MIC_CTRL, + SUN6I_CODEC_MIC_CTRL_MBIASEN, 0, NULL, 0), + + /* Mic input path */ + SND_SOC_DAPM_MUX("Mic2 Amplifier Source Route", + SND_SOC_NOPM, 0, 0, sun6i_codec_mic2_src), + SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN6I_CODEC_MIC_CTRL, + SUN6I_CODEC_MIC_CTRL_MIC1AMPEN, 0, NULL, 0), + SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN6I_CODEC_MIC_CTRL, + SUN6I_CODEC_MIC_CTRL_MIC2AMPEN, 0, NULL, 0), + + /* Line In */ + SND_SOC_DAPM_INPUT("LINEIN"), + + /* Digital parts of the ADCs */ + SND_SOC_DAPM_SUPPLY("ADC Enable", SUN6I_CODEC_ADC_FIFOC, + SUN6I_CODEC_ADC_FIFOC_EN_AD, 0, + NULL, 0), + + /* Analog parts of the ADCs */ + SND_SOC_DAPM_ADC("Left ADC", "Codec Capture", SUN6I_CODEC_ADC_ACTL, + SUN6I_CODEC_ADC_ACTL_ADCLEN, 0), + SND_SOC_DAPM_ADC("Right ADC", "Codec Capture", SUN6I_CODEC_ADC_ACTL, + SUN6I_CODEC_ADC_ACTL_ADCREN, 0), + + /* ADC Mixers */ + SOC_MIXER_ARRAY("Left ADC Mixer", SND_SOC_NOPM, 0, 0, + sun6i_codec_adc_mixer_controls), + SOC_MIXER_ARRAY("Right ADC Mixer", SND_SOC_NOPM, 0, 0, + sun6i_codec_adc_mixer_controls), + + /* Digital parts of the DACs */ + SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC, + SUN4I_CODEC_DAC_DPC_EN_DA, 0, + NULL, 0), + + /* Analog parts of the DACs */ + SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", + SUN6I_CODEC_OM_DACA_CTRL, + SUN6I_CODEC_OM_DACA_CTRL_DACALEN, 0), + SND_SOC_DAPM_DAC("Right DAC", "Codec Playback", + SUN6I_CODEC_OM_DACA_CTRL, + SUN6I_CODEC_OM_DACA_CTRL_DACAREN, 0), + + /* Mixers */ + SOC_MIXER_ARRAY("Left Mixer", SUN6I_CODEC_OM_DACA_CTRL, + SUN6I_CODEC_OM_DACA_CTRL_LMIXEN, 0, + sun6i_codec_mixer_controls), + SOC_MIXER_ARRAY("Right Mixer", SUN6I_CODEC_OM_DACA_CTRL, + SUN6I_CODEC_OM_DACA_CTRL_RMIXEN, 0, + sun6i_codec_mixer_controls), + + /* Headphone output path */ + SND_SOC_DAPM_MUX("Headphone Source Playback Route", + SND_SOC_NOPM, 0, 0, sun6i_codec_hp_src), + SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN6I_CODEC_OM_PA_CTRL, + SUN6I_CODEC_OM_PA_CTRL_HPPAEN, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("HPCOM Protection", SUN6I_CODEC_OM_PA_CTRL, + SUN6I_CODEC_OM_PA_CTRL_COMPTEN, 0, NULL, 0), + SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPCOM", SUN6I_CODEC_OM_PA_CTRL, + SUN6I_CODEC_OM_PA_CTRL_HPCOM_CTL, 0x3, 0x3, 0), + SND_SOC_DAPM_OUTPUT("HP"), + + /* Line Out path */ + SND_SOC_DAPM_MUX("Line Out Source Playback Route", + SND_SOC_NOPM, 0, 0, sun6i_codec_lineout_src), + SND_SOC_DAPM_OUTPUT("LINEOUT"), +}; + +static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = { + /* DAC Routes */ + { "Left DAC", NULL, "DAC Enable" }, + { "Right DAC", NULL, "DAC Enable" }, + + /* Microphone Routes */ + { "Mic1 Amplifier", NULL, "MIC1"}, + { "Mic2 Amplifier Source Route", "Mic2", "MIC2" }, + { "Mic2 Amplifier Source Route", "Mic3", "MIC3" }, + { "Mic2 Amplifier", NULL, "Mic2 Amplifier Source Route"}, + + /* Left Mixer Routes */ + { "Left Mixer", "DAC Playback Switch", "Left DAC" }, + { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" }, + { "Left Mixer", "Line In Playback Switch", "LINEIN" }, + { "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, + { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, + + /* Right Mixer Routes */ + { "Right Mixer", "DAC Playback Switch", "Right DAC" }, + { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" }, + { "Right Mixer", "Line In Playback Switch", "LINEIN" }, + { "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, + { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, + + /* Left ADC Mixer Routes */ + { "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" }, + { "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" }, + { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" }, + { "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, + { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, + + /* Right ADC Mixer Routes */ + { "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" }, + { "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" }, + { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" }, + { "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, + { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, + + /* Headphone Routes */ + { "Headphone Source Playback Route", "DAC", "Left DAC" }, + { "Headphone Source Playback Route", "DAC", "Right DAC" }, + { "Headphone Source Playback Route", "Mixer", "Left Mixer" }, + { "Headphone Source Playback Route", "Mixer", "Right Mixer" }, + { "Headphone Amp", NULL, "Headphone Source Playback Route" }, + { "HP", NULL, "Headphone Amp" }, + { "HPCOM", NULL, "HPCOM Protection" }, + + /* Line Out Routes */ + { "Line Out Source Playback Route", "Stereo", "Left Mixer" }, + { "Line Out Source Playback Route", "Stereo", "Right Mixer" }, + { "Line Out Source Playback Route", "Mono Differential", "Left Mixer" }, + { "LINEOUT", NULL, "Line Out Source Playback Route" }, + + /* ADC Routes */ + { "Left ADC", NULL, "ADC Enable" }, + { "Right ADC", NULL, "ADC Enable" }, + { "Left ADC", NULL, "Left ADC Mixer" }, + { "Right ADC", NULL, "Right ADC Mixer" }, +}; + +static struct snd_soc_codec_driver sun6i_codec_codec = { + .component_driver = { + .controls = sun6i_codec_codec_widgets, + .num_controls = ARRAY_SIZE(sun6i_codec_codec_widgets), + .dapm_widgets = sun6i_codec_codec_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sun6i_codec_codec_dapm_widgets), + .dapm_routes = sun6i_codec_codec_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(sun6i_codec_codec_dapm_routes), + }, +}; + +/* sun8i A23 codec */ +static const struct snd_kcontrol_new sun8i_a23_codec_codec_controls[] = { + SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC, + SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1, + sun6i_codec_dvol_scale), +}; + +static const struct snd_soc_dapm_widget sun8i_a23_codec_codec_widgets[] = { + /* Digital parts of the ADCs */ + SND_SOC_DAPM_SUPPLY("ADC Enable", SUN6I_CODEC_ADC_FIFOC, + SUN6I_CODEC_ADC_FIFOC_EN_AD, 0, NULL, 0), + /* Digital parts of the DACs */ + SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC, + SUN4I_CODEC_DAC_DPC_EN_DA, 0, NULL, 0), + +}; + +static struct snd_soc_codec_driver sun8i_a23_codec_codec = { + .component_driver = { + .controls = sun8i_a23_codec_codec_controls, + .num_controls = ARRAY_SIZE(sun8i_a23_codec_codec_controls), + .dapm_widgets = sun8i_a23_codec_codec_widgets, + .num_dapm_widgets = ARRAY_SIZE(sun8i_a23_codec_codec_widgets), + }, }; static const struct snd_soc_component_driver sun4i_codec_component = { @@ -676,20 +1144,6 @@ static struct snd_soc_dai_driver dummy_cpu_dai = { }, }; -static const struct regmap_config sun4i_codec_regmap_config = { - .reg_bits = 32, - .reg_stride = 4, - .val_bits = 32, - .max_register = SUN4I_CODEC_AC_MIC_PHONE_CAL, -}; - -static const struct of_device_id sun4i_codec_of_match[] = { - { .compatible = "allwinner,sun4i-a10-codec" }, - { .compatible = "allwinner,sun7i-a20-codec" }, - {} -}; -MODULE_DEVICE_TABLE(of, sun4i_codec_of_match); - static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev, int *num_links) { @@ -738,11 +1192,11 @@ static struct snd_soc_card *sun4i_codec_create_card(struct device *dev) card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); if (!card) - return NULL; + return ERR_PTR(-ENOMEM); card->dai_link = sun4i_codec_create_link(dev, &card->num_links); if (!card->dai_link) - return NULL; + return ERR_PTR(-ENOMEM); card->dev = dev; card->name = "sun4i-codec"; @@ -754,10 +1208,264 @@ static struct snd_soc_card *sun4i_codec_create_card(struct device *dev) return card; }; +static const struct snd_soc_dapm_widget sun6i_codec_card_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_LINE("Line In", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Mic", NULL), + SND_SOC_DAPM_SPK("Speaker", sun4i_codec_spk_event), +}; + +static struct snd_soc_card *sun6i_codec_create_card(struct device *dev) +{ + struct snd_soc_card *card; + int ret; + + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!card) + return ERR_PTR(-ENOMEM); + + card->dai_link = sun4i_codec_create_link(dev, &card->num_links); + if (!card->dai_link) + return ERR_PTR(-ENOMEM); + + card->dev = dev; + card->name = "A31 Audio Codec"; + card->dapm_widgets = sun6i_codec_card_dapm_widgets; + card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets); + card->fully_routed = true; + + ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing"); + if (ret) + dev_warn(dev, "failed to parse audio-routing: %d\n", ret); + + return card; +}; + +/* Connect digital side enables to analog side widgets */ +static const struct snd_soc_dapm_route sun8i_codec_card_routes[] = { + /* ADC Routes */ + { "Left ADC", NULL, "ADC Enable" }, + { "Right ADC", NULL, "ADC Enable" }, + { "Codec Capture", NULL, "Left ADC" }, + { "Codec Capture", NULL, "Right ADC" }, + + /* DAC Routes */ + { "Left DAC", NULL, "DAC Enable" }, + { "Right DAC", NULL, "DAC Enable" }, + { "Left DAC", NULL, "Codec Playback" }, + { "Right DAC", NULL, "Codec Playback" }, +}; + +static struct snd_soc_aux_dev aux_dev = { + .name = "Codec Analog Controls", +}; + +static struct snd_soc_card *sun8i_a23_codec_create_card(struct device *dev) +{ + struct snd_soc_card *card; + int ret; + + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!card) + return ERR_PTR(-ENOMEM); + + aux_dev.codec_of_node = of_parse_phandle(dev->of_node, + "allwinner,codec-analog-controls", + 0); + if (!aux_dev.codec_of_node) { + dev_err(dev, "Can't find analog controls for codec.\n"); + return ERR_PTR(-EINVAL); + }; + + card->dai_link = sun4i_codec_create_link(dev, &card->num_links); + if (!card->dai_link) + return ERR_PTR(-ENOMEM); + + card->dev = dev; + card->name = "A23 Audio Codec"; + card->dapm_widgets = sun6i_codec_card_dapm_widgets; + card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets); + card->dapm_routes = sun8i_codec_card_routes; + card->num_dapm_routes = ARRAY_SIZE(sun8i_codec_card_routes); + card->aux_dev = &aux_dev; + card->num_aux_devs = 1; + card->fully_routed = true; + + ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing"); + if (ret) + dev_warn(dev, "failed to parse audio-routing: %d\n", ret); + + return card; +}; + +static struct snd_soc_card *sun8i_h3_codec_create_card(struct device *dev) +{ + struct snd_soc_card *card; + int ret; + + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!card) + return ERR_PTR(-ENOMEM); + + aux_dev.codec_of_node = of_parse_phandle(dev->of_node, + "allwinner,codec-analog-controls", + 0); + if (!aux_dev.codec_of_node) { + dev_err(dev, "Can't find analog controls for codec.\n"); + return ERR_PTR(-EINVAL); + }; + + card->dai_link = sun4i_codec_create_link(dev, &card->num_links); + if (!card->dai_link) + return ERR_PTR(-ENOMEM); + + card->dev = dev; + card->name = "H3 Audio Codec"; + card->dapm_widgets = sun6i_codec_card_dapm_widgets; + card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets); + card->dapm_routes = sun8i_codec_card_routes; + card->num_dapm_routes = ARRAY_SIZE(sun8i_codec_card_routes); + card->aux_dev = &aux_dev; + card->num_aux_devs = 1; + card->fully_routed = true; + + ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing"); + if (ret) + dev_warn(dev, "failed to parse audio-routing: %d\n", ret); + + return card; +}; + +static const struct regmap_config sun4i_codec_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = SUN4I_CODEC_ADC_RXCNT, +}; + +static const struct regmap_config sun6i_codec_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = SUN6I_CODEC_HMIC_DATA, +}; + +static const struct regmap_config sun7i_codec_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = SUN7I_CODEC_AC_MIC_PHONE_CAL, +}; + +static const struct regmap_config sun8i_a23_codec_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = SUN8I_A23_CODEC_ADC_RXCNT, +}; + +static const struct regmap_config sun8i_h3_codec_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = SUN8I_H3_CODEC_ADC_DBG, +}; + +struct sun4i_codec_quirks { + const struct regmap_config *regmap_config; + const struct snd_soc_codec_driver *codec; + struct snd_soc_card * (*create_card)(struct device *dev); + struct reg_field reg_adc_fifoc; /* used for regmap_field */ + unsigned int reg_dac_txdata; /* TX FIFO offset for DMA config */ + unsigned int reg_adc_rxdata; /* RX FIFO offset for DMA config */ + bool has_reset; +}; + +static const struct sun4i_codec_quirks sun4i_codec_quirks = { + .regmap_config = &sun4i_codec_regmap_config, + .codec = &sun4i_codec_codec, + .create_card = sun4i_codec_create_card, + .reg_adc_fifoc = REG_FIELD(SUN4I_CODEC_ADC_FIFOC, 0, 31), + .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA, + .reg_adc_rxdata = SUN4I_CODEC_ADC_RXDATA, +}; + +static const struct sun4i_codec_quirks sun6i_a31_codec_quirks = { + .regmap_config = &sun6i_codec_regmap_config, + .codec = &sun6i_codec_codec, + .create_card = sun6i_codec_create_card, + .reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31), + .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA, + .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA, + .has_reset = true, +}; + +static const struct sun4i_codec_quirks sun7i_codec_quirks = { + .regmap_config = &sun7i_codec_regmap_config, + .codec = &sun4i_codec_codec, + .create_card = sun4i_codec_create_card, + .reg_adc_fifoc = REG_FIELD(SUN4I_CODEC_ADC_FIFOC, 0, 31), + .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA, + .reg_adc_rxdata = SUN4I_CODEC_ADC_RXDATA, +}; + +static const struct sun4i_codec_quirks sun8i_a23_codec_quirks = { + .regmap_config = &sun8i_a23_codec_regmap_config, + .codec = &sun8i_a23_codec_codec, + .create_card = sun8i_a23_codec_create_card, + .reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31), + .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA, + .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA, + .has_reset = true, +}; + +static const struct sun4i_codec_quirks sun8i_h3_codec_quirks = { + .regmap_config = &sun8i_h3_codec_regmap_config, + /* + * TODO Share the codec structure with A23 for now. + * This should be split out when adding digital audio + * processing support for the H3. + */ + .codec = &sun8i_a23_codec_codec, + .create_card = sun8i_h3_codec_create_card, + .reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31), + .reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA, + .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA, + .has_reset = true, +}; + +static const struct of_device_id sun4i_codec_of_match[] = { + { + .compatible = "allwinner,sun4i-a10-codec", + .data = &sun4i_codec_quirks, + }, + { + .compatible = "allwinner,sun6i-a31-codec", + .data = &sun6i_a31_codec_quirks, + }, + { + .compatible = "allwinner,sun7i-a20-codec", + .data = &sun7i_codec_quirks, + }, + { + .compatible = "allwinner,sun8i-a23-codec", + .data = &sun8i_a23_codec_quirks, + }, + { + .compatible = "allwinner,sun8i-h3-codec", + .data = &sun8i_h3_codec_quirks, + }, + {} +}; +MODULE_DEVICE_TABLE(of, sun4i_codec_of_match); + static int sun4i_codec_probe(struct platform_device *pdev) { struct snd_soc_card *card; struct sun4i_codec *scodec; + const struct sun4i_codec_quirks *quirks; struct resource *res; void __iomem *base; int ret; @@ -775,8 +1483,14 @@ static int sun4i_codec_probe(struct platform_device *pdev) return PTR_ERR(base); } + quirks = of_device_get_match_data(&pdev->dev); + if (quirks == NULL) { + dev_err(&pdev->dev, "Failed to determine the quirks to use\n"); + return -ENODEV; + } + scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base, - &sun4i_codec_regmap_config); + quirks->regmap_config); if (IS_ERR(scodec->regmap)) { dev_err(&pdev->dev, "Failed to create our regmap\n"); return PTR_ERR(scodec->regmap); @@ -795,10 +1509,12 @@ static int sun4i_codec_probe(struct platform_device *pdev) return PTR_ERR(scodec->clk_module); } - /* Enable the bus clock */ - if (clk_prepare_enable(scodec->clk_apb)) { - dev_err(&pdev->dev, "Failed to enable the APB clock\n"); - return -EINVAL; + if (quirks->has_reset) { + scodec->rst = devm_reset_control_get(&pdev->dev, NULL); + if (IS_ERR(scodec->rst)) { + dev_err(&pdev->dev, "Failed to get reset control\n"); + return PTR_ERR(scodec->rst); + } } scodec->gpio_pa = devm_gpiod_get_optional(&pdev->dev, "allwinner,pa", @@ -810,21 +1526,48 @@ static int sun4i_codec_probe(struct platform_device *pdev) return ret; } + /* reg_field setup */ + scodec->reg_adc_fifoc = devm_regmap_field_alloc(&pdev->dev, + scodec->regmap, + quirks->reg_adc_fifoc); + if (IS_ERR(scodec->reg_adc_fifoc)) { + ret = PTR_ERR(scodec->reg_adc_fifoc); + dev_err(&pdev->dev, "Failed to create regmap fields: %d\n", + ret); + return ret; + } + + /* Enable the bus clock */ + if (clk_prepare_enable(scodec->clk_apb)) { + dev_err(&pdev->dev, "Failed to enable the APB clock\n"); + return -EINVAL; + } + + /* Deassert the reset control */ + if (scodec->rst) { + ret = reset_control_deassert(scodec->rst); + if (ret) { + dev_err(&pdev->dev, + "Failed to deassert the reset control\n"); + goto err_clk_disable; + } + } + /* DMA configuration for TX FIFO */ - scodec->playback_dma_data.addr = res->start + SUN4I_CODEC_DAC_TXDATA; - scodec->playback_dma_data.maxburst = 4; + scodec->playback_dma_data.addr = res->start + quirks->reg_dac_txdata; + scodec->playback_dma_data.maxburst = 8; scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; /* DMA configuration for RX FIFO */ - scodec->capture_dma_data.addr = res->start + SUN4I_CODEC_ADC_RXDATA; - scodec->capture_dma_data.maxburst = 4; + scodec->capture_dma_data.addr = res->start + quirks->reg_adc_rxdata; + scodec->capture_dma_data.maxburst = 8; scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; - ret = snd_soc_register_codec(&pdev->dev, &sun4i_codec_codec, + ret = snd_soc_register_codec(&pdev->dev, quirks->codec, &sun4i_codec_dai, 1); if (ret) { dev_err(&pdev->dev, "Failed to register our codec\n"); - goto err_clk_disable; + goto err_assert_reset; } ret = devm_snd_soc_register_component(&pdev->dev, @@ -841,8 +1584,9 @@ static int sun4i_codec_probe(struct platform_device *pdev) goto err_unregister_codec; } - card = sun4i_codec_create_card(&pdev->dev); - if (!card) { + card = quirks->create_card(&pdev->dev); + if (IS_ERR(card)) { + ret = PTR_ERR(card); dev_err(&pdev->dev, "Failed to create our card\n"); goto err_unregister_codec; } @@ -860,6 +1604,9 @@ static int sun4i_codec_probe(struct platform_device *pdev) err_unregister_codec: snd_soc_unregister_codec(&pdev->dev); +err_assert_reset: + if (scodec->rst) + reset_control_assert(scodec->rst); err_clk_disable: clk_disable_unprepare(scodec->clk_apb); return ret; @@ -872,6 +1619,8 @@ static int sun4i_codec_remove(struct platform_device *pdev) snd_soc_unregister_card(card); snd_soc_unregister_codec(&pdev->dev); + if (scodec->rst) + reset_control_assert(scodec->rst); clk_disable_unprepare(scodec->clk_apb); return 0; @@ -891,4 +1640,5 @@ MODULE_DESCRIPTION("Allwinner A10 codec driver"); MODULE_AUTHOR("Emilio López <emilio@elopez.com.ar>"); MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>"); MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); +MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 687a8f83dbe5..f24d19526603 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -93,6 +93,9 @@ struct sun4i_i2s { struct clk *mod_clk; struct regmap *regmap; + unsigned int mclk_freq; + + struct snd_dmaengine_dai_dma_data capture_dma_data; struct snd_dmaengine_dai_dma_data playback_dma_data; }; @@ -157,14 +160,24 @@ static int sun4i_i2s_get_mclk_div(struct sun4i_i2s *i2s, } static int sun4i_i2s_oversample_rates[] = { 128, 192, 256, 384, 512, 768 }; +static bool sun4i_i2s_oversample_is_valid(unsigned int oversample) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sun4i_i2s_oversample_rates); i++) + if (sun4i_i2s_oversample_rates[i] == oversample) + return true; + + return false; +} static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s, unsigned int rate, unsigned int word_size) { - unsigned int clk_rate; + unsigned int oversample_rate, clk_rate; int bclk_div, mclk_div; - int ret, i; + int ret; switch (rate) { case 176400: @@ -196,21 +209,18 @@ static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s, if (ret) return ret; - /* Always favor the highest oversampling rate */ - for (i = (ARRAY_SIZE(sun4i_i2s_oversample_rates) - 1); i >= 0; i--) { - unsigned int oversample_rate = sun4i_i2s_oversample_rates[i]; - - bclk_div = sun4i_i2s_get_bclk_div(i2s, oversample_rate, - word_size); - mclk_div = sun4i_i2s_get_mclk_div(i2s, oversample_rate, - clk_rate, - rate); + oversample_rate = i2s->mclk_freq / rate; + if (!sun4i_i2s_oversample_is_valid(oversample_rate)) + return -EINVAL; - if ((bclk_div >= 0) && (mclk_div >= 0)) - break; - } + bclk_div = sun4i_i2s_get_bclk_div(i2s, oversample_rate, + word_size); + if (bclk_div < 0) + return -EINVAL; - if ((bclk_div < 0) || (mclk_div < 0)) + mclk_div = sun4i_i2s_get_mclk_div(i2s, oversample_rate, + clk_rate, rate); + if (mclk_div < 0) return -EINVAL; regmap_write(i2s->regmap, SUN4I_I2S_CLK_DIV_REG, @@ -341,6 +351,27 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } +static void sun4i_i2s_start_capture(struct sun4i_i2s *i2s) +{ + /* Flush RX FIFO */ + regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG, + SUN4I_I2S_FIFO_CTRL_FLUSH_RX, + SUN4I_I2S_FIFO_CTRL_FLUSH_RX); + + /* Clear RX counter */ + regmap_write(i2s->regmap, SUN4I_I2S_RX_CNT_REG, 0); + + /* Enable RX Block */ + regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, + SUN4I_I2S_CTRL_RX_EN, + SUN4I_I2S_CTRL_RX_EN); + + /* Enable RX DRQ */ + regmap_update_bits(i2s->regmap, SUN4I_I2S_DMA_INT_CTRL_REG, + SUN4I_I2S_DMA_INT_CTRL_RX_DRQ_EN, + SUN4I_I2S_DMA_INT_CTRL_RX_DRQ_EN); +} + static void sun4i_i2s_start_playback(struct sun4i_i2s *i2s) { /* Flush TX FIFO */ @@ -362,6 +393,18 @@ static void sun4i_i2s_start_playback(struct sun4i_i2s *i2s) SUN4I_I2S_DMA_INT_CTRL_TX_DRQ_EN); } +static void sun4i_i2s_stop_capture(struct sun4i_i2s *i2s) +{ + /* Disable RX Block */ + regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, + SUN4I_I2S_CTRL_RX_EN, + 0); + + /* Disable RX DRQ */ + regmap_update_bits(i2s->regmap, SUN4I_I2S_DMA_INT_CTRL_REG, + SUN4I_I2S_DMA_INT_CTRL_RX_DRQ_EN, + 0); +} static void sun4i_i2s_stop_playback(struct sun4i_i2s *i2s) { @@ -388,7 +431,7 @@ static int sun4i_i2s_trigger(struct snd_pcm_substream *substream, int cmd, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) sun4i_i2s_start_playback(i2s); else - return -EINVAL; + sun4i_i2s_start_capture(i2s); break; case SNDRV_PCM_TRIGGER_STOP: @@ -397,7 +440,7 @@ static int sun4i_i2s_trigger(struct snd_pcm_substream *substream, int cmd, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) sun4i_i2s_stop_playback(i2s); else - return -EINVAL; + sun4i_i2s_stop_capture(i2s); break; default: @@ -447,9 +490,23 @@ static void sun4i_i2s_shutdown(struct snd_pcm_substream *substream, regmap_write(i2s->regmap, SUN4I_I2S_CTRL_REG, 0); } +static int sun4i_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + if (clk_id != 0) + return -EINVAL; + + i2s->mclk_freq = freq; + + return 0; +} + static const struct snd_soc_dai_ops sun4i_i2s_dai_ops = { .hw_params = sun4i_i2s_hw_params, .set_fmt = sun4i_i2s_set_fmt, + .set_sysclk = sun4i_i2s_set_sysclk, .shutdown = sun4i_i2s_shutdown, .startup = sun4i_i2s_startup, .trigger = sun4i_i2s_trigger, @@ -459,7 +516,9 @@ static int sun4i_i2s_dai_probe(struct snd_soc_dai *dai) { struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); - snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data, NULL); + snd_soc_dai_init_dma_data(dai, + &i2s->playback_dma_data, + &i2s->capture_dma_data); snd_soc_dai_set_drvdata(dai, i2s); @@ -468,6 +527,13 @@ static int sun4i_i2s_dai_probe(struct snd_soc_dai *dai) static struct snd_soc_dai_driver sun4i_i2s_dai = { .probe = sun4i_i2s_dai_probe, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, .playback = { .stream_name = "Playback", .channels_min = 2, @@ -630,6 +696,9 @@ static int sun4i_i2s_probe(struct platform_device *pdev) i2s->playback_dma_data.addr = res->start + SUN4I_I2S_FIFO_TX_REG; i2s->playback_dma_data.maxburst = 4; + i2s->capture_dma_data.addr = res->start + SUN4I_I2S_FIFO_RX_REG; + i2s->capture_dma_data.maxburst = 4; + pm_runtime_enable(&pdev->dev); if (!pm_runtime_enabled(&pdev->dev)) { ret = sun4i_i2s_runtime_resume(&pdev->dev); diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c index 0b04fb02125c..88fbb3a1e660 100644 --- a/sound/soc/sunxi/sun4i-spdif.c +++ b/sound/soc/sunxi/sun4i-spdif.c @@ -29,6 +29,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/reset.h> #include <sound/dmaengine_pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -162,6 +163,7 @@ struct sun4i_spdif_dev { struct platform_device *pdev; struct clk *spdif_clk; struct clk *apb_clk; + struct reset_control *rst; struct snd_soc_dai_driver cpu_dai_drv; struct regmap *regmap; struct snd_dmaengine_dai_dma_data dma_params_tx; @@ -411,6 +413,7 @@ static const struct snd_soc_dapm_route dit_routes[] = { static const struct of_device_id sun4i_spdif_of_match[] = { { .compatible = "allwinner,sun4i-a10-spdif", }, + { .compatible = "allwinner,sun6i-a31-spdif", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sun4i_spdif_of_match); @@ -482,11 +485,23 @@ static int sun4i_spdif_probe(struct platform_device *pdev) } host->dma_params_tx.addr = res->start + SUN4I_SPDIF_TXFIFO; - host->dma_params_tx.maxburst = 4; + host->dma_params_tx.maxburst = 8; host->dma_params_tx.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; platform_set_drvdata(pdev, host); + if (of_device_is_compatible(pdev->dev.of_node, + "allwinner,sun6i-a31-spdif")) { + host->rst = devm_reset_control_get_optional(&pdev->dev, NULL); + if (IS_ERR(host->rst) && PTR_ERR(host->rst) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + dev_err(&pdev->dev, "Failed to get reset: %d\n", ret); + goto err_disable_apb_clk; + } + if (!IS_ERR(host->rst)) + reset_control_deassert(host->rst); + } + ret = devm_snd_soc_register_component(&pdev->dev, &sun4i_spdif_component, &sun4i_spdif_dai, 1); if (ret) diff --git a/sound/soc/sunxi/sun8i-codec-analog.c b/sound/soc/sunxi/sun8i-codec-analog.c new file mode 100644 index 000000000000..af02290ebe49 --- /dev/null +++ b/sound/soc/sunxi/sun8i-codec-analog.c @@ -0,0 +1,665 @@ +/* + * This driver supports the analog controls for the internal codec + * found in Allwinner's A31s, A23, A33 and H3 SoCs. + * + * Copyright 2016 Chen-Yu Tsai <wens@csie.org> + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> + +/* Codec analog control register offsets and bit fields */ +#define SUN8I_ADDA_HP_VOLC 0x00 +#define SUN8I_ADDA_HP_VOLC_PA_CLK_GATE 7 +#define SUN8I_ADDA_HP_VOLC_HP_VOL 0 +#define SUN8I_ADDA_LOMIXSC 0x01 +#define SUN8I_ADDA_LOMIXSC_MIC1 6 +#define SUN8I_ADDA_LOMIXSC_MIC2 5 +#define SUN8I_ADDA_LOMIXSC_PHONE 4 +#define SUN8I_ADDA_LOMIXSC_PHONEN 3 +#define SUN8I_ADDA_LOMIXSC_LINEINL 2 +#define SUN8I_ADDA_LOMIXSC_DACL 1 +#define SUN8I_ADDA_LOMIXSC_DACR 0 +#define SUN8I_ADDA_ROMIXSC 0x02 +#define SUN8I_ADDA_ROMIXSC_MIC1 6 +#define SUN8I_ADDA_ROMIXSC_MIC2 5 +#define SUN8I_ADDA_ROMIXSC_PHONE 4 +#define SUN8I_ADDA_ROMIXSC_PHONEP 3 +#define SUN8I_ADDA_ROMIXSC_LINEINR 2 +#define SUN8I_ADDA_ROMIXSC_DACR 1 +#define SUN8I_ADDA_ROMIXSC_DACL 0 +#define SUN8I_ADDA_DAC_PA_SRC 0x03 +#define SUN8I_ADDA_DAC_PA_SRC_DACAREN 7 +#define SUN8I_ADDA_DAC_PA_SRC_DACALEN 6 +#define SUN8I_ADDA_DAC_PA_SRC_RMIXEN 5 +#define SUN8I_ADDA_DAC_PA_SRC_LMIXEN 4 +#define SUN8I_ADDA_DAC_PA_SRC_RHPPAMUTE 3 +#define SUN8I_ADDA_DAC_PA_SRC_LHPPAMUTE 2 +#define SUN8I_ADDA_DAC_PA_SRC_RHPIS 1 +#define SUN8I_ADDA_DAC_PA_SRC_LHPIS 0 +#define SUN8I_ADDA_PHONEIN_GCTRL 0x04 +#define SUN8I_ADDA_PHONEIN_GCTRL_PHONEPG 4 +#define SUN8I_ADDA_PHONEIN_GCTRL_PHONENG 0 +#define SUN8I_ADDA_LINEIN_GCTRL 0x05 +#define SUN8I_ADDA_LINEIN_GCTRL_LINEING 4 +#define SUN8I_ADDA_LINEIN_GCTRL_PHONEG 0 +#define SUN8I_ADDA_MICIN_GCTRL 0x06 +#define SUN8I_ADDA_MICIN_GCTRL_MIC1G 4 +#define SUN8I_ADDA_MICIN_GCTRL_MIC2G 0 +#define SUN8I_ADDA_PAEN_HP_CTRL 0x07 +#define SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN 7 +#define SUN8I_ADDA_PAEN_HP_CTRL_LINEOUTEN 7 /* H3 specific */ +#define SUN8I_ADDA_PAEN_HP_CTRL_HPCOM_FC 5 +#define SUN8I_ADDA_PAEN_HP_CTRL_COMPTEN 4 +#define SUN8I_ADDA_PAEN_HP_CTRL_PA_ANTI_POP_CTRL 2 +#define SUN8I_ADDA_PAEN_HP_CTRL_LTRNMUTE 1 +#define SUN8I_ADDA_PAEN_HP_CTRL_RTLNMUTE 0 +#define SUN8I_ADDA_PHONEOUT_CTRL 0x08 +#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUTG 5 +#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUTEN 4 +#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_MIC1 3 +#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_MIC2 2 +#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_RMIX 1 +#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_LMIX 0 +#define SUN8I_ADDA_PHONE_GAIN_CTRL 0x09 +#define SUN8I_ADDA_PHONE_GAIN_CTRL_LINEOUT_VOL 3 +#define SUN8I_ADDA_PHONE_GAIN_CTRL_PHONEPREG 0 +#define SUN8I_ADDA_MIC2G_CTRL 0x0a +#define SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN 7 +#define SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST 4 +#define SUN8I_ADDA_MIC2G_CTRL_LINEOUTLEN 3 +#define SUN8I_ADDA_MIC2G_CTRL_LINEOUTREN 2 +#define SUN8I_ADDA_MIC2G_CTRL_LINEOUTLSRC 1 +#define SUN8I_ADDA_MIC2G_CTRL_LINEOUTRSRC 0 +#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL 0x0b +#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIASEN 7 +#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MMICBIASEN 6 +#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIAS_MODE 5 +#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN 3 +#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1BOOST 0 +#define SUN8I_ADDA_LADCMIXSC 0x0c +#define SUN8I_ADDA_LADCMIXSC_MIC1 6 +#define SUN8I_ADDA_LADCMIXSC_MIC2 5 +#define SUN8I_ADDA_LADCMIXSC_PHONE 4 +#define SUN8I_ADDA_LADCMIXSC_PHONEN 3 +#define SUN8I_ADDA_LADCMIXSC_LINEINL 2 +#define SUN8I_ADDA_LADCMIXSC_OMIXRL 1 +#define SUN8I_ADDA_LADCMIXSC_OMIXRR 0 +#define SUN8I_ADDA_RADCMIXSC 0x0d +#define SUN8I_ADDA_RADCMIXSC_MIC1 6 +#define SUN8I_ADDA_RADCMIXSC_MIC2 5 +#define SUN8I_ADDA_RADCMIXSC_PHONE 4 +#define SUN8I_ADDA_RADCMIXSC_PHONEP 3 +#define SUN8I_ADDA_RADCMIXSC_LINEINR 2 +#define SUN8I_ADDA_RADCMIXSC_OMIXR 1 +#define SUN8I_ADDA_RADCMIXSC_OMIXL 0 +#define SUN8I_ADDA_RES 0x0e +#define SUN8I_ADDA_RES_MMICBIAS_SEL 4 +#define SUN8I_ADDA_RES_PA_ANTI_POP_CTRL 0 +#define SUN8I_ADDA_ADC_AP_EN 0x0f +#define SUN8I_ADDA_ADC_AP_EN_ADCREN 7 +#define SUN8I_ADDA_ADC_AP_EN_ADCLEN 6 +#define SUN8I_ADDA_ADC_AP_EN_ADCG 0 + +/* Analog control register access bits */ +#define ADDA_PR 0x0 /* PRCM base + 0x1c0 */ +#define ADDA_PR_RESET BIT(28) +#define ADDA_PR_WRITE BIT(24) +#define ADDA_PR_ADDR_SHIFT 16 +#define ADDA_PR_ADDR_MASK GENMASK(4, 0) +#define ADDA_PR_DATA_IN_SHIFT 8 +#define ADDA_PR_DATA_IN_MASK GENMASK(7, 0) +#define ADDA_PR_DATA_OUT_SHIFT 0 +#define ADDA_PR_DATA_OUT_MASK GENMASK(7, 0) + +/* regmap access bits */ +static int adda_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + void __iomem *base = (void __iomem *)context; + u32 tmp; + + /* De-assert reset */ + writel(readl(base) | ADDA_PR_RESET, base); + + /* Clear write bit */ + writel(readl(base) & ~ADDA_PR_WRITE, base); + + /* Set register address */ + tmp = readl(base); + tmp &= ~(ADDA_PR_ADDR_MASK << ADDA_PR_ADDR_SHIFT); + tmp |= (reg & ADDA_PR_ADDR_MASK) << ADDA_PR_ADDR_SHIFT; + writel(tmp, base); + + /* Read back value */ + *val = readl(base) & ADDA_PR_DATA_OUT_MASK; + + return 0; +} + +static int adda_reg_write(void *context, unsigned int reg, unsigned int val) +{ + void __iomem *base = (void __iomem *)context; + u32 tmp; + + /* De-assert reset */ + writel(readl(base) | ADDA_PR_RESET, base); + + /* Set register address */ + tmp = readl(base); + tmp &= ~(ADDA_PR_ADDR_MASK << ADDA_PR_ADDR_SHIFT); + tmp |= (reg & ADDA_PR_ADDR_MASK) << ADDA_PR_ADDR_SHIFT; + writel(tmp, base); + + /* Set data to write */ + tmp = readl(base); + tmp &= ~(ADDA_PR_DATA_IN_MASK << ADDA_PR_DATA_IN_SHIFT); + tmp |= (val & ADDA_PR_DATA_IN_MASK) << ADDA_PR_DATA_IN_SHIFT; + writel(tmp, base); + + /* Set write bit to signal a write */ + writel(readl(base) | ADDA_PR_WRITE, base); + + /* Clear write bit */ + writel(readl(base) & ~ADDA_PR_WRITE, base); + + return 0; +} + +static const struct regmap_config adda_pr_regmap_cfg = { + .name = "adda-pr", + .reg_bits = 5, + .reg_stride = 1, + .val_bits = 8, + .reg_read = adda_reg_read, + .reg_write = adda_reg_write, + .fast_io = true, + .max_register = 24, +}; + +/* mixer controls */ +static const struct snd_kcontrol_new sun8i_codec_mixer_controls[] = { + SOC_DAPM_DOUBLE_R("DAC Playback Switch", + SUN8I_ADDA_LOMIXSC, + SUN8I_ADDA_ROMIXSC, + SUN8I_ADDA_LOMIXSC_DACL, 1, 0), + SOC_DAPM_DOUBLE_R("DAC Reversed Playback Switch", + SUN8I_ADDA_LOMIXSC, + SUN8I_ADDA_ROMIXSC, + SUN8I_ADDA_LOMIXSC_DACR, 1, 0), + SOC_DAPM_DOUBLE_R("Line In Playback Switch", + SUN8I_ADDA_LOMIXSC, + SUN8I_ADDA_ROMIXSC, + SUN8I_ADDA_LOMIXSC_LINEINL, 1, 0), + SOC_DAPM_DOUBLE_R("Mic1 Playback Switch", + SUN8I_ADDA_LOMIXSC, + SUN8I_ADDA_ROMIXSC, + SUN8I_ADDA_LOMIXSC_MIC1, 1, 0), + SOC_DAPM_DOUBLE_R("Mic2 Playback Switch", + SUN8I_ADDA_LOMIXSC, + SUN8I_ADDA_ROMIXSC, + SUN8I_ADDA_LOMIXSC_MIC2, 1, 0), +}; + +/* ADC mixer controls */ +static const struct snd_kcontrol_new sun8i_codec_adc_mixer_controls[] = { + SOC_DAPM_DOUBLE_R("Mixer Capture Switch", + SUN8I_ADDA_LADCMIXSC, + SUN8I_ADDA_RADCMIXSC, + SUN8I_ADDA_LADCMIXSC_OMIXRL, 1, 0), + SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch", + SUN8I_ADDA_LADCMIXSC, + SUN8I_ADDA_RADCMIXSC, + SUN8I_ADDA_LADCMIXSC_OMIXRR, 1, 0), + SOC_DAPM_DOUBLE_R("Line In Capture Switch", + SUN8I_ADDA_LADCMIXSC, + SUN8I_ADDA_RADCMIXSC, + SUN8I_ADDA_LADCMIXSC_LINEINL, 1, 0), + SOC_DAPM_DOUBLE_R("Mic1 Capture Switch", + SUN8I_ADDA_LADCMIXSC, + SUN8I_ADDA_RADCMIXSC, + SUN8I_ADDA_LADCMIXSC_MIC1, 1, 0), + SOC_DAPM_DOUBLE_R("Mic2 Capture Switch", + SUN8I_ADDA_LADCMIXSC, + SUN8I_ADDA_RADCMIXSC, + SUN8I_ADDA_LADCMIXSC_MIC2, 1, 0), +}; + +/* volume / mute controls */ +static const DECLARE_TLV_DB_SCALE(sun8i_codec_out_mixer_pregain_scale, + -450, 150, 0); +static const DECLARE_TLV_DB_RANGE(sun8i_codec_mic_gain_scale, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0), +); + +static const struct snd_kcontrol_new sun8i_codec_common_controls[] = { + /* Mixer pre-gains */ + SOC_SINGLE_TLV("Line In Playback Volume", SUN8I_ADDA_LINEIN_GCTRL, + SUN8I_ADDA_LINEIN_GCTRL_LINEING, + 0x7, 0, sun8i_codec_out_mixer_pregain_scale), + SOC_SINGLE_TLV("Mic1 Playback Volume", SUN8I_ADDA_MICIN_GCTRL, + SUN8I_ADDA_MICIN_GCTRL_MIC1G, + 0x7, 0, sun8i_codec_out_mixer_pregain_scale), + SOC_SINGLE_TLV("Mic2 Playback Volume", + SUN8I_ADDA_MICIN_GCTRL, SUN8I_ADDA_MICIN_GCTRL_MIC2G, + 0x7, 0, sun8i_codec_out_mixer_pregain_scale), + + /* Microphone Amp boost gains */ + SOC_SINGLE_TLV("Mic1 Boost Volume", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, + SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1BOOST, 0x7, 0, + sun8i_codec_mic_gain_scale), + SOC_SINGLE_TLV("Mic2 Boost Volume", SUN8I_ADDA_MIC2G_CTRL, + SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST, 0x7, 0, + sun8i_codec_mic_gain_scale), + + /* ADC */ + SOC_SINGLE_TLV("ADC Gain Capture Volume", SUN8I_ADDA_ADC_AP_EN, + SUN8I_ADDA_ADC_AP_EN_ADCG, 0x7, 0, + sun8i_codec_out_mixer_pregain_scale), +}; + +static const struct snd_soc_dapm_widget sun8i_codec_common_widgets[] = { + /* ADC */ + SND_SOC_DAPM_ADC("Left ADC", NULL, SUN8I_ADDA_ADC_AP_EN, + SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0), + SND_SOC_DAPM_ADC("Right ADC", NULL, SUN8I_ADDA_ADC_AP_EN, + SUN8I_ADDA_ADC_AP_EN_ADCREN, 0), + + /* DAC */ + SND_SOC_DAPM_DAC("Left DAC", NULL, SUN8I_ADDA_DAC_PA_SRC, + SUN8I_ADDA_DAC_PA_SRC_DACALEN, 0), + SND_SOC_DAPM_DAC("Right DAC", NULL, SUN8I_ADDA_DAC_PA_SRC, + SUN8I_ADDA_DAC_PA_SRC_DACAREN, 0), + /* + * Due to this component and the codec belonging to separate DAPM + * contexts, we need to manually link the above widgets to their + * stream widgets at the card level. + */ + + /* Line In */ + SND_SOC_DAPM_INPUT("LINEIN"), + + /* Microphone inputs */ + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + + /* Microphone Bias */ + SND_SOC_DAPM_SUPPLY("MBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, + SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MMICBIASEN, + 0, NULL, 0), + + /* Mic input path */ + SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, + SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN, 0, NULL, 0), + SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN8I_ADDA_MIC2G_CTRL, + SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN, 0, NULL, 0), + + /* Mixers */ + SND_SOC_DAPM_MIXER("Left Mixer", SUN8I_ADDA_DAC_PA_SRC, + SUN8I_ADDA_DAC_PA_SRC_LMIXEN, 0, + sun8i_codec_mixer_controls, + ARRAY_SIZE(sun8i_codec_mixer_controls)), + SND_SOC_DAPM_MIXER("Right Mixer", SUN8I_ADDA_DAC_PA_SRC, + SUN8I_ADDA_DAC_PA_SRC_RMIXEN, 0, + sun8i_codec_mixer_controls, + ARRAY_SIZE(sun8i_codec_mixer_controls)), + SND_SOC_DAPM_MIXER("Left ADC Mixer", SUN8I_ADDA_ADC_AP_EN, + SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0, + sun8i_codec_adc_mixer_controls, + ARRAY_SIZE(sun8i_codec_adc_mixer_controls)), + SND_SOC_DAPM_MIXER("Right ADC Mixer", SUN8I_ADDA_ADC_AP_EN, + SUN8I_ADDA_ADC_AP_EN_ADCREN, 0, + sun8i_codec_adc_mixer_controls, + ARRAY_SIZE(sun8i_codec_adc_mixer_controls)), +}; + +static const struct snd_soc_dapm_route sun8i_codec_common_routes[] = { + /* Microphone Routes */ + { "Mic1 Amplifier", NULL, "MIC1"}, + { "Mic2 Amplifier", NULL, "MIC2"}, + + /* Left Mixer Routes */ + { "Left Mixer", "DAC Playback Switch", "Left DAC" }, + { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" }, + { "Left Mixer", "Line In Playback Switch", "LINEIN" }, + { "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, + { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, + + /* Right Mixer Routes */ + { "Right Mixer", "DAC Playback Switch", "Right DAC" }, + { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" }, + { "Right Mixer", "Line In Playback Switch", "LINEIN" }, + { "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, + { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, + + /* Left ADC Mixer Routes */ + { "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" }, + { "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" }, + { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" }, + { "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, + { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, + + /* Right ADC Mixer Routes */ + { "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" }, + { "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" }, + { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" }, + { "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, + { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, + + /* ADC Routes */ + { "Left ADC", NULL, "Left ADC Mixer" }, + { "Right ADC", NULL, "Right ADC Mixer" }, +}; + +/* headphone specific controls, widgets, and routes */ +static const DECLARE_TLV_DB_SCALE(sun8i_codec_hp_vol_scale, -6300, 100, 1); +static const struct snd_kcontrol_new sun8i_codec_headphone_controls[] = { + SOC_SINGLE_TLV("Headphone Playback Volume", + SUN8I_ADDA_HP_VOLC, + SUN8I_ADDA_HP_VOLC_HP_VOL, 0x3f, 0, + sun8i_codec_hp_vol_scale), + SOC_DOUBLE("Headphone Playback Switch", + SUN8I_ADDA_DAC_PA_SRC, + SUN8I_ADDA_DAC_PA_SRC_LHPPAMUTE, + SUN8I_ADDA_DAC_PA_SRC_RHPPAMUTE, 1, 0), +}; + +static const char * const sun8i_codec_hp_src_enum_text[] = { + "DAC", "Mixer", +}; + +static SOC_ENUM_DOUBLE_DECL(sun8i_codec_hp_src_enum, + SUN8I_ADDA_DAC_PA_SRC, + SUN8I_ADDA_DAC_PA_SRC_LHPIS, + SUN8I_ADDA_DAC_PA_SRC_RHPIS, + sun8i_codec_hp_src_enum_text); + +static const struct snd_kcontrol_new sun8i_codec_hp_src[] = { + SOC_DAPM_ENUM("Headphone Source Playback Route", + sun8i_codec_hp_src_enum), +}; + +static const struct snd_soc_dapm_widget sun8i_codec_headphone_widgets[] = { + SND_SOC_DAPM_MUX("Headphone Source Playback Route", + SND_SOC_NOPM, 0, 0, sun8i_codec_hp_src), + SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN8I_ADDA_PAEN_HP_CTRL, + SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("HPCOM Protection", SUN8I_ADDA_PAEN_HP_CTRL, + SUN8I_ADDA_PAEN_HP_CTRL_COMPTEN, 0, NULL, 0), + SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPCOM", SUN8I_ADDA_PAEN_HP_CTRL, + SUN8I_ADDA_PAEN_HP_CTRL_HPCOM_FC, 0x3, 0x3, 0), + SND_SOC_DAPM_OUTPUT("HP"), +}; + +static const struct snd_soc_dapm_route sun8i_codec_headphone_routes[] = { + { "Headphone Source Playback Route", "DAC", "Left DAC" }, + { "Headphone Source Playback Route", "DAC", "Right DAC" }, + { "Headphone Source Playback Route", "Mixer", "Left Mixer" }, + { "Headphone Source Playback Route", "Mixer", "Right Mixer" }, + { "Headphone Amp", NULL, "Headphone Source Playback Route" }, + { "HPCOM", NULL, "HPCOM Protection" }, + { "HP", NULL, "Headphone Amp" }, +}; + +static int sun8i_codec_add_headphone(struct snd_soc_component *cmpnt) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); + struct device *dev = cmpnt->dev; + int ret; + + ret = snd_soc_add_component_controls(cmpnt, + sun8i_codec_headphone_controls, + ARRAY_SIZE(sun8i_codec_headphone_controls)); + if (ret) { + dev_err(dev, "Failed to add Headphone controls: %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_headphone_widgets, + ARRAY_SIZE(sun8i_codec_headphone_widgets)); + if (ret) { + dev_err(dev, "Failed to add Headphone DAPM widgets: %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_headphone_routes, + ARRAY_SIZE(sun8i_codec_headphone_routes)); + if (ret) { + dev_err(dev, "Failed to add Headphone DAPM routes: %d\n", ret); + return ret; + } + + return 0; +} + +/* hmic specific widget */ +static const struct snd_soc_dapm_widget sun8i_codec_hmic_widgets[] = { + SND_SOC_DAPM_SUPPLY("HBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, + SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIASEN, + 0, NULL, 0), +}; + +static int sun8i_codec_add_hmic(struct snd_soc_component *cmpnt) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); + struct device *dev = cmpnt->dev; + int ret; + + ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_hmic_widgets, + ARRAY_SIZE(sun8i_codec_hmic_widgets)); + if (ret) + dev_err(dev, "Failed to add Mic3 DAPM widgets: %d\n", ret); + + return ret; +} + +/* line out specific controls, widgets and routes */ +static const DECLARE_TLV_DB_RANGE(sun8i_codec_lineout_vol_scale, + 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), + 2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0), +); +static const struct snd_kcontrol_new sun8i_codec_lineout_controls[] = { + SOC_SINGLE_TLV("Line Out Playback Volume", + SUN8I_ADDA_PHONE_GAIN_CTRL, + SUN8I_ADDA_PHONE_GAIN_CTRL_LINEOUT_VOL, 0x1f, 0, + sun8i_codec_lineout_vol_scale), + SOC_DOUBLE("Line Out Playback Switch", + SUN8I_ADDA_MIC2G_CTRL, + SUN8I_ADDA_MIC2G_CTRL_LINEOUTLEN, + SUN8I_ADDA_MIC2G_CTRL_LINEOUTREN, 1, 0), +}; + +static const char * const sun8i_codec_lineout_src_enum_text[] = { + "Stereo", "Mono Differential", +}; + +static SOC_ENUM_DOUBLE_DECL(sun8i_codec_lineout_src_enum, + SUN8I_ADDA_MIC2G_CTRL, + SUN8I_ADDA_MIC2G_CTRL_LINEOUTLSRC, + SUN8I_ADDA_MIC2G_CTRL_LINEOUTRSRC, + sun8i_codec_lineout_src_enum_text); + +static const struct snd_kcontrol_new sun8i_codec_lineout_src[] = { + SOC_DAPM_ENUM("Line Out Source Playback Route", + sun8i_codec_lineout_src_enum), +}; + +static const struct snd_soc_dapm_widget sun8i_codec_lineout_widgets[] = { + SND_SOC_DAPM_MUX("Line Out Source Playback Route", + SND_SOC_NOPM, 0, 0, sun8i_codec_lineout_src), + /* It is unclear if this is a buffer or gate, model it as a supply */ + SND_SOC_DAPM_SUPPLY("Line Out Enable", SUN8I_ADDA_PAEN_HP_CTRL, + SUN8I_ADDA_PAEN_HP_CTRL_LINEOUTEN, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("LINEOUT"), +}; + +static const struct snd_soc_dapm_route sun8i_codec_lineout_routes[] = { + { "Line Out Source Playback Route", "Stereo", "Left Mixer" }, + { "Line Out Source Playback Route", "Stereo", "Right Mixer" }, + { "Line Out Source Playback Route", "Mono Differential", "Left Mixer" }, + { "Line Out Source Playback Route", "Mono Differential", "Right Mixer" }, + { "LINEOUT", NULL, "Line Out Source Playback Route" }, + { "LINEOUT", NULL, "Line Out Enable", }, +}; + +static int sun8i_codec_add_lineout(struct snd_soc_component *cmpnt) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); + struct device *dev = cmpnt->dev; + int ret; + + ret = snd_soc_add_component_controls(cmpnt, + sun8i_codec_lineout_controls, + ARRAY_SIZE(sun8i_codec_lineout_controls)); + if (ret) { + dev_err(dev, "Failed to add Line Out controls: %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_lineout_widgets, + ARRAY_SIZE(sun8i_codec_lineout_widgets)); + if (ret) { + dev_err(dev, "Failed to add Line Out DAPM widgets: %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_lineout_routes, + ARRAY_SIZE(sun8i_codec_lineout_routes)); + if (ret) { + dev_err(dev, "Failed to add Line Out DAPM routes: %d\n", ret); + return ret; + } + + return 0; +} + +struct sun8i_codec_analog_quirks { + bool has_headphone; + bool has_hmic; + bool has_lineout; +}; + +static const struct sun8i_codec_analog_quirks sun8i_a23_quirks = { + .has_headphone = true, + .has_hmic = true, +}; + +static const struct sun8i_codec_analog_quirks sun8i_h3_quirks = { + .has_lineout = true, +}; + +static int sun8i_codec_analog_cmpnt_probe(struct snd_soc_component *cmpnt) +{ + struct device *dev = cmpnt->dev; + const struct sun8i_codec_analog_quirks *quirks; + int ret; + + /* + * This would never return NULL unless someone directly registers a + * platform device matching this driver's name, without specifying a + * device tree node. + */ + quirks = of_device_get_match_data(dev); + + /* Add controls, widgets, and routes for individual features */ + + if (quirks->has_headphone) { + ret = sun8i_codec_add_headphone(cmpnt); + if (ret) + return ret; + } + + if (quirks->has_hmic) { + ret = sun8i_codec_add_hmic(cmpnt); + if (ret) + return ret; + } + + if (quirks->has_lineout) { + ret = sun8i_codec_add_lineout(cmpnt); + if (ret) + return ret; + } + + return 0; +} + +static const struct snd_soc_component_driver sun8i_codec_analog_cmpnt_drv = { + .controls = sun8i_codec_common_controls, + .num_controls = ARRAY_SIZE(sun8i_codec_common_controls), + .dapm_widgets = sun8i_codec_common_widgets, + .num_dapm_widgets = ARRAY_SIZE(sun8i_codec_common_widgets), + .dapm_routes = sun8i_codec_common_routes, + .num_dapm_routes = ARRAY_SIZE(sun8i_codec_common_routes), + .probe = sun8i_codec_analog_cmpnt_probe, +}; + +static const struct of_device_id sun8i_codec_analog_of_match[] = { + { + .compatible = "allwinner,sun8i-a23-codec-analog", + .data = &sun8i_a23_quirks, + }, + { + .compatible = "allwinner,sun8i-h3-codec-analog", + .data = &sun8i_h3_quirks, + }, + {} +}; +MODULE_DEVICE_TABLE(of, sun8i_codec_analog_of_match); + +static int sun8i_codec_analog_probe(struct platform_device *pdev) +{ + struct resource *res; + struct regmap *regmap; + void __iomem *base; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) { + dev_err(&pdev->dev, "Failed to map the registers\n"); + return PTR_ERR(base); + } + + regmap = devm_regmap_init(&pdev->dev, NULL, base, &adda_pr_regmap_cfg); + if (IS_ERR(regmap)) { + dev_err(&pdev->dev, "Failed to create regmap\n"); + return PTR_ERR(regmap); + } + + return devm_snd_soc_register_component(&pdev->dev, + &sun8i_codec_analog_cmpnt_drv, + NULL, 0); +} + +static struct platform_driver sun8i_codec_analog_driver = { + .driver = { + .name = "sun8i-codec-analog", + .of_match_table = sun8i_codec_analog_of_match, + }, + .probe = sun8i_codec_analog_probe, +}; +module_platform_driver(sun8i_codec_analog_driver); + +MODULE_DESCRIPTION("Allwinner internal codec analog controls driver"); +MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:sun8i-codec-analog"); diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index a6768f832c6f..efbe8d4c019e 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -138,3 +138,14 @@ config SND_SOC_TEGRA_RT5677 help Say Y or M here if you want to add support for SoC audio on Tegra boards using the RT5677 codec, such as Ryu. + +config SND_SOC_TEGRA_SGTL5000 + tristate "SoC Audio support for Tegra boards using a SGTL5000 codec" + depends on SND_SOC_TEGRA && I2C && GPIOLIB + select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC + select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC + select SND_SOC_SGTL5000 + help + Say Y or M here if you want to add support for SoC audio on Tegra + boards using the SGTL5000 codec, such as Apalis T30, Apalis TK1 or + Colibri T30. diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index 9171655ad843..f214a3fd0024 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -26,6 +26,7 @@ snd-soc-tegra-wm9712-objs := tegra_wm9712.o snd-soc-tegra-trimslice-objs := trimslice.o snd-soc-tegra-alc5632-objs := tegra_alc5632.o snd-soc-tegra-max98090-objs := tegra_max98090.o +snd-soc-tegra-sgtl5000-objs := tegra_sgtl5000.o obj-$(CONFIG_SND_SOC_TEGRA_RT5640) += snd-soc-tegra-rt5640.o obj-$(CONFIG_SND_SOC_TEGRA_RT5677) += snd-soc-tegra-rt5677.o @@ -35,3 +36,4 @@ obj-$(CONFIG_SND_SOC_TEGRA_WM9712) += snd-soc-tegra-wm9712.o obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o obj-$(CONFIG_SND_SOC_TEGRA_ALC5632) += snd-soc-tegra-alc5632.o obj-$(CONFIG_SND_SOC_TEGRA_MAX98090) += snd-soc-tegra-max98090.o +obj-$(CONFIG_SND_SOC_TEGRA_SGTL5000) += snd-soc-tegra-sgtl5000.o
\ No newline at end of file diff --git a/sound/soc/tegra/tegra_alc5632.c b/sound/soc/tegra/tegra_alc5632.c index deb597f7c302..eead6e7f205b 100644 --- a/sound/soc/tegra/tegra_alc5632.c +++ b/sound/soc/tegra/tegra_alc5632.c @@ -65,7 +65,7 @@ static int tegra_alc5632_asoc_hw_params(struct snd_pcm_substream *substream, return 0; } -static struct snd_soc_ops tegra_alc5632_asoc_ops = { +static const struct snd_soc_ops tegra_alc5632_asoc_ops = { .hw_params = tegra_alc5632_asoc_hw_params, }; diff --git a/sound/soc/tegra/tegra_max98090.c b/sound/soc/tegra/tegra_max98090.c index 902da36581d1..a403db6d563e 100644 --- a/sound/soc/tegra/tegra_max98090.c +++ b/sound/soc/tegra/tegra_max98090.c @@ -93,7 +93,7 @@ static int tegra_max98090_asoc_hw_params(struct snd_pcm_substream *substream, return 0; } -static struct snd_soc_ops tegra_max98090_ops = { +static const struct snd_soc_ops tegra_max98090_ops = { .hw_params = tegra_max98090_asoc_hw_params, }; diff --git a/sound/soc/tegra/tegra_rt5640.c b/sound/soc/tegra/tegra_rt5640.c index 773daecaa5e8..25b9fc03ba62 100644 --- a/sound/soc/tegra/tegra_rt5640.c +++ b/sound/soc/tegra/tegra_rt5640.c @@ -1,5 +1,5 @@ /* -* tegra_rt5640.c - Tegra machine ASoC driver for boards using WM8903 codec. +* tegra_rt5640.c - Tegra machine ASoC driver for boards using RT5640 codec. * * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. * @@ -76,7 +76,7 @@ static int tegra_rt5640_asoc_hw_params(struct snd_pcm_substream *substream, return 0; } -static struct snd_soc_ops tegra_rt5640_ops = { +static const struct snd_soc_ops tegra_rt5640_ops = { .hw_params = tegra_rt5640_asoc_hw_params, }; diff --git a/sound/soc/tegra/tegra_rt5677.c b/sound/soc/tegra/tegra_rt5677.c index 1470873ecde6..ebf58d0e0f10 100644 --- a/sound/soc/tegra/tegra_rt5677.c +++ b/sound/soc/tegra/tegra_rt5677.c @@ -93,7 +93,7 @@ static int tegra_rt5677_event_hp(struct snd_soc_dapm_widget *w, return 0; } -static struct snd_soc_ops tegra_rt5677_ops = { +static const struct snd_soc_ops tegra_rt5677_ops = { .hw_params = tegra_rt5677_asoc_hw_params, }; diff --git a/sound/soc/tegra/tegra_sgtl5000.c b/sound/soc/tegra/tegra_sgtl5000.c new file mode 100644 index 000000000000..4bbab098f50b --- /dev/null +++ b/sound/soc/tegra/tegra_sgtl5000.c @@ -0,0 +1,212 @@ +/* + * tegra_sgtl5000.c - Tegra machine ASoC driver for boards using SGTL5000 codec + * + * Author: Marcel Ziswiler <marcel@ziswiler.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Based on code copyright/by: + * + * Copyright (C) 2010-2012 - NVIDIA, Inc. + * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd. + * Copyright 2007 Wolfson Microelectronics PLC. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "../codecs/sgtl5000.h" + +#include "tegra_asoc_utils.h" + +#define DRV_NAME "tegra-snd-sgtl5000" + +struct tegra_sgtl5000 { + struct tegra_asoc_utils_data util_data; +}; + +static int tegra_sgtl5000_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_card *card = rtd->card; + struct tegra_sgtl5000 *machine = snd_soc_card_get_drvdata(card); + int srate, mclk; + int err; + + srate = params_rate(params); + switch (srate) { + case 11025: + case 22050: + case 44100: + case 88200: + mclk = 11289600; + break; + default: + mclk = 12288000; + break; + } + + err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk); + if (err < 0) { + dev_err(card->dev, "Can't configure clocks\n"); + return err; + } + + err = snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, mclk, + SND_SOC_CLOCK_IN); + if (err < 0) { + dev_err(card->dev, "codec_dai clock not set\n"); + return err; + } + + return 0; +} + +static const struct snd_soc_ops tegra_sgtl5000_ops = { + .hw_params = tegra_sgtl5000_hw_params, +}; + +static const struct snd_soc_dapm_widget tegra_sgtl5000_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_LINE("Line In Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), +}; + +static struct snd_soc_dai_link tegra_sgtl5000_dai = { + .name = "sgtl5000", + .stream_name = "HiFi", + .codec_dai_name = "sgtl5000", + .ops = &tegra_sgtl5000_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, +}; + +static struct snd_soc_card snd_soc_tegra_sgtl5000 = { + .name = "tegra-sgtl5000", + .owner = THIS_MODULE, + .dai_link = &tegra_sgtl5000_dai, + .num_links = 1, + .dapm_widgets = tegra_sgtl5000_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra_sgtl5000_dapm_widgets), + .fully_routed = true, +}; + +static int tegra_sgtl5000_driver_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct snd_soc_card *card = &snd_soc_tegra_sgtl5000; + struct tegra_sgtl5000 *machine; + int ret; + + machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_sgtl5000), + GFP_KERNEL); + if (!machine) { + dev_err(&pdev->dev, "Can't allocate tegra_sgtl5000 struct\n"); + return -ENOMEM; + } + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, machine); + + ret = snd_soc_of_parse_card_name(card, "nvidia,model"); + if (ret) + goto err; + + ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing"); + if (ret) + goto err; + + tegra_sgtl5000_dai.codec_of_node = of_parse_phandle(np, + "nvidia,audio-codec", 0); + if (!tegra_sgtl5000_dai.codec_of_node) { + dev_err(&pdev->dev, + "Property 'nvidia,audio-codec' missing or invalid\n"); + ret = -EINVAL; + goto err; + } + + tegra_sgtl5000_dai.cpu_of_node = of_parse_phandle(np, + "nvidia,i2s-controller", 0); + if (!tegra_sgtl5000_dai.cpu_of_node) { + dev_err(&pdev->dev, + "Property 'nvidia,i2s-controller' missing/invalid\n"); + ret = -EINVAL; + goto err; + } + + tegra_sgtl5000_dai.platform_of_node = tegra_sgtl5000_dai.cpu_of_node; + + ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev); + if (ret) + goto err; + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto err_fini_utils; + } + + return 0; + +err_fini_utils: + tegra_asoc_utils_fini(&machine->util_data); +err: + return ret; +} + +static int tegra_sgtl5000_driver_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct tegra_sgtl5000 *machine = snd_soc_card_get_drvdata(card); + int ret; + + ret = snd_soc_unregister_card(card); + + tegra_asoc_utils_fini(&machine->util_data); + + return ret; +} + +static const struct of_device_id tegra_sgtl5000_of_match[] = { + { .compatible = "nvidia,tegra-audio-sgtl5000", }, + { /* sentinel */ }, +}; + +static struct platform_driver tegra_sgtl5000_driver = { + .driver = { + .name = DRV_NAME, + .pm = &snd_soc_pm_ops, + .of_match_table = tegra_sgtl5000_of_match, + }, + .probe = tegra_sgtl5000_driver_probe, + .remove = tegra_sgtl5000_driver_remove, +}; +module_platform_driver(tegra_sgtl5000_driver); + +MODULE_AUTHOR("Marcel Ziswiler <marcel@ziswiler.com>"); +MODULE_DESCRIPTION("Tegra SGTL5000 machine ASoC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, tegra_sgtl5000_of_match); diff --git a/sound/soc/tegra/tegra_wm8753.c b/sound/soc/tegra/tegra_wm8753.c index f0cd01dbfc38..bdedd1028569 100644 --- a/sound/soc/tegra/tegra_wm8753.c +++ b/sound/soc/tegra/tegra_wm8753.c @@ -89,7 +89,7 @@ static int tegra_wm8753_hw_params(struct snd_pcm_substream *substream, return 0; } -static struct snd_soc_ops tegra_wm8753_ops = { +static const struct snd_soc_ops tegra_wm8753_ops = { .hw_params = tegra_wm8753_hw_params, }; diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c index e485278e027a..2013e9c4bba0 100644 --- a/sound/soc/tegra/tegra_wm8903.c +++ b/sound/soc/tegra/tegra_wm8903.c @@ -96,7 +96,7 @@ static int tegra_wm8903_hw_params(struct snd_pcm_substream *substream, return 0; } -static struct snd_soc_ops tegra_wm8903_ops = { +static const struct snd_soc_ops tegra_wm8903_ops = { .hw_params = tegra_wm8903_hw_params, }; diff --git a/sound/soc/tegra/trimslice.c b/sound/soc/tegra/trimslice.c index 2cea203c4f5f..870f84ab5005 100644 --- a/sound/soc/tegra/trimslice.c +++ b/sound/soc/tegra/trimslice.c @@ -74,7 +74,7 @@ static int trimslice_asoc_hw_params(struct snd_pcm_substream *substream, return 0; } -static struct snd_soc_ops trimslice_asoc_ops = { +static const struct snd_soc_ops trimslice_asoc_ops = { .hw_params = trimslice_asoc_hw_params, }; diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c index 6d5698b25bd4..b343efd9be5b 100644 --- a/sound/soc/ux500/ux500_msp_dai.c +++ b/sound/soc/ux500/ux500_msp_dai.c @@ -187,7 +187,7 @@ static int setup_clocking(struct snd_soc_dai *dai, default: dev_err(dai->dev, - "%s: Error: Unsopported inversion (fmt = 0x%x)!\n", + "%s: Error: Unsupported inversion (fmt = 0x%x)!\n", __func__, fmt); return -EINVAL; @@ -218,7 +218,7 @@ static int setup_clocking(struct snd_soc_dai *dai, break; default: - dev_err(dai->dev, "%s: Error: Unsopported master (fmt = 0x%x)!\n", + dev_err(dai->dev, "%s: Error: Unsupported master (fmt = 0x%x)!\n", __func__, fmt); return -EINVAL; @@ -374,7 +374,7 @@ static int setup_msp_config(struct snd_pcm_substream *substream, break; default: - dev_err(dai->dev, "%s: Error: Unsopported format (%d)!\n", + dev_err(dai->dev, "%s: Error: Unsupported format (%d)!\n", __func__, fmt); return -EINVAL; } diff --git a/sound/soc/zte/Kconfig b/sound/soc/zte/Kconfig index c47eb25e441f..6d8a90d36315 100644 --- a/sound/soc/zte/Kconfig +++ b/sound/soc/zte/Kconfig @@ -1,17 +1,17 @@ -config ZX296702_SPDIF - tristate "ZX296702 spdif" - depends on SOC_ZX296702 || COMPILE_TEST +config ZX_SPDIF + tristate "ZTE ZX SPDIF Driver Support" + depends on ARCH_ZX || COMPILE_TEST depends on COMMON_CLK select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for codecs attached to the - zx296702 spdif interface + ZTE ZX SPDIF interface -config ZX296702_I2S - tristate "ZX296702 i2s" - depends on SOC_ZX296702 || COMPILE_TEST +config ZX_I2S + tristate "ZTE ZX I2S Driver Support" + depends on ARCH_ZX || COMPILE_TEST depends on COMMON_CLK select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for codecs attached to the - zx296702 i2s interface + ZTE ZX I2S interface diff --git a/sound/soc/zte/Makefile b/sound/soc/zte/Makefile index 254ed2c8c1a0..77768f5fd10c 100644 --- a/sound/soc/zte/Makefile +++ b/sound/soc/zte/Makefile @@ -1,2 +1,2 @@ -obj-$(CONFIG_ZX296702_SPDIF) += zx296702-spdif.o -obj-$(CONFIG_ZX296702_I2S) += zx296702-i2s.o +obj-$(CONFIG_ZX_SPDIF) += zx-spdif.o +obj-$(CONFIG_ZX_I2S) += zx-i2s.o diff --git a/sound/soc/zte/zx296702-i2s.c b/sound/soc/zte/zx-i2s.c index 1cad93dc1fcf..1cad93dc1fcf 100644 --- a/sound/soc/zte/zx296702-i2s.c +++ b/sound/soc/zte/zx-i2s.c diff --git a/sound/soc/zte/zx296702-spdif.c b/sound/soc/zte/zx-spdif.c index 26265ce4caca..9fa6463ce5d7 100644 --- a/sound/soc/zte/zx296702-spdif.c +++ b/sound/soc/zte/zx-spdif.c @@ -71,7 +71,7 @@ #define ZX_VALID_RIGHT_TRACK (2 << 0) #define ZX_VALID_TRACK_MASK (3 << 0) -#define ZX_SPDIF_CLK_RAT (4 * 32) +#define ZX_SPDIF_CLK_RAT (2 * 32) struct zx_spdif_info { struct snd_dmaengine_dai_dma_data dma_data; diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c index 0190cb6332f2..52063b262667 100644 --- a/sound/sparc/dbri.c +++ b/sound/sparc/dbri.c @@ -304,7 +304,7 @@ struct snd_dbri { spinlock_t lock; struct dbri_dma *dma; /* Pointer to our DMA block */ - u32 dma_dvma; /* DBRI visible DMA address */ + dma_addr_t dma_dvma; /* DBRI visible DMA address */ void __iomem *regs; /* dbri HW regs */ int dbri_irqp; /* intr queue pointer */ @@ -657,12 +657,14 @@ static void dbri_cmdwait(struct snd_dbri *dbri) */ static s32 *dbri_cmdlock(struct snd_dbri *dbri, int len) { + u32 dvma_addr = (u32)dbri->dma_dvma; + /* Space for 2 WAIT cmds (replaced later by 1 JUMP cmd) */ len += 2; spin_lock(&dbri->cmdlock); if (dbri->cmdptr - dbri->dma->cmd + len < DBRI_NO_CMDS - 2) return dbri->cmdptr + 2; - else if (len < sbus_readl(dbri->regs + REG8) - dbri->dma_dvma) + else if (len < sbus_readl(dbri->regs + REG8) - dvma_addr) return dbri->dma->cmd; else printk(KERN_ERR "DBRI: no space for commands."); @@ -680,6 +682,7 @@ static s32 *dbri_cmdlock(struct snd_dbri *dbri, int len) */ static void dbri_cmdsend(struct snd_dbri *dbri, s32 *cmd, int len) { + u32 dvma_addr = (u32)dbri->dma_dvma; s32 tmp, addr; static int wait_id = 0; @@ -689,7 +692,7 @@ static void dbri_cmdsend(struct snd_dbri *dbri, s32 *cmd, int len) *(cmd+1) = DBRI_CMD(D_WAIT, 1, wait_id); /* Replace the last command with JUMP */ - addr = dbri->dma_dvma + (cmd - len - dbri->dma->cmd) * sizeof(s32); + addr = dvma_addr + (cmd - len - dbri->dma->cmd) * sizeof(s32); *(dbri->cmdptr+1) = addr; *(dbri->cmdptr) = DBRI_CMD(D_JUMP, 0, 0); @@ -747,6 +750,7 @@ static void dbri_reset(struct snd_dbri *dbri) /* Lock must not be held before calling this */ static void dbri_initialize(struct snd_dbri *dbri) { + u32 dvma_addr = (u32)dbri->dma_dvma; s32 *cmd; u32 dma_addr; unsigned long flags; @@ -764,7 +768,7 @@ static void dbri_initialize(struct snd_dbri *dbri) /* * Initialize the interrupt ring buffer. */ - dma_addr = dbri->dma_dvma + dbri_dma_off(intr, 0); + dma_addr = dvma_addr + dbri_dma_off(intr, 0); dbri->dma->intr[0] = dma_addr; dbri->dbri_irqp = 1; /* @@ -778,7 +782,7 @@ static void dbri_initialize(struct snd_dbri *dbri) dbri->cmdptr = cmd; *(cmd++) = DBRI_CMD(D_WAIT, 1, 0); *(cmd++) = DBRI_CMD(D_WAIT, 1, 0); - dma_addr = dbri->dma_dvma + dbri_dma_off(cmd, 0); + dma_addr = dvma_addr + dbri_dma_off(cmd, 0); sbus_writel(dma_addr, dbri->regs + REG8); spin_unlock(&dbri->cmdlock); @@ -1077,6 +1081,7 @@ static void recv_fixed(struct snd_dbri *dbri, int pipe, volatile __u32 *ptr) static int setup_descs(struct snd_dbri *dbri, int streamno, unsigned int period) { struct dbri_streaminfo *info = &dbri->stream_info[streamno]; + u32 dvma_addr = (u32)dbri->dma_dvma; __u32 dvma_buffer; int desc; int len; @@ -1177,7 +1182,7 @@ static int setup_descs(struct snd_dbri *dbri, int streamno, unsigned int period) else { dbri->next_desc[last_desc] = desc; dbri->dma->desc[last_desc].nda = - dbri->dma_dvma + dbri_dma_off(desc, desc); + dvma_addr + dbri_dma_off(desc, desc); } last_desc = desc; @@ -1192,7 +1197,7 @@ static int setup_descs(struct snd_dbri *dbri, int streamno, unsigned int period) } dbri->dma->desc[last_desc].nda = - dbri->dma_dvma + dbri_dma_off(desc, first_desc); + dvma_addr + dbri_dma_off(desc, first_desc); dbri->next_desc[last_desc] = first_desc; dbri->pipes[info->pipe].first_desc = first_desc; dbri->pipes[info->pipe].desc = first_desc; @@ -1697,6 +1702,7 @@ interrupts are disabled. static void xmit_descs(struct snd_dbri *dbri) { struct dbri_streaminfo *info; + u32 dvma_addr; s32 *cmd; unsigned long flags; int first_td; @@ -1704,6 +1710,7 @@ static void xmit_descs(struct snd_dbri *dbri) if (dbri == NULL) return; /* Disabled */ + dvma_addr = (u32)dbri->dma_dvma; info = &dbri->stream_info[DBRI_REC]; spin_lock_irqsave(&dbri->lock, flags); @@ -1718,7 +1725,7 @@ static void xmit_descs(struct snd_dbri *dbri) *(cmd++) = DBRI_CMD(D_SDP, 0, dbri->pipes[info->pipe].sdp | D_SDP_P | D_SDP_EVERY | D_SDP_C); - *(cmd++) = dbri->dma_dvma + + *(cmd++) = dvma_addr + dbri_dma_off(desc, first_td); dbri_cmdsend(dbri, cmd, 2); @@ -1740,7 +1747,7 @@ static void xmit_descs(struct snd_dbri *dbri) *(cmd++) = DBRI_CMD(D_SDP, 0, dbri->pipes[info->pipe].sdp | D_SDP_P | D_SDP_EVERY | D_SDP_C); - *(cmd++) = dbri->dma_dvma + + *(cmd++) = dvma_addr + dbri_dma_off(desc, first_td); dbri_cmdsend(dbri, cmd, 2); @@ -2539,7 +2546,7 @@ static int snd_dbri_create(struct snd_card *card, if (!dbri->dma) return -ENOMEM; - dprintk(D_GEN, "DMA Cmd Block 0x%p (0x%08x)\n", + dprintk(D_GEN, "DMA Cmd Block 0x%p (%pad)\n", dbri->dma, dbri->dma_dvma); /* Map the registers into memory. */ diff --git a/sound/usb/caiaq/audio.c b/sound/usb/caiaq/audio.c index 327f8642ca80..8f66ba730d69 100644 --- a/sound/usb/caiaq/audio.c +++ b/sound/usb/caiaq/audio.c @@ -739,7 +739,6 @@ static struct urb **alloc_urbs(struct snd_usb_caiaqdev *cdev, int dir, int *ret) for (i = 0; i < N_URBS; i++) { urbs[i] = usb_alloc_urb(FRAMES_PER_URB, GFP_KERNEL); if (!urbs[i]) { - dev_err(dev, "unable to usb_alloc_urb(), OOM!?\n"); *ret = -ENOMEM; return urbs; } diff --git a/sound/usb/card.c b/sound/usb/card.c index 9e5276d6dda0..f36cb068dad3 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -206,7 +206,6 @@ static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int int if (! snd_usb_parse_audio_interface(chip, interface)) { usb_set_interface(dev, interface, 0); /* reset the current interface */ usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L); - return -EINVAL; } return 0; @@ -315,7 +314,8 @@ static int snd_usb_audio_free(struct snd_usb_audio *chip) snd_usb_endpoint_free(ep); mutex_destroy(&chip->mutex); - dev_set_drvdata(&chip->dev->dev, NULL); + if (!atomic_read(&chip->shutdown)) + dev_set_drvdata(&chip->dev->dev, NULL); kfree(chip); return 0; } diff --git a/sound/usb/card.h b/sound/usb/card.h index 71778ca4b26a..111b0f009afa 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -92,7 +92,7 @@ struct snd_usb_endpoint { unsigned int curframesize; /* current packet size in frames (for capture) */ unsigned int syncmaxsize; /* sync endpoint packet size */ unsigned int fill_max:1; /* fill max packet size always */ - unsigned int udh01_fb_quirk:1; /* corrupted feedback data */ + unsigned int tenor_fb_quirk:1; /* corrupted feedback data */ unsigned int datainterval; /* log_2 of data packet interval */ unsigned int syncinterval; /* P for adaptive mode, 0 otherwise */ unsigned char silence_value; diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index c07a7eda42a2..c90607ebe155 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -384,6 +384,9 @@ static void snd_complete_urb(struct urb *urb) if (unlikely(atomic_read(&ep->chip->shutdown))) goto exit_clear; + if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags))) + goto exit_clear; + if (usb_pipeout(ep->pipe)) { retire_outbound_urb(ep, ctx); /* can be stopped during retire callback */ @@ -502,10 +505,6 @@ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip, ep->syncinterval = 3; ep->syncmaxsize = le16_to_cpu(get_endpoint(alts, 1)->wMaxPacketSize); - - if (chip->usb_id == USB_ID(0x0644, 0x8038) /* TEAC UD-H01 */ && - ep->syncmaxsize == 4) - ep->udh01_fb_quirk = 1; } list_add_tail(&ep->list, &chip->ep_list); @@ -538,6 +537,11 @@ static int wait_clear_urbs(struct snd_usb_endpoint *ep) alive, ep->ep_num); clear_bit(EP_FLAG_STOPPING, &ep->flags); + ep->data_subs = NULL; + ep->sync_slave = NULL; + ep->retire_data_urb = NULL; + ep->prepare_data_urb = NULL; + return 0; } @@ -634,10 +638,24 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep, ep->datainterval = fmt->datainterval; ep->stride = frame_bits >> 3; - ep->silence_value = pcm_format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0; - /* assume max. frequency is 25% higher than nominal */ - ep->freqmax = ep->freqn + (ep->freqn >> 2); + switch (pcm_format) { + case SNDRV_PCM_FORMAT_U8: + ep->silence_value = 0x80; + break; + case SNDRV_PCM_FORMAT_DSD_U8: + case SNDRV_PCM_FORMAT_DSD_U16_LE: + case SNDRV_PCM_FORMAT_DSD_U32_LE: + case SNDRV_PCM_FORMAT_DSD_U16_BE: + case SNDRV_PCM_FORMAT_DSD_U32_BE: + ep->silence_value = 0x69; + break; + default: + ep->silence_value = 0; + } + + /* assume max. frequency is 50% higher than nominal */ + ep->freqmax = ep->freqn + (ep->freqn >> 1); /* Round up freqmax to nearest integer in order to calculate maximum * packet size, which must represent a whole number of frames. * This is accomplished by adding 0x0.ffff before converting the @@ -902,9 +920,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, /** * snd_usb_endpoint_start: start an snd_usb_endpoint * - * @ep: the endpoint to start - * @can_sleep: flag indicating whether the operation is executed in - * non-atomic context + * @ep: the endpoint to start * * A call to this function will increment the use count of the endpoint. * In case it is not already running, the URBs for this endpoint will be @@ -914,7 +930,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, * * Returns an error if the URB submission failed, 0 in all other cases. */ -int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, bool can_sleep) +int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) { int err; unsigned int i; @@ -928,8 +944,6 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, bool can_sleep) /* just to be sure */ deactivate_urbs(ep, false); - if (can_sleep) - wait_clear_urbs(ep); ep->active_mask = 0; ep->unlink_mask = 0; @@ -1010,10 +1024,6 @@ void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep) if (--ep->use_count == 0) { deactivate_urbs(ep, false); - ep->data_subs = NULL; - ep->sync_slave = NULL; - ep->retire_data_urb = NULL; - ep->prepare_data_urb = NULL; set_bit(EP_FLAG_STOPPING, &ep->flags); } } @@ -1171,15 +1181,16 @@ void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, if (f == 0) return; - if (unlikely(sender->udh01_fb_quirk)) { + if (unlikely(sender->tenor_fb_quirk)) { /* - * The TEAC UD-H01 firmware sometimes changes the feedback value + * Devices based on Tenor 8802 chipsets (TEAC UD-H01 + * and others) sometimes change the feedback value * by +/- 0x1.0000. */ if (f < ep->freqn - 0x8000) - f += 0x10000; + f += 0xf000; else if (f > ep->freqn + 0x8000) - f -= 0x10000; + f -= 0xf000; } else if (unlikely(ep->freqshift == INT_MIN)) { /* * The first time we see a feedback value, determine its format diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index 6428392d8f62..584f295d7c77 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -18,7 +18,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, struct audioformat *fmt, struct snd_usb_endpoint *sync_ep); -int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, bool can_sleep); +int snd_usb_endpoint_start(struct snd_usb_endpoint *ep); void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep); void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep); int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep); diff --git a/sound/usb/format.c b/sound/usb/format.c index 789d19ec035d..2c44386e5569 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -176,10 +176,8 @@ static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audiof int r, idx; fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL); - if (fp->rate_table == NULL) { - usb_audio_err(chip, "cannot malloc\n"); + if (fp->rate_table == NULL) return -ENOMEM; - } fp->nr_rates = 0; fp->rate_min = fp->rate_max = 0; diff --git a/sound/usb/hiface/pcm.c b/sound/usb/hiface/pcm.c index 2c44139b4041..33db205dd12b 100644 --- a/sound/usb/hiface/pcm.c +++ b/sound/usb/hiface/pcm.c @@ -445,6 +445,8 @@ static int hiface_pcm_prepare(struct snd_pcm_substream *alsa_sub) mutex_lock(&rt->stream_mutex); + hiface_pcm_stream_stop(rt); + sub->dma_off = 0; sub->period_off = 0; diff --git a/sound/usb/line6/Kconfig b/sound/usb/line6/Kconfig index f4585d378ef3..39b400392d71 100644 --- a/sound/usb/line6/Kconfig +++ b/sound/usb/line6/Kconfig @@ -2,6 +2,7 @@ config SND_USB_LINE6 tristate select SND_RAWMIDI select SND_PCM + select SND_HWDEP config SND_USB_POD tristate "Line 6 POD USB support" @@ -21,10 +22,10 @@ config SND_USB_POD re-amping) config SND_USB_PODHD - tristate "Line 6 POD HD300/400/500 USB support" + tristate "Line 6 POD X3/HD300/400/500 USB support" select SND_USB_LINE6 help - This is a driver for POD HD300, 400 and 500 devices. + This is a driver for POD X3, HD300, 400 and 500 devices. config SND_USB_TONEPORT tristate "TonePort GX, UX1 and UX2 USB support" diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c index f518fbbe88de..7c812565f90d 100644 --- a/sound/usb/line6/capture.c +++ b/sound/usb/line6/capture.c @@ -29,10 +29,10 @@ static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm) int ret; struct urb *urb_in; - index = - find_first_zero_bit(&line6pcm->in.active_urbs, LINE6_ISO_BUFFERS); + index = find_first_zero_bit(&line6pcm->in.active_urbs, + line6pcm->line6->iso_buffers); - if (index < 0 || index >= LINE6_ISO_BUFFERS) { + if (index < 0 || index >= line6pcm->line6->iso_buffers) { dev_err(line6pcm->line6->ifcdev, "no free URB found\n"); return -EINVAL; } @@ -44,13 +44,13 @@ static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm) struct usb_iso_packet_descriptor *fin = &urb_in->iso_frame_desc[i]; fin->offset = urb_size; - fin->length = line6pcm->max_packet_size; - urb_size += line6pcm->max_packet_size; + fin->length = line6pcm->max_packet_size_in; + urb_size += line6pcm->max_packet_size_in; } urb_in->transfer_buffer = line6pcm->in.buffer + - index * LINE6_ISO_PACKETS * line6pcm->max_packet_size; + index * LINE6_ISO_PACKETS * line6pcm->max_packet_size_in; urb_in->transfer_buffer_length = urb_size; urb_in->context = line6pcm; @@ -73,7 +73,7 @@ int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm) { int ret = 0, i; - for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { + for (i = 0; i < line6pcm->line6->iso_buffers; ++i) { ret = submit_audio_in_urb(line6pcm); if (ret < 0) break; @@ -90,7 +90,9 @@ void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize) struct snd_pcm_substream *substream = get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE); struct snd_pcm_runtime *runtime = substream->runtime; - const int bytes_per_frame = line6pcm->properties->bytes_per_frame; + const int bytes_per_frame = + line6pcm->properties->bytes_per_channel * + line6pcm->properties->capture_hw.channels_max; int frames = fsize / bytes_per_frame; if (runtime == NULL) @@ -154,7 +156,7 @@ static void audio_in_callback(struct urb *urb) line6pcm->in.last_frame = urb->start_frame; /* find index of URB */ - for (index = 0; index < LINE6_ISO_BUFFERS; ++index) + for (index = 0; index < line6pcm->line6->iso_buffers; ++index) if (urb == line6pcm->in.urbs[index]) break; @@ -173,17 +175,27 @@ static void audio_in_callback(struct urb *urb) fbuf = urb->transfer_buffer + fin->offset; fsize = fin->actual_length; - if (fsize > line6pcm->max_packet_size) { + if (fsize > line6pcm->max_packet_size_in) { dev_err(line6pcm->line6->ifcdev, "driver and/or device bug: packet too large (%d > %d)\n", - fsize, line6pcm->max_packet_size); + fsize, line6pcm->max_packet_size_in); } length += fsize; - /* the following assumes LINE6_ISO_PACKETS == 1: */ + BUILD_BUG_ON_MSG(LINE6_ISO_PACKETS != 1, + "The following code assumes LINE6_ISO_PACKETS == 1"); + /* TODO: + * Also, if iso_buffers != 2, the prev frame is almost random at + * playback side. + * This needs to be redesigned. It should be "stable", but we may + * experience sync problems on such high-speed configs. + */ + line6pcm->prev_fbuf = fbuf; - line6pcm->prev_fsize = fsize; + line6pcm->prev_fsize = fsize / + (line6pcm->properties->bytes_per_channel * + line6pcm->properties->capture_hw.channels_max); if (!test_bit(LINE6_STREAM_IMPULSE, &line6pcm->in.running) && test_bit(LINE6_STREAM_PCM, &line6pcm->in.running) && @@ -220,6 +232,8 @@ static int snd_line6_capture_open(struct snd_pcm_substream *substream) if (err < 0) return err; + line6_pcm_acquire(line6pcm, LINE6_STREAM_CAPTURE_HELPER, false); + runtime->hw = line6pcm->properties->capture_hw; return 0; } @@ -227,6 +241,9 @@ static int snd_line6_capture_open(struct snd_pcm_substream *substream) /* close capture callback */ static int snd_line6_capture_close(struct snd_pcm_substream *substream) { + struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); + + line6_pcm_release(line6pcm, LINE6_STREAM_CAPTURE_HELPER); return 0; } @@ -247,8 +264,13 @@ int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm) struct usb_line6 *line6 = line6pcm->line6; int i; + line6pcm->in.urbs = kzalloc( + sizeof(struct urb *) * line6->iso_buffers, GFP_KERNEL); + if (line6pcm->in.urbs == NULL) + return -ENOMEM; + /* create audio URBs and fill in constant values: */ - for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { + for (i = 0; i < line6->iso_buffers; ++i) { struct urb *urb; /* URB for audio in: */ diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c index 81b7da8e56d3..90009c0b3a92 100644 --- a/sound/usb/line6/driver.c +++ b/sound/usb/line6/driver.c @@ -17,6 +17,7 @@ #include <sound/core.h> #include <sound/initval.h> +#include <sound/hwdep.h> #include "capture.h" #include "driver.h" @@ -29,7 +30,7 @@ /* This is Line 6's MIDI manufacturer ID. */ -const unsigned char line6_midi_id[] = { +const unsigned char line6_midi_id[3] = { 0x00, 0x01, 0x0c }; EXPORT_SYMBOL_GPL(line6_midi_id); @@ -66,10 +67,17 @@ static int line6_start_listen(struct usb_line6 *line6) { int err; - usb_fill_int_urb(line6->urb_listen, line6->usbdev, - usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r), - line6->buffer_listen, LINE6_BUFSIZE_LISTEN, - line6_data_received, line6, line6->interval); + if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) { + usb_fill_int_urb(line6->urb_listen, line6->usbdev, + usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r), + line6->buffer_listen, LINE6_BUFSIZE_LISTEN, + line6_data_received, line6, line6->interval); + } else { + usb_fill_bulk_urb(line6->urb_listen, line6->usbdev, + usb_rcvbulkpipe(line6->usbdev, line6->properties->ep_ctrl_r), + line6->buffer_listen, LINE6_BUFSIZE_LISTEN, + line6_data_received, line6); + } line6->urb_listen->actual_length = 0; err = usb_submit_urb(line6->urb_listen, GFP_ATOMIC); return err; @@ -90,6 +98,7 @@ static int line6_send_raw_message(struct usb_line6 *line6, const char *buffer, int size) { int i, done = 0; + const struct line6_properties *properties = line6->properties; for (i = 0; i < size; i += line6->max_packet_size) { int partial; @@ -97,15 +106,21 @@ static int line6_send_raw_message(struct usb_line6 *line6, const char *buffer, int frag_size = min(line6->max_packet_size, size - i); int retval; - retval = usb_interrupt_msg(line6->usbdev, - usb_sndintpipe(line6->usbdev, - line6->properties->ep_ctrl_w), - (char *)frag_buf, frag_size, - &partial, LINE6_TIMEOUT * HZ); + if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) { + retval = usb_interrupt_msg(line6->usbdev, + usb_sndintpipe(line6->usbdev, properties->ep_ctrl_w), + (char *)frag_buf, frag_size, + &partial, LINE6_TIMEOUT * HZ); + } else { + retval = usb_bulk_msg(line6->usbdev, + usb_sndbulkpipe(line6->usbdev, properties->ep_ctrl_w), + (char *)frag_buf, frag_size, + &partial, LINE6_TIMEOUT * HZ); + } if (retval) { dev_err(line6->ifcdev, - "usb_interrupt_msg failed (%d)\n", retval); + "usb_bulk_msg failed (%d)\n", retval); break; } @@ -140,10 +155,17 @@ static int line6_send_raw_message_async_part(struct message *msg, int done = msg->done; int bytes = min(msg->size - done, line6->max_packet_size); - usb_fill_int_urb(urb, line6->usbdev, - usb_sndintpipe(line6->usbdev, line6->properties->ep_ctrl_w), - (char *)msg->buffer + done, bytes, - line6_async_request_sent, msg, line6->interval); + if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) { + usb_fill_int_urb(urb, line6->usbdev, + usb_sndintpipe(line6->usbdev, line6->properties->ep_ctrl_w), + (char *)msg->buffer + done, bytes, + line6_async_request_sent, msg, line6->interval); + } else { + usb_fill_bulk_urb(urb, line6->usbdev, + usb_sndbulkpipe(line6->usbdev, line6->properties->ep_ctrl_w), + (char *)msg->buffer + done, bytes, + line6_async_request_sent, msg); + } msg->done += bytes; retval = usb_submit_urb(urb, GFP_ATOMIC); @@ -269,28 +291,36 @@ static void line6_data_received(struct urb *urb) if (urb->status == -ESHUTDOWN) return; - done = - line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length); + if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) { + done = + line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length); - if (done < urb->actual_length) { - line6_midibuf_ignore(mb, done); - dev_dbg(line6->ifcdev, "%d %d buffer overflow - message skipped\n", - done, urb->actual_length); - } + if (done < urb->actual_length) { + line6_midibuf_ignore(mb, done); + dev_dbg(line6->ifcdev, "%d %d buffer overflow - message skipped\n", + done, urb->actual_length); + } - for (;;) { - done = - line6_midibuf_read(mb, line6->buffer_message, - LINE6_MESSAGE_MAXLEN); + for (;;) { + done = + line6_midibuf_read(mb, line6->buffer_message, + LINE6_MIDI_MESSAGE_MAXLEN); - if (done == 0) - break; + if (done == 0) + break; - line6->message_length = done; - line6_midi_receive(line6, line6->buffer_message, done); + line6->message_length = done; + line6_midi_receive(line6, line6->buffer_message, done); + if (line6->process_message) + line6->process_message(line6); + } + } else { + line6->buffer_message = urb->transfer_buffer; + line6->message_length = urb->actual_length; if (line6->process_message) line6->process_message(line6); + line6->buffer_message = NULL; } line6_start_listen(line6); @@ -447,12 +477,16 @@ static void line6_destruct(struct snd_card *card) struct usb_line6 *line6 = card->private_data; struct usb_device *usbdev = line6->usbdev; - /* free buffer memory first: */ + /* Free buffer memory first. We cannot depend on the existence of private + * data from the (podhd) module, it may be gone already during this call + */ kfree(line6->buffer_message); + kfree(line6->buffer_listen); /* then free URBs: */ usb_free_urb(line6->urb_listen); + line6->urb_listen = NULL; /* decrement reference counters: */ usb_put_dev(usbdev); @@ -462,13 +496,29 @@ static void line6_destruct(struct snd_card *card) static void line6_get_interval(struct usb_line6 *line6) { struct usb_device *usbdev = line6->usbdev; + const struct line6_properties *properties = line6->properties; + int pipe; struct usb_host_endpoint *ep; - unsigned pipe = usb_rcvintpipe(usbdev, line6->properties->ep_ctrl_r); - unsigned epnum = usb_pipeendpoint(pipe); - ep = usbdev->ep_in[epnum]; + if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) { + pipe = + usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r); + } else { + pipe = + usb_rcvbulkpipe(line6->usbdev, line6->properties->ep_ctrl_r); + } + ep = usbdev->ep_in[usb_pipeendpoint(pipe)]; + if (ep) { line6->interval = ep->desc.bInterval; + if (usbdev->speed == USB_SPEED_LOW) { + line6->intervals_per_second = USB_LOW_INTERVALS_PER_SECOND; + line6->iso_buffers = USB_LOW_ISO_BUFFERS; + } else { + line6->intervals_per_second = USB_HIGH_INTERVALS_PER_SECOND; + line6->iso_buffers = USB_HIGH_ISO_BUFFERS; + } + line6->max_packet_size = le16_to_cpu(ep->desc.wMaxPacketSize); } else { dev_err(line6->ifcdev, @@ -478,6 +528,138 @@ static void line6_get_interval(struct usb_line6 *line6) } } + +/* Enable buffering of incoming messages, flush the buffer */ +static int line6_hwdep_open(struct snd_hwdep *hw, struct file *file) +{ + struct usb_line6 *line6 = hw->private_data; + + /* NOTE: hwdep layer provides atomicity here */ + + line6->messages.active = 1; + + return 0; +} + +/* Stop buffering */ +static int line6_hwdep_release(struct snd_hwdep *hw, struct file *file) +{ + struct usb_line6 *line6 = hw->private_data; + + line6->messages.active = 0; + + return 0; +} + +/* Read from circular buffer, return to user */ +static long +line6_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, + loff_t *offset) +{ + struct usb_line6 *line6 = hwdep->private_data; + long rv = 0; + unsigned int out_count; + + if (mutex_lock_interruptible(&line6->messages.read_lock)) + return -ERESTARTSYS; + + while (kfifo_len(&line6->messages.fifo) == 0) { + mutex_unlock(&line6->messages.read_lock); + + rv = wait_event_interruptible( + line6->messages.wait_queue, + kfifo_len(&line6->messages.fifo) != 0); + if (rv < 0) + return rv; + + if (mutex_lock_interruptible(&line6->messages.read_lock)) + return -ERESTARTSYS; + } + + if (kfifo_peek_len(&line6->messages.fifo) > count) { + /* Buffer too small; allow re-read of the current item... */ + rv = -EINVAL; + } else { + rv = kfifo_to_user(&line6->messages.fifo, buf, count, &out_count); + if (rv == 0) + rv = out_count; + } + + mutex_unlock(&line6->messages.read_lock); + return rv; +} + +/* Write directly (no buffering) to device by user*/ +static long +line6_hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count, + loff_t *offset) +{ + struct usb_line6 *line6 = hwdep->private_data; + int rv; + char *data_copy; + + if (count > line6->max_packet_size * LINE6_RAW_MESSAGES_MAXCOUNT) { + /* This is an arbitrary limit - still better than nothing... */ + return -EINVAL; + } + + data_copy = memdup_user(data, count); + if (IS_ERR(data_copy)) + return PTR_ERR(data_copy); + + rv = line6_send_raw_message(line6, data_copy, count); + + kfree(data_copy); + return rv; +} + +static const struct snd_hwdep_ops hwdep_ops = { + .open = line6_hwdep_open, + .release = line6_hwdep_release, + .read = line6_hwdep_read, + .write = line6_hwdep_write, +}; + +/* Insert into circular buffer */ +static void line6_hwdep_push_message(struct usb_line6 *line6) +{ + if (!line6->messages.active) + return; + + if (kfifo_avail(&line6->messages.fifo) >= line6->message_length) { + /* No race condition here, there's only one writer */ + kfifo_in(&line6->messages.fifo, + line6->buffer_message, line6->message_length); + } /* else TODO: signal overflow */ + + wake_up_interruptible(&line6->messages.wait_queue); +} + +static int line6_hwdep_init(struct usb_line6 *line6) +{ + int err; + struct snd_hwdep *hwdep; + + /* TODO: usb_driver_claim_interface(); */ + line6->process_message = line6_hwdep_push_message; + line6->messages.active = 0; + init_waitqueue_head(&line6->messages.wait_queue); + mutex_init(&line6->messages.read_lock); + INIT_KFIFO(line6->messages.fifo); + + err = snd_hwdep_new(line6->card, "config", 0, &hwdep); + if (err < 0) + goto end; + strcpy(hwdep->name, "config"); + hwdep->iface = SNDRV_HWDEP_IFACE_LINE6; + hwdep->ops = hwdep_ops; + hwdep->private_data = line6; + hwdep->exclusive = true; + +end: + return err; +} + static int line6_init_cap_control(struct usb_line6 *line6) { int ret; @@ -487,14 +669,20 @@ static int line6_init_cap_control(struct usb_line6 *line6) if (!line6->buffer_listen) return -ENOMEM; - line6->buffer_message = kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL); - if (!line6->buffer_message) - return -ENOMEM; - line6->urb_listen = usb_alloc_urb(0, GFP_KERNEL); if (!line6->urb_listen) return -ENOMEM; + if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) { + line6->buffer_message = kmalloc(LINE6_MIDI_MESSAGE_MAXLEN, GFP_KERNEL); + if (!line6->buffer_message) + return -ENOMEM; + } else { + ret = line6_hwdep_init(line6); + if (ret < 0) + return ret; + } + ret = line6_start_listen(line6); if (ret < 0) { dev_err(line6->ifcdev, "cannot start listening: %d\n", ret); @@ -558,6 +746,7 @@ int line6_probe(struct usb_interface *interface, /* query interface number */ interface_number = interface->cur_altsetting->desc.bInterfaceNumber; + /* TODO reserves the bus bandwidth even without actual transfer */ ret = usb_set_interface(usbdev, interface_number, properties->altsetting); if (ret < 0) { @@ -565,9 +754,8 @@ int line6_probe(struct usb_interface *interface, goto error; } - line6_get_interval(line6); - if (properties->capabilities & LINE6_CAP_CONTROL) { + line6_get_interval(line6); ret = line6_init_cap_control(line6); if (ret < 0) goto error; @@ -670,3 +858,4 @@ EXPORT_SYMBOL_GPL(line6_resume); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); + diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h index 7da643e79e3b..a5c2e9ae5f17 100644 --- a/sound/usb/line6/driver.h +++ b/sound/usb/line6/driver.h @@ -12,21 +12,37 @@ #ifndef DRIVER_H #define DRIVER_H -#include <linux/spinlock.h> #include <linux/usb.h> +#include <linux/mutex.h> +#include <linux/kfifo.h> #include <sound/core.h> #include "midi.h" -#define USB_INTERVALS_PER_SECOND 1000 +/* USB 1.1 speed configuration */ +#define USB_LOW_INTERVALS_PER_SECOND 1000 +#define USB_LOW_ISO_BUFFERS 2 + +/* USB 2.0+ speed configuration */ +#define USB_HIGH_INTERVALS_PER_SECOND 8000 +#define USB_HIGH_ISO_BUFFERS 16 /* Fallback USB interval and max packet size values */ #define LINE6_FALLBACK_INTERVAL 10 #define LINE6_FALLBACK_MAXPACKETSIZE 16 #define LINE6_TIMEOUT 1 -#define LINE6_BUFSIZE_LISTEN 32 -#define LINE6_MESSAGE_MAXLEN 256 +#define LINE6_BUFSIZE_LISTEN 64 +#define LINE6_MIDI_MESSAGE_MAXLEN 256 + +#define LINE6_RAW_MESSAGES_MAXCOUNT_ORDER 7 +/* 4k packets are common, BUFSIZE * MAXCOUNT should be bigger... */ +#define LINE6_RAW_MESSAGES_MAXCOUNT (1 << LINE6_RAW_MESSAGES_MAXCOUNT_ORDER) + + +#if LINE6_BUFSIZE_LISTEN > 65535 +#error "Use dynamic fifo instead" +#endif /* Line 6 MIDI control commands @@ -82,10 +98,11 @@ struct line6_properties { int altsetting; - unsigned ep_ctrl_r; - unsigned ep_ctrl_w; - unsigned ep_audio_r; - unsigned ep_audio_w; + unsigned int ctrl_if; + unsigned int ep_ctrl_r; + unsigned int ep_ctrl_w; + unsigned int ep_audio_r; + unsigned int ep_audio_w; }; /* Capability bits */ @@ -94,8 +111,12 @@ enum { LINE6_CAP_CONTROL = 1 << 0, /* device supports PCM input/output via USB */ LINE6_CAP_PCM = 1 << 1, - /* device support hardware monitoring */ + /* device supports hardware monitoring */ LINE6_CAP_HWMON = 1 << 2, + /* device requires output data when input is read */ + LINE6_CAP_IN_NEEDS_OUT = 1 << 3, + /* device uses raw MIDI via USB (data endpoints) */ + LINE6_CAP_CONTROL_MIDI = 1 << 4, }; /* @@ -109,10 +130,15 @@ struct usb_line6 { /* Properties */ const struct line6_properties *properties; - /* Interval (ms) */ + /* Interval for data USB packets */ int interval; + /* ...for isochronous transfers framing */ + int intervals_per_second; + + /* Number of isochronous URBs used for frame transfers */ + int iso_buffers; - /* Maximum size of USB packet */ + /* Maximum size of data USB packet */ int max_packet_size; /* Device representing the USB interface */ @@ -129,18 +155,30 @@ struct usb_line6 { /* Line 6 MIDI device data structure */ struct snd_line6_midi *line6midi; - /* URB for listening to PODxt Pro control endpoint */ + /* URB for listening to POD data endpoint */ struct urb *urb_listen; - /* Buffer for listening to PODxt Pro control endpoint */ + /* Buffer for incoming data from POD data endpoint */ unsigned char *buffer_listen; - /* Buffer for message to be processed */ + /* Buffer for message to be processed, generated from MIDI layer */ unsigned char *buffer_message; - /* Length of message to be processed */ + /* Length of message to be processed, generated from MIDI layer */ int message_length; + /* Circular buffer for non-MIDI control messages */ + struct { + struct mutex read_lock; + wait_queue_head_t wait_queue; + unsigned int active:1; + STRUCT_KFIFO_REC_2(LINE6_BUFSIZE_LISTEN * LINE6_RAW_MESSAGES_MAXCOUNT) + fifo; + } messages; + + /* If MIDI is supported, buffer_message contains the pre-processed data; + * otherwise the data is only in urb_listen (buffer_incoming). + */ void (*process_message)(struct usb_line6 *); void (*disconnect)(struct usb_line6 *line6); }; diff --git a/sound/usb/line6/midi.c b/sound/usb/line6/midi.c index cebea9b7f769..d0fb2f205bd9 100644 --- a/sound/usb/line6/midi.c +++ b/sound/usb/line6/midi.c @@ -258,7 +258,7 @@ int line6_init_midi(struct usb_line6 *line6) struct snd_rawmidi *rmidi; struct snd_line6_midi *line6midi; - if (!(line6->properties->capabilities & LINE6_CAP_CONTROL)) { + if (!(line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)) { /* skip MIDI initialization and report success */ return 0; } diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c index 41aa3355e920..fab53f58d447 100644 --- a/sound/usb/line6/pcm.c +++ b/sound/usb/line6/pcm.c @@ -52,7 +52,7 @@ static int snd_line6_impulse_volume_put(struct snd_kcontrol *kcontrol, line6pcm->impulse_volume = value; if (value > 0) { - err = line6_pcm_acquire(line6pcm, LINE6_STREAM_IMPULSE); + err = line6_pcm_acquire(line6pcm, LINE6_STREAM_IMPULSE, true); if (err < 0) { line6pcm->impulse_volume = 0; return err; @@ -104,7 +104,7 @@ static void line6_unlink_audio_urbs(struct snd_line6_pcm *line6pcm, { int i; - for (i = 0; i < LINE6_ISO_BUFFERS; i++) { + for (i = 0; i < line6pcm->line6->iso_buffers; i++) { if (test_bit(i, &pcms->active_urbs)) { if (!test_and_set_bit(i, &pcms->unlink_urbs)) usb_unlink_urb(pcms->urbs[i]); @@ -124,7 +124,7 @@ static void line6_wait_clear_audio_urbs(struct snd_line6_pcm *line6pcm, do { alive = 0; - for (i = 0; i < LINE6_ISO_BUFFERS; i++) { + for (i = 0; i < line6pcm->line6->iso_buffers; i++) { if (test_bit(i, &pcms->active_urbs)) alive++; } @@ -146,15 +146,20 @@ get_stream(struct snd_line6_pcm *line6pcm, int direction) } /* allocate a buffer if not opened yet; - * call this in line6pcm.state_change mutex + * call this in line6pcm.state_mutex */ static int line6_buffer_acquire(struct snd_line6_pcm *line6pcm, - struct line6_pcm_stream *pstr, int type) + struct line6_pcm_stream *pstr, int direction, int type) { + const int pkt_size = + (direction == SNDRV_PCM_STREAM_PLAYBACK) ? + line6pcm->max_packet_size_out : + line6pcm->max_packet_size_in; + /* Invoked multiple times in a row so allocate once only */ if (!test_and_set_bit(type, &pstr->opened) && !pstr->buffer) { - pstr->buffer = kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * - line6pcm->max_packet_size, GFP_KERNEL); + pstr->buffer = kmalloc(line6pcm->line6->iso_buffers * + LINE6_ISO_PACKETS * pkt_size, GFP_KERNEL); if (!pstr->buffer) return -ENOMEM; } @@ -162,12 +167,11 @@ static int line6_buffer_acquire(struct snd_line6_pcm *line6pcm, } /* free a buffer if all streams are closed; - * call this in line6pcm.state_change mutex + * call this in line6pcm.state_mutex */ static void line6_buffer_release(struct snd_line6_pcm *line6pcm, struct line6_pcm_stream *pstr, int type) { - clear_bit(type, &pstr->opened); if (!pstr->opened) { line6_wait_clear_audio_urbs(line6pcm, pstr); @@ -194,6 +198,7 @@ static int line6_stream_start(struct snd_line6_pcm *line6pcm, int direction, else ret = line6_submit_audio_in_all_urbs(line6pcm); } + if (ret < 0) clear_bit(type, &pstr->running); spin_unlock_irqrestore(&pstr->lock, flags); @@ -237,6 +242,14 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: + if (s->stream == SNDRV_PCM_STREAM_CAPTURE && + (line6pcm->line6->properties->capabilities & + LINE6_CAP_IN_NEEDS_OUT)) { + err = line6_stream_start(line6pcm, SNDRV_PCM_STREAM_PLAYBACK, + LINE6_STREAM_CAPTURE_HELPER); + if (err < 0) + return err; + } err = line6_stream_start(line6pcm, s->stream, LINE6_STREAM_PCM); if (err < 0) @@ -245,6 +258,12 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: + if (s->stream == SNDRV_PCM_STREAM_CAPTURE && + (line6pcm->line6->properties->capabilities & + LINE6_CAP_IN_NEEDS_OUT)) { + line6_stream_stop(line6pcm, SNDRV_PCM_STREAM_PLAYBACK, + LINE6_STREAM_CAPTURE_HELPER); + } line6_stream_stop(line6pcm, s->stream, LINE6_STREAM_PCM); break; @@ -278,27 +297,30 @@ snd_pcm_uframes_t snd_line6_pointer(struct snd_pcm_substream *substream) return pstr->pos_done; } -/* Acquire and start duplex streams: +/* Acquire and optionally start duplex streams: * type is either LINE6_STREAM_IMPULSE or LINE6_STREAM_MONITOR */ -int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type) +int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type, bool start) { struct line6_pcm_stream *pstr; int ret = 0, dir; + /* TODO: We should assert SNDRV_PCM_STREAM_PLAYBACK/CAPTURE == 0/1 */ mutex_lock(&line6pcm->state_mutex); for (dir = 0; dir < 2; dir++) { pstr = get_stream(line6pcm, dir); - ret = line6_buffer_acquire(line6pcm, pstr, type); + ret = line6_buffer_acquire(line6pcm, pstr, dir, type); if (ret < 0) goto error; if (!pstr->running) line6_wait_clear_audio_urbs(line6pcm, pstr); } - for (dir = 0; dir < 2; dir++) { - ret = line6_stream_start(line6pcm, dir, type); - if (ret < 0) - goto error; + if (start) { + for (dir = 0; dir < 2; dir++) { + ret = line6_stream_start(line6pcm, dir, type); + if (ret < 0) + goto error; + } } error: mutex_unlock(&line6pcm->state_mutex); @@ -334,7 +356,8 @@ int snd_line6_hw_params(struct snd_pcm_substream *substream, struct line6_pcm_stream *pstr = get_stream(line6pcm, substream->stream); mutex_lock(&line6pcm->state_mutex); - ret = line6_buffer_acquire(line6pcm, pstr, LINE6_STREAM_PCM); + ret = line6_buffer_acquire(line6pcm, pstr, substream->stream, + LINE6_STREAM_PCM); if (ret < 0) goto error; @@ -434,24 +457,30 @@ static struct snd_kcontrol_new line6_controls[] = { /* Cleanup the PCM device. */ -static void cleanup_urbs(struct line6_pcm_stream *pcms) +static void cleanup_urbs(struct line6_pcm_stream *pcms, int iso_buffers) { int i; - for (i = 0; i < LINE6_ISO_BUFFERS; i++) { + /* Most likely impossible in current code... */ + if (pcms->urbs == NULL) + return; + + for (i = 0; i < iso_buffers; i++) { if (pcms->urbs[i]) { usb_kill_urb(pcms->urbs[i]); usb_free_urb(pcms->urbs[i]); } } + kfree(pcms->urbs); + pcms->urbs = NULL; } static void line6_cleanup_pcm(struct snd_pcm *pcm) { struct snd_line6_pcm *line6pcm = snd_pcm_chip(pcm); - cleanup_urbs(&line6pcm->out); - cleanup_urbs(&line6pcm->in); + cleanup_urbs(&line6pcm->out, line6pcm->line6->iso_buffers); + cleanup_urbs(&line6pcm->in, line6pcm->line6->iso_buffers); kfree(line6pcm); } @@ -523,12 +552,12 @@ int line6_init_pcm(struct usb_line6 *line6, line6pcm->volume_monitor = 255; line6pcm->line6 = line6; - /* Read and write buffers are sized identically, so choose minimum */ - line6pcm->max_packet_size = min( - usb_maxpacket(line6->usbdev, - usb_rcvisocpipe(line6->usbdev, ep_read), 0), - usb_maxpacket(line6->usbdev, - usb_sndisocpipe(line6->usbdev, ep_write), 1)); + line6pcm->max_packet_size_in = + usb_maxpacket(line6->usbdev, + usb_rcvisocpipe(line6->usbdev, ep_read), 0); + line6pcm->max_packet_size_out = + usb_maxpacket(line6->usbdev, + usb_sndisocpipe(line6->usbdev, ep_write), 1); spin_lock_init(&line6pcm->out.lock); spin_lock_init(&line6pcm->in.lock); diff --git a/sound/usb/line6/pcm.h b/sound/usb/line6/pcm.h index 508410adbd51..bb0c9cbf2a78 100644 --- a/sound/usb/line6/pcm.h +++ b/sound/usb/line6/pcm.h @@ -20,9 +20,6 @@ #include "driver.h" -/* number of URBs */ -#define LINE6_ISO_BUFFERS 2 - /* number of USB frames per URB The Line 6 Windows driver always transmits two frames per packet, but @@ -31,7 +28,9 @@ */ #define LINE6_ISO_PACKETS 1 -/* in a "full speed" device (such as the PODxt Pro) this means 1ms */ +/* in a "full speed" device (such as the PODxt Pro) this means 1ms, + * for "high speed" it's 1/8ms + */ #define LINE6_ISO_INTERVAL 1 #define LINE6_IMPULSE_DEFAULT_PERIOD 100 @@ -74,6 +73,7 @@ enum { LINE6_STREAM_PCM, LINE6_STREAM_MONITOR, LINE6_STREAM_IMPULSE, + LINE6_STREAM_CAPTURE_HELPER, }; /* misc bit flags for PCM operation */ @@ -85,12 +85,12 @@ enum { struct line6_pcm_properties { struct snd_pcm_hardware playback_hw, capture_hw; struct snd_pcm_hw_constraint_ratdens rates; - int bytes_per_frame; + int bytes_per_channel; }; struct line6_pcm_stream { /* allocated URBs */ - struct urb *urbs[LINE6_ISO_BUFFERS]; + struct urb **urbs; /* Temporary buffer; * Since the packet size is not known in advance, this buffer is @@ -157,11 +157,12 @@ struct snd_line6_pcm { /* Previously captured frame (for software monitoring) */ unsigned char *prev_fbuf; - /* Size of previously captured frame (for software monitoring) */ + /* Size of previously captured frame (for software monitoring/sync) */ int prev_fsize; /* Maximum size of USB packet */ - int max_packet_size; + int max_packet_size_in; + int max_packet_size_out; /* PCM playback volume (left and right) */ int volume_playback[2]; @@ -191,7 +192,8 @@ extern int snd_line6_hw_params(struct snd_pcm_substream *substream, extern int snd_line6_hw_free(struct snd_pcm_substream *substream); extern snd_pcm_uframes_t snd_line6_pointer(struct snd_pcm_substream *substream); extern void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm); -extern int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type); +extern int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type, + bool start); extern void line6_pcm_release(struct snd_line6_pcm *line6pcm, int type); #endif diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c index 97ed593f6010..812d18191e01 100644 --- a/sound/usb/line6/playback.c +++ b/sound/usb/line6/playback.c @@ -146,18 +146,20 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm) int index; int i, urb_size, urb_frames; int ret; - const int bytes_per_frame = line6pcm->properties->bytes_per_frame; + const int bytes_per_frame = + line6pcm->properties->bytes_per_channel * + line6pcm->properties->playback_hw.channels_max; const int frame_increment = line6pcm->properties->rates.rats[0].num_min; const int frame_factor = line6pcm->properties->rates.rats[0].den * - (USB_INTERVALS_PER_SECOND / LINE6_ISO_INTERVAL); + (line6pcm->line6->intervals_per_second / LINE6_ISO_INTERVAL); struct urb *urb_out; - index = - find_first_zero_bit(&line6pcm->out.active_urbs, LINE6_ISO_BUFFERS); + index = find_first_zero_bit(&line6pcm->out.active_urbs, + line6pcm->line6->iso_buffers); - if (index < 0 || index >= LINE6_ISO_BUFFERS) { + if (index < 0 || index >= line6pcm->line6->iso_buffers) { dev_err(line6pcm->line6->ifcdev, "no free URB found\n"); return -EINVAL; } @@ -165,6 +167,7 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm) urb_out = line6pcm->out.urbs[index]; urb_size = 0; + /* TODO: this may not work for LINE6_ISO_PACKETS != 1 */ for (i = 0; i < LINE6_ISO_PACKETS; ++i) { /* compute frame size for given sampling rate */ int fsize = 0; @@ -178,9 +181,11 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm) line6pcm->out.count += frame_increment; n = line6pcm->out.count / frame_factor; line6pcm->out.count -= n * frame_factor; - fsize = n * bytes_per_frame; + fsize = n; } + fsize *= bytes_per_frame; + fout->offset = urb_size; fout->length = fsize; urb_size += fsize; @@ -195,7 +200,7 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm) urb_frames = urb_size / bytes_per_frame; urb_out->transfer_buffer = line6pcm->out.buffer + - index * LINE6_ISO_PACKETS * line6pcm->max_packet_size; + index * LINE6_ISO_PACKETS * line6pcm->max_packet_size_out; urb_out->transfer_buffer_length = urb_size; urb_out->context = line6pcm; @@ -286,7 +291,7 @@ int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm) { int ret = 0, i; - for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { + for (i = 0; i < line6pcm->line6->iso_buffers; ++i) { ret = submit_audio_out_urb(line6pcm); if (ret < 0) break; @@ -305,6 +310,9 @@ static void audio_out_callback(struct urb *urb) struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context; struct snd_pcm_substream *substream = get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK); + const int bytes_per_frame = + line6pcm->properties->bytes_per_channel * + line6pcm->properties->playback_hw.channels_max; #if USE_CLEAR_BUFFER_WORKAROUND memset(urb->transfer_buffer, 0, urb->transfer_buffer_length); @@ -313,11 +321,11 @@ static void audio_out_callback(struct urb *urb) line6pcm->out.last_frame = urb->start_frame; /* find index of URB */ - for (index = 0; index < LINE6_ISO_BUFFERS; index++) + for (index = 0; index < line6pcm->line6->iso_buffers; index++) if (urb == line6pcm->out.urbs[index]) break; - if (index >= LINE6_ISO_BUFFERS) + if (index >= line6pcm->line6->iso_buffers) return; /* URB has been unlinked asynchronously */ for (i = 0; i < LINE6_ISO_PACKETS; i++) @@ -329,7 +337,7 @@ static void audio_out_callback(struct urb *urb) struct snd_pcm_runtime *runtime = substream->runtime; line6pcm->out.pos_done += - length / line6pcm->properties->bytes_per_frame; + length / bytes_per_frame; if (line6pcm->out.pos_done >= runtime->buffer_size) line6pcm->out.pos_done -= runtime->buffer_size; @@ -401,8 +409,13 @@ int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm) struct usb_line6 *line6 = line6pcm->line6; int i; + line6pcm->out.urbs = kzalloc( + sizeof(struct urb *) * line6->iso_buffers, GFP_KERNEL); + if (line6pcm->out.urbs == NULL) + return -ENOMEM; + /* create audio URBs and fill in constant values: */ - for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { + for (i = 0; i < line6->iso_buffers; ++i) { struct urb *urb; /* URB for audio out: */ diff --git a/sound/usb/line6/pod.c b/sound/usb/line6/pod.c index 45dd34874f43..17aa616e61f5 100644 --- a/sound/usb/line6/pod.c +++ b/sound/usb/line6/pod.c @@ -83,7 +83,6 @@ struct usb_line6_pod { }; #define POD_SYSEX_CODE 3 -#define POD_BYTES_PER_FRAME 6 /* 24bit audio (stereo) */ /* *INDENT-OFF* */ @@ -167,7 +166,7 @@ static struct line6_pcm_properties pod_pcm_properties = { .rates = { .nrats = 1, .rats = &pod_ratden}, - .bytes_per_frame = POD_BYTES_PER_FRAME + .bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */ }; static const char pod_version_header[] = { @@ -476,6 +475,7 @@ static const struct line6_properties pod_properties_table[] = { .id = "BassPODxt", .name = "BassPODxt", .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_CONTROL_MIDI | LINE6_CAP_PCM | LINE6_CAP_HWMON, .altsetting = 5, @@ -488,6 +488,7 @@ static const struct line6_properties pod_properties_table[] = { .id = "BassPODxtLive", .name = "BassPODxt Live", .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_CONTROL_MIDI | LINE6_CAP_PCM | LINE6_CAP_HWMON, .altsetting = 1, @@ -500,6 +501,7 @@ static const struct line6_properties pod_properties_table[] = { .id = "BassPODxtPro", .name = "BassPODxt Pro", .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_CONTROL_MIDI | LINE6_CAP_PCM | LINE6_CAP_HWMON, .altsetting = 5, @@ -511,7 +513,8 @@ static const struct line6_properties pod_properties_table[] = { [LINE6_POCKETPOD] = { .id = "PocketPOD", .name = "Pocket POD", - .capabilities = LINE6_CAP_CONTROL, + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_CONTROL_MIDI, .altsetting = 0, .ep_ctrl_r = 0x82, .ep_ctrl_w = 0x02, @@ -521,6 +524,7 @@ static const struct line6_properties pod_properties_table[] = { .id = "PODxt", .name = "PODxt", .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_CONTROL_MIDI | LINE6_CAP_PCM | LINE6_CAP_HWMON, .altsetting = 5, @@ -533,6 +537,7 @@ static const struct line6_properties pod_properties_table[] = { .id = "PODxtLive", .name = "PODxt Live", .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_CONTROL_MIDI | LINE6_CAP_PCM | LINE6_CAP_HWMON, .altsetting = 1, @@ -545,6 +550,7 @@ static const struct line6_properties pod_properties_table[] = { .id = "PODxtPro", .name = "PODxt Pro", .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_CONTROL_MIDI | LINE6_CAP_PCM | LINE6_CAP_HWMON, .altsetting = 5, diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c index 63dcaef41ac3..6ab23e5aee71 100644 --- a/sound/usb/line6/podhd.c +++ b/sound/usb/line6/podhd.c @@ -2,6 +2,7 @@ * Line 6 Pod HD * * Copyright (C) 2011 Stefan Hajnoczi <stefanha@gmail.com> + * Copyright (C) 2015 Andrej Krutak <dev@andree.sk> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -18,14 +19,46 @@ #include "driver.h" #include "pcm.h" +#define PODHD_STARTUP_DELAY 500 + +/* + * Stages of POD startup procedure + */ +enum { + PODHD_STARTUP_INIT = 1, + PODHD_STARTUP_SCHEDULE_WORKQUEUE, + PODHD_STARTUP_SETUP, + PODHD_STARTUP_LAST = PODHD_STARTUP_SETUP - 1 +}; + enum { LINE6_PODHD300, LINE6_PODHD400, LINE6_PODHD500_0, LINE6_PODHD500_1, + LINE6_PODX3, + LINE6_PODX3LIVE }; -#define PODHD_BYTES_PER_FRAME 6 /* 24bit audio (stereo) */ +struct usb_line6_podhd { + /* Generic Line 6 USB data */ + struct usb_line6 line6; + + /* Timer for device initialization */ + struct timer_list startup_timer; + + /* Work handler for device initialization */ + struct work_struct startup_work; + + /* Current progress in startup procedure */ + int startup_progress; + + /* Serial number of device */ + u32 serial_number; + + /* Firmware version */ + int firmware_version; +}; static struct snd_ratden podhd_ratden = { .num_min = 48000, @@ -73,29 +106,258 @@ static struct line6_pcm_properties podhd_pcm_properties = { .rates = { .nrats = 1, .rats = &podhd_ratden}, - .bytes_per_frame = PODHD_BYTES_PER_FRAME + .bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */ +}; + +static struct line6_pcm_properties podx3_pcm_properties = { + .playback_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 60000, + .period_bytes_min = 64, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 1024}, + .capture_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + /* 1+2: Main signal (out), 3+4: Tone 1, + * 5+6: Tone 2, 7+8: raw + */ + .channels_min = 8, + .channels_max = 8, + .buffer_bytes_max = 60000, + .period_bytes_min = 64, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 1024}, + .rates = { + .nrats = 1, + .rats = &podhd_ratden}, + .bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */ +}; +static struct usb_driver podhd_driver; + +static void podhd_startup_start_workqueue(unsigned long data); +static void podhd_startup_workqueue(struct work_struct *work); +static int podhd_startup_finalize(struct usb_line6_podhd *pod); + +static ssize_t serial_number_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct snd_card *card = dev_to_snd_card(dev); + struct usb_line6_podhd *pod = card->private_data; + + return sprintf(buf, "%u\n", pod->serial_number); +} + +static ssize_t firmware_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct snd_card *card = dev_to_snd_card(dev); + struct usb_line6_podhd *pod = card->private_data; + + return sprintf(buf, "%06x\n", pod->firmware_version); +} + +static DEVICE_ATTR_RO(firmware_version); +static DEVICE_ATTR_RO(serial_number); + +static struct attribute *podhd_dev_attrs[] = { + &dev_attr_firmware_version.attr, + &dev_attr_serial_number.attr, + NULL +}; + +static const struct attribute_group podhd_dev_attr_group = { + .name = "podhd", + .attrs = podhd_dev_attrs, }; /* + * POD X3 startup procedure. + * + * May be compatible with other POD HD's, since it's also similar to the + * previous POD setup. In any case, it doesn't seem to be required for the + * audio nor bulk interfaces to work. + */ + +static void podhd_startup(struct usb_line6_podhd *pod) +{ + CHECK_STARTUP_PROGRESS(pod->startup_progress, PODHD_STARTUP_INIT); + + /* delay startup procedure: */ + line6_start_timer(&pod->startup_timer, PODHD_STARTUP_DELAY, + podhd_startup_start_workqueue, (unsigned long)pod); +} + +static void podhd_startup_start_workqueue(unsigned long data) +{ + struct usb_line6_podhd *pod = (struct usb_line6_podhd *)data; + + CHECK_STARTUP_PROGRESS(pod->startup_progress, + PODHD_STARTUP_SCHEDULE_WORKQUEUE); + + /* schedule work for global work queue: */ + schedule_work(&pod->startup_work); +} + +static int podhd_dev_start(struct usb_line6_podhd *pod) +{ + int ret; + u8 init_bytes[8]; + int i; + struct usb_device *usbdev = pod->line6.usbdev; + + ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), + 0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, + 0x11, 0, + NULL, 0, LINE6_TIMEOUT * HZ); + if (ret < 0) { + dev_err(pod->line6.ifcdev, "read request failed (error %d)\n", ret); + return ret; + } + + /* NOTE: looks like some kind of ping message */ + ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x11, 0x0, + &init_bytes, 3, LINE6_TIMEOUT * HZ); + if (ret < 0) { + dev_err(pod->line6.ifcdev, + "receive length failed (error %d)\n", ret); + return ret; + } + + pod->firmware_version = + (init_bytes[0] << 16) | (init_bytes[1] << 8) | (init_bytes[2] << 0); + + for (i = 0; i <= 16; i++) { + ret = line6_read_data(&pod->line6, 0xf000 + 0x08 * i, init_bytes, 8); + if (ret < 0) + return ret; + } + + ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), + USB_REQ_SET_FEATURE, + USB_TYPE_STANDARD | USB_RECIP_DEVICE | USB_DIR_OUT, + 1, 0, + NULL, 0, LINE6_TIMEOUT * HZ); + if (ret < 0) + return ret; + + return 0; +} + +static void podhd_startup_workqueue(struct work_struct *work) +{ + struct usb_line6_podhd *pod = + container_of(work, struct usb_line6_podhd, startup_work); + + CHECK_STARTUP_PROGRESS(pod->startup_progress, PODHD_STARTUP_SETUP); + + podhd_dev_start(pod); + line6_read_serial_number(&pod->line6, &pod->serial_number); + + podhd_startup_finalize(pod); +} + +static int podhd_startup_finalize(struct usb_line6_podhd *pod) +{ + struct usb_line6 *line6 = &pod->line6; + + /* ALSA audio interface: */ + return snd_card_register(line6->card); +} + +static void podhd_disconnect(struct usb_line6 *line6) +{ + struct usb_line6_podhd *pod = (struct usb_line6_podhd *)line6; + + if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) { + struct usb_interface *intf; + + del_timer_sync(&pod->startup_timer); + cancel_work_sync(&pod->startup_work); + + intf = usb_ifnum_to_if(line6->usbdev, + pod->line6.properties->ctrl_if); + usb_driver_release_interface(&podhd_driver, intf); + } +} + +/* Try to init POD HD device. */ static int podhd_init(struct usb_line6 *line6, const struct usb_device_id *id) { int err; + struct usb_line6_podhd *pod = (struct usb_line6_podhd *) line6; + struct usb_interface *intf; - /* initialize MIDI subsystem: */ - err = line6_init_midi(line6); - if (err < 0) - return err; + line6->disconnect = podhd_disconnect; - /* initialize PCM subsystem: */ - err = line6_init_pcm(line6, &podhd_pcm_properties); - if (err < 0) - return err; + if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) { + /* claim the data interface */ + intf = usb_ifnum_to_if(line6->usbdev, + pod->line6.properties->ctrl_if); + if (!intf) { + dev_err(pod->line6.ifcdev, "interface %d not found\n", + pod->line6.properties->ctrl_if); + return -ENODEV; + } - /* register USB audio system: */ - return snd_card_register(line6->card); + err = usb_driver_claim_interface(&podhd_driver, intf, NULL); + if (err != 0) { + dev_err(pod->line6.ifcdev, "can't claim interface %d, error %d\n", + pod->line6.properties->ctrl_if, err); + return err; + } + + /* create sysfs entries: */ + err = snd_card_add_dev_attr(line6->card, &podhd_dev_attr_group); + if (err < 0) + return err; + } + + if (pod->line6.properties->capabilities & LINE6_CAP_PCM) { + /* initialize PCM subsystem: */ + err = line6_init_pcm(line6, + (id->driver_info == LINE6_PODX3 || + id->driver_info == LINE6_PODX3LIVE) ? &podx3_pcm_properties : + &podhd_pcm_properties); + if (err < 0) + return err; + } + + if (!(pod->line6.properties->capabilities & LINE6_CAP_CONTROL)) { + /* register USB audio system directly */ + return podhd_startup_finalize(pod); + } + + /* init device and delay registering */ + init_timer(&pod->startup_timer); + INIT_WORK(&pod->startup_work, podhd_startup_workqueue); + podhd_startup(pod); + return 0; } #define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod) @@ -103,10 +365,13 @@ static int podhd_init(struct usb_line6 *line6, /* table of devices that work with this driver */ static const struct usb_device_id podhd_id_table[] = { + /* TODO: no need to alloc data interfaces when only audio is used */ { LINE6_DEVICE(0x5057), .driver_info = LINE6_PODHD300 }, { LINE6_DEVICE(0x5058), .driver_info = LINE6_PODHD400 }, { LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500_0 }, { LINE6_IF_NUM(0x414D, 1), .driver_info = LINE6_PODHD500_1 }, + { LINE6_IF_NUM(0x414A, 0), .driver_info = LINE6_PODX3 }, + { LINE6_IF_NUM(0x414B, 0), .driver_info = LINE6_PODX3LIVE }, {} }; @@ -116,8 +381,7 @@ static const struct line6_properties podhd_properties_table[] = { [LINE6_PODHD300] = { .id = "PODHD300", .name = "POD HD300", - .capabilities = LINE6_CAP_CONTROL - | LINE6_CAP_PCM + .capabilities = LINE6_CAP_PCM | LINE6_CAP_HWMON, .altsetting = 5, .ep_ctrl_r = 0x84, @@ -128,8 +392,7 @@ static const struct line6_properties podhd_properties_table[] = { [LINE6_PODHD400] = { .id = "PODHD400", .name = "POD HD400", - .capabilities = LINE6_CAP_CONTROL - | LINE6_CAP_PCM + .capabilities = LINE6_CAP_PCM | LINE6_CAP_HWMON, .altsetting = 5, .ep_ctrl_r = 0x84, @@ -140,8 +403,7 @@ static const struct line6_properties podhd_properties_table[] = { [LINE6_PODHD500_0] = { .id = "PODHD500", .name = "POD HD500", - .capabilities = LINE6_CAP_CONTROL - | LINE6_CAP_PCM + .capabilities = LINE6_CAP_PCM | LINE6_CAP_HWMON, .altsetting = 1, .ep_ctrl_r = 0x81, @@ -152,8 +414,7 @@ static const struct line6_properties podhd_properties_table[] = { [LINE6_PODHD500_1] = { .id = "PODHD500", .name = "POD HD500", - .capabilities = LINE6_CAP_CONTROL - | LINE6_CAP_PCM + .capabilities = LINE6_CAP_PCM | LINE6_CAP_HWMON, .altsetting = 1, .ep_ctrl_r = 0x81, @@ -161,6 +422,30 @@ static const struct line6_properties podhd_properties_table[] = { .ep_audio_r = 0x86, .ep_audio_w = 0x02, }, + [LINE6_PODX3] = { + .id = "PODX3", + .name = "POD X3", + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM | LINE6_CAP_HWMON | LINE6_CAP_IN_NEEDS_OUT, + .altsetting = 1, + .ep_ctrl_r = 0x81, + .ep_ctrl_w = 0x01, + .ctrl_if = 1, + .ep_audio_r = 0x86, + .ep_audio_w = 0x02, + }, + [LINE6_PODX3LIVE] = { + .id = "PODX3LIVE", + .name = "POD X3 LIVE", + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM | LINE6_CAP_HWMON | LINE6_CAP_IN_NEEDS_OUT, + .altsetting = 1, + .ep_ctrl_r = 0x81, + .ep_ctrl_w = 0x01, + .ctrl_if = 1, + .ep_audio_r = 0x86, + .ep_audio_w = 0x02, + }, }; /* @@ -171,7 +456,7 @@ static int podhd_probe(struct usb_interface *interface, { return line6_probe(interface, id, "Line6-PODHD", &podhd_properties_table[id->driver_info], - podhd_init, sizeof(struct usb_line6)); + podhd_init, sizeof(struct usb_line6_podhd)); } static struct usb_driver podhd_driver = { diff --git a/sound/usb/line6/toneport.c b/sound/usb/line6/toneport.c index 6d4c50c9b17d..8e22f430d700 100644 --- a/sound/usb/line6/toneport.c +++ b/sound/usb/line6/toneport.c @@ -114,7 +114,7 @@ static struct line6_pcm_properties toneport_pcm_properties = { .rates = { .nrats = 1, .rats = &toneport_ratden}, - .bytes_per_frame = 4 + .bytes_per_channel = 2 }; static const struct { @@ -177,7 +177,7 @@ static int snd_toneport_monitor_put(struct snd_kcontrol *kcontrol, line6pcm->volume_monitor = ucontrol->value.integer.value[0]; if (line6pcm->volume_monitor > 0) { - err = line6_pcm_acquire(line6pcm, LINE6_STREAM_MONITOR); + err = line6_pcm_acquire(line6pcm, LINE6_STREAM_MONITOR, true); if (err < 0) { line6pcm->volume_monitor = 0; line6_pcm_release(line6pcm, LINE6_STREAM_MONITOR); @@ -246,7 +246,7 @@ static void toneport_start_pcm(unsigned long arg) struct usb_line6_toneport *toneport = (struct usb_line6_toneport *)arg; struct usb_line6 *line6 = &toneport->line6; - line6_pcm_acquire(line6->line6pcm, LINE6_STREAM_MONITOR); + line6_pcm_acquire(line6->line6pcm, LINE6_STREAM_MONITOR, true); } /* control definition */ diff --git a/sound/usb/line6/variax.c b/sound/usb/line6/variax.c index ddc23ddf0750..0c4512d0382e 100644 --- a/sound/usb/line6/variax.c +++ b/sound/usb/line6/variax.c @@ -259,7 +259,8 @@ static const struct line6_properties variax_properties_table[] = { [LINE6_PODXTLIVE_VARIAX] = { .id = "PODxtLive", .name = "PODxt Live", - .capabilities = LINE6_CAP_CONTROL, + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_CONTROL_MIDI, .altsetting = 1, .ep_ctrl_r = 0x86, .ep_ctrl_w = 0x05, @@ -269,7 +270,8 @@ static const struct line6_properties variax_properties_table[] = { [LINE6_VARIAX] = { .id = "Variax", .name = "Variax Workbench", - .capabilities = LINE6_CAP_CONTROL, + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_CONTROL_MIDI, .altsetting = 1, .ep_ctrl_r = 0x82, .ep_ctrl_w = 0x01, diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 2f8c388ef84f..4703caea56b2 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -932,9 +932,10 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval, case USB_ID(0x046d, 0x0826): /* HD Webcam c525 */ case USB_ID(0x046d, 0x08ca): /* Logitech Quickcam Fusion */ case USB_ID(0x046d, 0x0991): + case USB_ID(0x046d, 0x09a2): /* QuickCam Communicate Deluxe/S7500 */ /* Most audio usb devices lie about volume resolution. * Most Logitech webcams have res = 384. - * Proboly there is some logitech magic behind this number --fishor + * Probably there is some logitech magic behind this number --fishor */ if (!strcmp(kctl->id.name, "Mic Capture Volume")) { usb_audio_info(chip, diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index f6c3bf79af9a..04991b009132 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -1831,6 +1831,7 @@ void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer, } static void snd_dragonfly_quirk_db_scale(struct usb_mixer_interface *mixer, + struct usb_mixer_elem_info *cval, struct snd_kcontrol *kctl) { /* Approximation using 10 ranges based on output measurement on hw v1.2. @@ -1848,10 +1849,19 @@ static void snd_dragonfly_quirk_db_scale(struct usb_mixer_interface *mixer, 41, 50, TLV_DB_MINMAX_ITEM(-441, 0), ); - usb_audio_info(mixer->chip, "applying DragonFly dB scale quirk\n"); - kctl->tlv.p = scale; - kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; - kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; + if (cval->min == 0 && cval->max == 50) { + usb_audio_info(mixer->chip, "applying DragonFly dB scale quirk (0-50 variant)\n"); + kctl->tlv.p = scale; + kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; + kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; + + } else if (cval->min == 0 && cval->max <= 1000) { + /* Some other clearly broken DragonFly variant. + * At least a 0..53 variant (hw v1.0) exists. + */ + usb_audio_info(mixer->chip, "ignoring too narrow dB range on a DragonFly device"); + kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; + } } void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer, @@ -1860,8 +1870,8 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer, { switch (mixer->chip->usb_id) { case USB_ID(0x21b4, 0x0081): /* AudioQuest DragonFly */ - if (unitid == 7 && cval->min == 0 && cval->max == 50) - snd_dragonfly_quirk_db_scale(mixer, kctl); + if (unitid == 7 && cval->control == UAC_FU_VOLUME) + snd_dragonfly_quirk_db_scale(mixer, cval, kctl); break; } } diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 44d178ee9177..9aa5b1855481 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -218,7 +218,7 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface, } } -static int start_endpoints(struct snd_usb_substream *subs, bool can_sleep) +static int start_endpoints(struct snd_usb_substream *subs) { int err; @@ -231,7 +231,7 @@ static int start_endpoints(struct snd_usb_substream *subs, bool can_sleep) dev_dbg(&subs->dev->dev, "Starting data EP @%p\n", ep); ep->data_subs = subs; - err = snd_usb_endpoint_start(ep, can_sleep); + err = snd_usb_endpoint_start(ep); if (err < 0) { clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags); return err; @@ -260,7 +260,7 @@ static int start_endpoints(struct snd_usb_substream *subs, bool can_sleep) dev_dbg(&subs->dev->dev, "Starting sync EP @%p\n", ep); ep->sync_slave = subs->data_endpoint; - err = snd_usb_endpoint_start(ep, can_sleep); + err = snd_usb_endpoint_start(ep); if (err < 0) { clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags); return err; @@ -348,6 +348,16 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, alts = &iface->altsetting[1]; goto add_sync_ep; + case USB_ID(0x2466, 0x8003): + ep = 0x86; + iface = usb_ifnum_to_if(dev, 2); + + if (!iface || iface->num_altsetting == 0) + return -EINVAL; + + alts = &iface->altsetting[1]; + goto add_sync_ep; + } if (attr == USB_ENDPOINT_SYNC_ASYNC && altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC && @@ -806,17 +816,18 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) if (ret < 0) goto unlock; - iface = usb_ifnum_to_if(subs->dev, subs->cur_audiofmt->iface); - alts = &iface->altsetting[subs->cur_audiofmt->altset_idx]; - ret = snd_usb_init_sample_rate(subs->stream->chip, - subs->cur_audiofmt->iface, - alts, - subs->cur_audiofmt, - subs->cur_rate); - if (ret < 0) - goto unlock; - if (subs->need_setup_ep) { + + iface = usb_ifnum_to_if(subs->dev, subs->cur_audiofmt->iface); + alts = &iface->altsetting[subs->cur_audiofmt->altset_idx]; + ret = snd_usb_init_sample_rate(subs->stream->chip, + subs->cur_audiofmt->iface, + alts, + subs->cur_audiofmt, + subs->cur_rate); + if (ret < 0) + goto unlock; + ret = configure_endpoint(subs); if (ret < 0) goto unlock; @@ -839,7 +850,7 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) /* for playback, submit the URBs now; otherwise, the first hwptr_done * updates for all URBs would happen at the same time when starting */ if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) - ret = start_endpoints(subs, true); + ret = start_endpoints(subs); unlock: snd_usb_unlock_shutdown(subs->stream->chip); @@ -1655,7 +1666,7 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream switch (cmd) { case SNDRV_PCM_TRIGGER_START: - err = start_endpoints(subs, false); + err = start_endpoints(subs); if (err < 0) return err; diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index c60a776e815d..8a59d4782a0f 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2907,6 +2907,23 @@ AU0828_DEVICE(0x2040, 0x7260, "Hauppauge", "HVR-950Q"), AU0828_DEVICE(0x2040, 0x7213, "Hauppauge", "HVR-950Q"), AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), +/* Syntek STK1160 */ +{ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | + USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS, + .idVendor = 0x05e1, + .idProduct = 0x0408, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, + .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { + .vendor_name = "Syntek", + .product_name = "STK1160", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_AUDIO_ALIGN_TRANSFER + } +}, + /* Digidesign Mbox */ { /* Thanks to Clemens Ladisch <clemens@ladisch.de> */ diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 152292e5ee2b..eb4b9f7a571e 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1135,6 +1135,7 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip) case USB_ID(0x045E, 0x076F): /* MS Lifecam HD-6000 */ case USB_ID(0x045E, 0x0772): /* MS Lifecam Studio */ case USB_ID(0x045E, 0x0779): /* MS Lifecam HD-3000 */ + case USB_ID(0x047F, 0x02F7): /* Plantronics BT-600 */ case USB_ID(0x047F, 0x0415): /* Plantronics BT-300 */ case USB_ID(0x047F, 0xAA05): /* Plantronics DA45 */ case USB_ID(0x04D8, 0xFEEA): /* Benchmark DAC1 Pre */ @@ -1165,6 +1166,18 @@ static bool is_marantz_denon_dac(unsigned int id) return false; } +/* TEAC UD-501/UD-503/NT-503 USB DACs need a vendor cmd to switch + * between PCM/DOP and native DSD mode + */ +static bool is_teac_50X_dac(unsigned int id) +{ + switch (id) { + case USB_ID(0x0644, 0x8043): /* TEAC UD-501/UD-503/NT-503 */ + return true; + } + return false; +} + int snd_usb_select_mode_quirk(struct snd_usb_substream *subs, struct audioformat *fmt) { @@ -1192,6 +1205,26 @@ int snd_usb_select_mode_quirk(struct snd_usb_substream *subs, break; } mdelay(20); + } else if (is_teac_50X_dac(subs->stream->chip->usb_id)) { + /* Vendor mode switch cmd is required. */ + switch (fmt->altsetting) { + case 3: /* DSD mode (DSD_U32) requested */ + err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0, + USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, + 1, 1, NULL, 0); + if (err < 0) + return err; + break; + + case 2: /* PCM or DOP mode (S32) requested */ + case 1: /* PCM mode (S16) requested */ + err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0, + USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, + 0, 1, NULL, 0); + if (err < 0) + return err; + break; + } } return 0; } @@ -1217,6 +1250,12 @@ void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep) ep->chip->usb_id == USB_ID(0x0763, 0x2031)) && ep->type == SND_USB_ENDPOINT_TYPE_DATA) ep->skip_packets = 16; + + /* Work around devices that report unreasonable feedback data */ + if ((ep->chip->usb_id == USB_ID(0x0644, 0x8038) || /* TEAC UD-H01 */ + ep->chip->usb_id == USB_ID(0x1852, 0x5034)) && /* T+A Dac8 */ + ep->syncmaxsize == 4) + ep->tenor_fb_quirk = 1; } void snd_usb_set_interface_quirk(struct usb_device *dev) @@ -1331,5 +1370,11 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, return SNDRV_PCM_FMTBIT_DSD_U32_BE; } + /* TEAC devices with USB DAC functionality */ + if (is_teac_50X_dac(chip->usb_id)) { + if (fp->altsetting == 3) + return SNDRV_PCM_FMTBIT_DSD_U32_BE; + } + return 0; } |