diff options
Diffstat (limited to 'sound')
213 files changed, 10630 insertions, 5118 deletions
diff --git a/sound/aoa/fabrics/layout.c b/sound/aoa/fabrics/layout.c index 1eddf8fa188f..8797d42e2b76 100644 --- a/sound/aoa/fabrics/layout.c +++ b/sound/aoa/fabrics/layout.c @@ -776,7 +776,7 @@ static int check_codec(struct aoa_codec *codec, struct codec_connection *cc; /* if the codec has a 'codec' node, we require a reference */ - if (codec->node && (strcmp(codec->node->name, "codec") == 0)) { + if (of_node_name_eq(codec->node, "codec")) { snprintf(propname, sizeof(propname), "platform-%s-codec-ref", codec->name); ref = of_get_property(ldev->sound, propname, NULL); @@ -1008,8 +1008,8 @@ static int aoa_fabric_layout_probe(struct soundbus_dev *sdev) return -ENODEV; /* by breaking out we keep a reference */ - while ((sound = of_get_next_child(sdev->ofdev.dev.of_node, sound))) { - if (sound->type && strcasecmp(sound->type, "soundchip") == 0) + for_each_child_of_node(sdev->ofdev.dev.of_node, sound) { + if (of_node_is_type(sound, "soundchip")) break; } if (!sound) diff --git a/sound/aoa/soundbus/core.c b/sound/aoa/soundbus/core.c index 70bcaa7f93dd..065d3a55725e 100644 --- a/sound/aoa/soundbus/core.c +++ b/sound/aoa/soundbus/core.c @@ -74,11 +74,11 @@ static int soundbus_uevent(struct device *dev, struct kobj_uevent_env *env) of = &soundbus_dev->ofdev; /* stuff we want to pass to /sbin/hotplug */ - retval = add_uevent_var(env, "OF_NAME=%s", of->dev.of_node->name); + retval = add_uevent_var(env, "OF_NAME=%pOFn", of->dev.of_node); if (retval) return retval; - retval = add_uevent_var(env, "OF_TYPE=%s", of->dev.of_node->type); + retval = add_uevent_var(env, "OF_TYPE=%s", of_node_get_device_type(of->dev.of_node)); if (retval) return retval; diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c index bd7c5029fc59..c3f57a3fb1a5 100644 --- a/sound/aoa/soundbus/i2sbus/core.c +++ b/sound/aoa/soundbus/i2sbus/core.c @@ -154,7 +154,7 @@ static int i2sbus_add_dev(struct macio_dev *macio, struct device_node *np) { struct i2sbus_dev *dev; - struct device_node *child = NULL, *sound = NULL; + struct device_node *child, *sound = NULL; struct resource *r; int i, layout = 0, rlen, ok = force; char node_name[6]; @@ -177,8 +177,8 @@ static int i2sbus_add_dev(struct macio_dev *macio, return 0; i = 0; - while ((child = of_get_next_child(np, child))) { - if (strcmp(child->name, "sound") == 0) { + for_each_child_of_node(np, child) { + if (of_node_name_eq(child, "sound")) { i++; sound = child; } diff --git a/sound/aoa/soundbus/sysfs.c b/sound/aoa/soundbus/sysfs.c index 81da020bddef..a2d55e15afbb 100644 --- a/sound/aoa/soundbus/sysfs.c +++ b/sound/aoa/soundbus/sysfs.c @@ -1,18 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 #include <linux/kernel.h> +#include <linux/of.h> #include <linux/stat.h> /* FIX UP */ #include "soundbus.h" -#define soundbus_config_of_attr(field, format_string) \ -static ssize_t \ -field##_show (struct device *dev, struct device_attribute *attr, \ - char *buf) \ -{ \ - struct soundbus_dev *mdev = to_soundbus_device (dev); \ - return sprintf (buf, format_string, mdev->ofdev.dev.of_node->field); \ -} - static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -25,17 +17,33 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, strcat(buf, "\n"); length = strlen(buf); } else { - length = sprintf(buf, "of:N%sT%s\n", - of->dev.of_node->name, of->dev.of_node->type); + length = sprintf(buf, "of:N%pOFn%c%s\n", + of->dev.of_node, 'T', + of_node_get_device_type(of->dev.of_node)); } return length; } static DEVICE_ATTR_RO(modalias); -soundbus_config_of_attr (name, "%s\n"); +static ssize_t name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct soundbus_dev *sdev = to_soundbus_device(dev); + struct platform_device *of = &sdev->ofdev; + + return sprintf(buf, "%pOFn\n", of->dev.of_node); +} static DEVICE_ATTR_RO(name); -soundbus_config_of_attr (type, "%s\n"); + +static ssize_t type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct soundbus_dev *sdev = to_soundbus_device(dev); + struct platform_device *of = &sdev->ofdev; + + return sprintf(buf, "%s\n", of_node_get_device_type(of->dev.of_node)); +} static DEVICE_ATTR_RO(type); struct attribute *soundbus_dev_attrs[] = { diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 26b5e245b074..a5b09e75e787 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -171,7 +171,8 @@ static int snd_compr_free(struct inode *inode, struct file *f) } data->stream.ops->free(&data->stream); - kfree(data->stream.runtime->buffer); + if (!data->stream.runtime->dma_buffer_p) + kfree(data->stream.runtime->buffer); kfree(data->stream.runtime); kfree(data); return 0; @@ -505,7 +506,7 @@ static int snd_compr_allocate_buffer(struct snd_compr_stream *stream, struct snd_compr_params *params) { unsigned int buffer_size; - void *buffer; + void *buffer = NULL; buffer_size = params->buffer.fragment_size * params->buffer.fragments; if (stream->ops->copy) { @@ -514,7 +515,18 @@ static int snd_compr_allocate_buffer(struct snd_compr_stream *stream, * the data from core */ } else { - buffer = kmalloc(buffer_size, GFP_KERNEL); + if (stream->runtime->dma_buffer_p) { + + if (buffer_size > stream->runtime->dma_buffer_p->bytes) + dev_err(&stream->device->dev, + "Not enough DMA buffer"); + else + buffer = stream->runtime->dma_buffer_p->area; + + } else { + buffer = kmalloc(buffer_size, GFP_KERNEL); + } + if (!buffer) return -ENOMEM; } diff --git a/sound/core/control.c b/sound/core/control.c index 649d3217590e..fad7db402443 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -348,22 +348,41 @@ static int snd_ctl_find_hole(struct snd_card *card, unsigned int count) return 0; } -/* add a new kcontrol object; call with card->controls_rwsem locked */ -static int __snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) +enum snd_ctl_add_mode { + CTL_ADD_EXCLUSIVE, CTL_REPLACE, CTL_ADD_ON_REPLACE, +}; + +/* add/replace a new kcontrol object; call with card->controls_rwsem locked */ +static int __snd_ctl_add_replace(struct snd_card *card, + struct snd_kcontrol *kcontrol, + enum snd_ctl_add_mode mode) { struct snd_ctl_elem_id id; unsigned int idx; unsigned int count; + struct snd_kcontrol *old; + int err; id = kcontrol->id; if (id.index > UINT_MAX - kcontrol->count) return -EINVAL; - if (snd_ctl_find_id(card, &id)) { - dev_err(card->dev, - "control %i:%i:%i:%s:%i is already present\n", - id.iface, id.device, id.subdevice, id.name, id.index); - return -EBUSY; + old = snd_ctl_find_id(card, &id); + if (!old) { + if (mode == CTL_REPLACE) + return -EINVAL; + } else { + if (mode == CTL_ADD_EXCLUSIVE) { + dev_err(card->dev, + "control %i:%i:%i:%s:%i is already present\n", + id.iface, id.device, id.subdevice, id.name, + id.index); + return -EBUSY; + } + + err = snd_ctl_remove(card, old); + if (err < 0) + return err; } if (snd_ctl_find_hole(card, kcontrol->count) < 0) @@ -382,21 +401,9 @@ static int __snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) return 0; } -/** - * snd_ctl_add - add the control instance to the card - * @card: the card instance - * @kcontrol: the control instance to add - * - * Adds the control instance created via snd_ctl_new() or - * snd_ctl_new1() to the given card. Assigns also an unique - * numid used for fast search. - * - * It frees automatically the control which cannot be added. - * - * Return: Zero if successful, or a negative error code on failure. - * - */ -int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) +static int snd_ctl_add_replace(struct snd_card *card, + struct snd_kcontrol *kcontrol, + enum snd_ctl_add_mode mode) { int err = -EINVAL; @@ -406,7 +413,7 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) goto error; down_write(&card->controls_rwsem); - err = __snd_ctl_add(card, kcontrol); + err = __snd_ctl_add_replace(card, kcontrol, mode); up_write(&card->controls_rwsem); if (err < 0) goto error; @@ -416,6 +423,25 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) snd_ctl_free_one(kcontrol); return err; } + +/** + * snd_ctl_add - add the control instance to the card + * @card: the card instance + * @kcontrol: the control instance to add + * + * Adds the control instance created via snd_ctl_new() or + * snd_ctl_new1() to the given card. Assigns also an unique + * numid used for fast search. + * + * It frees automatically the control which cannot be added. + * + * Return: Zero if successful, or a negative error code on failure. + * + */ +int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) +{ + return snd_ctl_add_replace(card, kcontrol, CTL_ADD_EXCLUSIVE); +} EXPORT_SYMBOL(snd_ctl_add); /** @@ -435,53 +461,8 @@ EXPORT_SYMBOL(snd_ctl_add); int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol, bool add_on_replace) { - struct snd_ctl_elem_id id; - unsigned int count; - unsigned int idx; - struct snd_kcontrol *old; - int ret; - - if (!kcontrol) - return -EINVAL; - if (snd_BUG_ON(!card || !kcontrol->info)) { - ret = -EINVAL; - goto error; - } - id = kcontrol->id; - down_write(&card->controls_rwsem); - old = snd_ctl_find_id(card, &id); - if (!old) { - if (add_on_replace) - goto add; - up_write(&card->controls_rwsem); - ret = -EINVAL; - goto error; - } - ret = snd_ctl_remove(card, old); - if (ret < 0) { - up_write(&card->controls_rwsem); - goto error; - } -add: - if (snd_ctl_find_hole(card, kcontrol->count) < 0) { - up_write(&card->controls_rwsem); - ret = -ENOMEM; - goto error; - } - list_add_tail(&kcontrol->list, &card->controls); - card->controls_count += kcontrol->count; - kcontrol->id.numid = card->last_numid + 1; - card->last_numid += kcontrol->count; - id = kcontrol->id; - count = kcontrol->count; - up_write(&card->controls_rwsem); - for (idx = 0; idx < count; idx++, id.index++, id.numid++) - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id); - return 0; - -error: - snd_ctl_free_one(kcontrol); - return ret; + return snd_ctl_add_replace(card, kcontrol, + add_on_replace ? CTL_ADD_ON_REPLACE : CTL_REPLACE); } EXPORT_SYMBOL(snd_ctl_replace); @@ -1369,7 +1350,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, /* This function manage to free the instance on failure. */ down_write(&card->controls_rwsem); - err = __snd_ctl_add(card, kctl); + err = __snd_ctl_add_replace(card, kctl, CTL_ADD_EXCLUSIVE); if (err < 0) { snd_ctl_free_one(kctl); goto unlock; diff --git a/sound/core/pcm.c b/sound/core/pcm.c index fdb9b92fc8d6..01b9d62eef14 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -25,6 +25,7 @@ #include <linux/time.h> #include <linux/mutex.h> #include <linux/device.h> +#include <linux/nospec.h> #include <sound/core.h> #include <sound/minors.h> #include <sound/pcm.h> @@ -129,6 +130,7 @@ static int snd_pcm_control_ioctl(struct snd_card *card, return -EFAULT; if (stream < 0 || stream > 1) return -EINVAL; + stream = array_index_nospec(stream, 2); if (get_user(subdevice, &info->subdevice)) return -EFAULT; mutex_lock(®ister_mutex); diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index 8a146b039276..052e00590259 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -41,6 +41,7 @@ config SND_OXFW * Mackie(Loud) U.420/U.420d * TASCAM FireOne * Stanton Controllers & Systems 1 Deck/Mixer + * APOGEE duet FireWire To compile this driver as a module, choose M here: the module will be called snd-oxfw. @@ -161,5 +162,6 @@ config SND_FIREFACE help Say Y here to include support for RME fireface series. * Fireface 400 + * Fireface 800 endif # SND_FIREWIRE diff --git a/sound/firewire/amdtp-stream-trace.h b/sound/firewire/amdtp-stream-trace.h index 54cdd4ffa9ce..ac20acf48fc6 100644 --- a/sound/firewire/amdtp-stream-trace.h +++ b/sound/firewire/amdtp-stream-trace.h @@ -131,7 +131,7 @@ TRACE_EVENT(in_packet_without_header, __entry->index = index; ), TP_printk( - "%02u %04u %04x %04x %02d %03u %3u %3u %02u %01u %02u", + "%02u %04u %04x %04x %02d %03u %02u %03u %02u %01u %02u", __entry->second, __entry->cycle, __entry->src, @@ -169,7 +169,7 @@ TRACE_EVENT(out_packet_without_header, __entry->dest = fw_parent_device(s->unit)->node_id; __entry->payload_quadlets = payload_length / 4; __entry->data_blocks = data_blocks, - __entry->data_blocks = s->data_block_counter, + __entry->data_block_counter = s->data_block_counter, __entry->packet_index = s->packet_index; __entry->irq = !!in_interrupt(); __entry->index = index; diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 9be76c808fcc..3ada55ed5381 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -654,15 +654,17 @@ end: } static int handle_in_packet_without_header(struct amdtp_stream *s, - unsigned int payload_quadlets, unsigned int cycle, + unsigned int payload_length, unsigned int cycle, unsigned int index) { __be32 *buffer; + unsigned int payload_quadlets; unsigned int data_blocks; struct snd_pcm_substream *pcm; unsigned int pcm_frames; buffer = s->buffer.packets[s->packet_index].buffer; + payload_quadlets = payload_length / 4; data_blocks = payload_quadlets / s->data_block_quadlets; trace_in_packet_without_header(s, cycle, payload_quadlets, data_blocks, diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index 672d13488454..d91874275d2c 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c @@ -408,7 +408,7 @@ static const struct ieee1394_device_id bebob_id_table[] = { /* Apogee Electronics, DA/AD/DD-16X (X-FireWire card) */ SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x00010048, &spec_normal), /* Apogee Electronics, Ensemble */ - SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x00001eee, &spec_normal), + SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x01eeee, &spec_normal), /* ESI, Quatafire610 */ SND_BEBOB_DEV_ENTRY(VEN_ESI, 0x00010064, &spec_normal), /* AcousticReality, eARMasterOne */ diff --git a/sound/firewire/fireface/Makefile b/sound/firewire/fireface/Makefile index 8f807284ba54..79a7d6d99d72 100644 --- a/sound/firewire/fireface/Makefile +++ b/sound/firewire/fireface/Makefile @@ -1,3 +1,4 @@ snd-fireface-objs := ff.o ff-transaction.o ff-midi.o ff-proc.o amdtp-ff.o \ - ff-stream.o ff-pcm.o ff-hwdep.o ff-protocol-ff400.o + ff-stream.o ff-pcm.o ff-hwdep.o ff-protocol-ff400.o \ + ff-protocol-ff800.o obj-$(CONFIG_SND_FIREFACE) += snd-fireface.o diff --git a/sound/firewire/fireface/ff-pcm.c b/sound/firewire/fireface/ff-pcm.c index bf47f9ec8703..d0bc96b20a65 100644 --- a/sound/firewire/fireface/ff-pcm.c +++ b/sound/firewire/fireface/ff-pcm.c @@ -8,11 +8,6 @@ #include "ff.h" -static inline unsigned int get_multiplier_mode_with_index(unsigned int index) -{ - return ((int)index - 1) / 2; -} - static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { @@ -24,10 +19,16 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; - unsigned int i, mode; + unsigned int i; for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) { - mode = get_multiplier_mode_with_index(i); + enum snd_ff_stream_mode mode; + int err; + + err = snd_ff_stream_get_multiplier_mode(i, &mode); + if (err < 0) + continue; + if (!snd_interval_test(c, pcm_channels[mode])) continue; @@ -49,10 +50,16 @@ static int hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; - unsigned int i, mode; + unsigned int i; for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) { - mode = get_multiplier_mode_with_index(i); + enum snd_ff_stream_mode mode; + int err; + + err = snd_ff_stream_get_multiplier_mode(i, &mode); + if (err < 0) + continue; + if (!snd_interval_test(r, amdtp_rate_table[i])) continue; @@ -66,7 +73,6 @@ static int hw_rule_channels(struct snd_pcm_hw_params *params, static void limit_channels_and_rates(struct snd_pcm_hardware *hw, const unsigned int *pcm_channels) { - unsigned int mode; unsigned int rate, channels; int i; @@ -76,7 +82,12 @@ static void limit_channels_and_rates(struct snd_pcm_hardware *hw, hw->rate_max = 0; for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) { - mode = get_multiplier_mode_with_index(i); + enum snd_ff_stream_mode mode; + int err; + + err = snd_ff_stream_get_multiplier_mode(i, &mode); + if (err < 0) + continue; channels = pcm_channels[mode]; if (pcm_channels[mode] == 0) @@ -141,7 +152,7 @@ static int pcm_open(struct snd_pcm_substream *substream) if (err < 0) goto release_lock; - err = ff->spec->protocol->get_clock(ff, &rate, &src); + err = snd_ff_transaction_get_clock(ff, &rate, &src); if (err < 0) goto release_lock; diff --git a/sound/firewire/fireface/ff-proc.c b/sound/firewire/fireface/ff-proc.c index 40ccbfd8ef89..a0c550dabe9a 100644 --- a/sound/firewire/fireface/ff-proc.c +++ b/sound/firewire/fireface/ff-proc.c @@ -12,16 +12,205 @@ static void proc_dump_clock_config(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_ff *ff = entry->private_data; + __le32 reg; + u32 data; + unsigned int rate; + const char *src; + int err; - ff->spec->protocol->dump_clock_config(ff, buffer); + err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST, + SND_FF_REG_CLOCK_CONFIG, ®, sizeof(reg), 0); + if (err < 0) + return; + + data = le32_to_cpu(reg); + + snd_iprintf(buffer, "Output S/PDIF format: %s (Emphasis: %s)\n", + (data & 0x20) ? "Professional" : "Consumer", + (data & 0x40) ? "on" : "off"); + + snd_iprintf(buffer, "Optical output interface format: %s\n", + ((data >> 8) & 0x01) ? "S/PDIF" : "ADAT"); + + snd_iprintf(buffer, "Word output single speed: %s\n", + ((data >> 8) & 0x20) ? "on" : "off"); + + snd_iprintf(buffer, "S/PDIF input interface: %s\n", + ((data >> 8) & 0x02) ? "Optical" : "Coaxial"); + + switch ((data >> 1) & 0x03) { + case 0x01: + rate = 32000; + break; + case 0x00: + rate = 44100; + break; + case 0x03: + rate = 48000; + break; + case 0x02: + default: + return; + } + + if (data & 0x08) + rate *= 2; + else if (data & 0x10) + rate *= 4; + + snd_iprintf(buffer, "Sampling rate: %d\n", rate); + + if (data & 0x01) { + src = "Internal"; + } else { + switch ((data >> 10) & 0x07) { + case 0x00: + src = "ADAT1"; + break; + case 0x01: + src = "ADAT2"; + break; + case 0x03: + src = "S/PDIF"; + break; + case 0x04: + src = "Word"; + break; + case 0x05: + src = "LTC"; + break; + default: + return; + } + } + + snd_iprintf(buffer, "Sync to clock source: %s\n", src); } static void proc_dump_sync_status(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_ff *ff = entry->private_data; + __le32 reg; + u32 data; + int err; + + err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, + SND_FF_REG_SYNC_STATUS, ®, sizeof(reg), 0); + if (err < 0) + return; + + data = le32_to_cpu(reg); + + snd_iprintf(buffer, "External source detection:\n"); + + snd_iprintf(buffer, "Word Clock:"); + if ((data >> 24) & 0x20) { + if ((data >> 24) & 0x40) + snd_iprintf(buffer, "sync\n"); + else + snd_iprintf(buffer, "lock\n"); + } else { + snd_iprintf(buffer, "none\n"); + } + + snd_iprintf(buffer, "S/PDIF:"); + if ((data >> 16) & 0x10) { + if ((data >> 16) & 0x04) + snd_iprintf(buffer, "sync\n"); + else + snd_iprintf(buffer, "lock\n"); + } else { + snd_iprintf(buffer, "none\n"); + } + + snd_iprintf(buffer, "ADAT1:"); + if ((data >> 8) & 0x04) { + if ((data >> 8) & 0x10) + snd_iprintf(buffer, "sync\n"); + else + snd_iprintf(buffer, "lock\n"); + } else { + snd_iprintf(buffer, "none\n"); + } + + snd_iprintf(buffer, "ADAT2:"); + if ((data >> 8) & 0x08) { + if ((data >> 8) & 0x20) + snd_iprintf(buffer, "sync\n"); + else + snd_iprintf(buffer, "lock\n"); + } else { + snd_iprintf(buffer, "none\n"); + } + + snd_iprintf(buffer, "\nUsed external source:\n"); + + if (((data >> 22) & 0x07) == 0x07) { + snd_iprintf(buffer, "None\n"); + } else { + switch ((data >> 22) & 0x07) { + case 0x00: + snd_iprintf(buffer, "ADAT1:"); + break; + case 0x01: + snd_iprintf(buffer, "ADAT2:"); + break; + case 0x03: + snd_iprintf(buffer, "S/PDIF:"); + break; + case 0x04: + snd_iprintf(buffer, "Word:"); + break; + case 0x07: + snd_iprintf(buffer, "Nothing:"); + break; + case 0x02: + case 0x05: + case 0x06: + default: + snd_iprintf(buffer, "unknown:"); + break; + } + + if ((data >> 25) & 0x07) { + switch ((data >> 25) & 0x07) { + case 0x01: + snd_iprintf(buffer, "32000\n"); + break; + case 0x02: + snd_iprintf(buffer, "44100\n"); + break; + case 0x03: + snd_iprintf(buffer, "48000\n"); + break; + case 0x04: + snd_iprintf(buffer, "64000\n"); + break; + case 0x05: + snd_iprintf(buffer, "88200\n"); + break; + case 0x06: + snd_iprintf(buffer, "96000\n"); + break; + case 0x07: + snd_iprintf(buffer, "128000\n"); + break; + case 0x08: + snd_iprintf(buffer, "176400\n"); + break; + case 0x09: + snd_iprintf(buffer, "192000\n"); + break; + case 0x00: + snd_iprintf(buffer, "unknown\n"); + break; + } + } + } - ff->spec->protocol->dump_sync_status(ff, buffer); + snd_iprintf(buffer, "Multiplied:"); + snd_iprintf(buffer, "%d\n", (data & 0x3ff) * 250); } static void add_node(struct snd_ff *ff, struct snd_info_entry *root, diff --git a/sound/firewire/fireface/ff-protocol-ff400.c b/sound/firewire/fireface/ff-protocol-ff400.c index 64c3cb0fb926..2280fab9b3c7 100644 --- a/sound/firewire/fireface/ff-protocol-ff400.c +++ b/sound/firewire/fireface/ff-protocol-ff400.c @@ -14,85 +14,60 @@ #define FF400_ISOC_COMM_START 0x000080100508ull #define FF400_TX_PACKET_FORMAT 0x00008010050cull #define FF400_ISOC_COMM_STOP 0x000080100510ull -#define FF400_SYNC_STATUS 0x0000801c0000ull -#define FF400_FETCH_PCM_FRAMES 0x0000801c0000ull /* For block request. */ -#define FF400_CLOCK_CONFIG 0x0000801c0004ull -#define FF400_MIDI_HIGH_ADDR 0x0000801003f4ull -#define FF400_MIDI_RX_PORT_0 0x000080180000ull -#define FF400_MIDI_RX_PORT_1 0x000080190000ull - -static int ff400_get_clock(struct snd_ff *ff, unsigned int *rate, - enum snd_ff_clock_src *src) +/* + * Fireface 400 manages isochronous channel number in 3 bit field. Therefore, + * we can allocate between 0 and 7 channel. + */ +static int keep_resources(struct snd_ff *ff, unsigned int rate) { - __le32 reg; - u32 data; + enum snd_ff_stream_mode mode; + int i; int err; - err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, - FF400_SYNC_STATUS, ®, sizeof(reg), 0); + // Check whether the given value is supported or not. + for (i = 0; i < CIP_SFC_COUNT; i++) { + if (amdtp_rate_table[i] == rate) + break; + } + if (i >= CIP_SFC_COUNT) + return -EINVAL; + + err = snd_ff_stream_get_multiplier_mode(i, &mode); if (err < 0) return err; - data = le32_to_cpu(reg); - /* Calculate sampling rate. */ - switch ((data >> 1) & 0x03) { - case 0x01: - *rate = 32000; - break; - case 0x00: - *rate = 44100; - break; - case 0x03: - *rate = 48000; - break; - case 0x02: - default: - return -EIO; - } - - if (data & 0x08) - *rate *= 2; - else if (data & 0x10) - *rate *= 4; + /* Keep resources for in-stream. */ + ff->tx_resources.channels_mask = 0x00000000000000ffuLL; + err = fw_iso_resources_allocate(&ff->tx_resources, + amdtp_stream_get_max_payload(&ff->tx_stream), + fw_parent_device(ff->unit)->max_speed); + if (err < 0) + return err; - /* Calculate source of clock. */ - if (data & 0x01) { - *src = SND_FF_CLOCK_SRC_INTERNAL; - } else { - /* TODO: 0x00, 0x01, 0x02, 0x06, 0x07? */ - switch ((data >> 10) & 0x07) { - case 0x03: - *src = SND_FF_CLOCK_SRC_SPDIF; - break; - case 0x04: - *src = SND_FF_CLOCK_SRC_WORD; - break; - case 0x05: - *src = SND_FF_CLOCK_SRC_LTC; - break; - case 0x00: - default: - *src = SND_FF_CLOCK_SRC_ADAT; - break; - } - } + /* Keep resources for out-stream. */ + err = amdtp_ff_set_parameters(&ff->rx_stream, rate, + ff->spec->pcm_playback_channels[mode]); + if (err < 0) + return err; + ff->rx_resources.channels_mask = 0x00000000000000ffuLL; + err = fw_iso_resources_allocate(&ff->rx_resources, + amdtp_stream_get_max_payload(&ff->rx_stream), + fw_parent_device(ff->unit)->max_speed); + if (err < 0) + fw_iso_resources_free(&ff->tx_resources); - return 0; + return err; } static int ff400_begin_session(struct snd_ff *ff, unsigned int rate) { __le32 reg; - int i, err; + int err; - /* Check whether the given value is supported or not. */ - for (i = 0; i < CIP_SFC_COUNT; i++) { - if (amdtp_rate_table[i] == rate) - break; - } - if (i == CIP_SFC_COUNT) - return -EINVAL; + err = keep_resources(ff, rate); + if (err < 0) + return err; /* Set the number of data blocks transferred in a second. */ reg = cpu_to_le32(rate); @@ -142,233 +117,45 @@ static void ff400_finish_session(struct snd_ff *ff) FF400_ISOC_COMM_STOP, ®, sizeof(reg), 0); } -static int ff400_switch_fetching_mode(struct snd_ff *ff, bool enable) +static void ff400_handle_midi_msg(struct snd_ff *ff, __le32 *buf, size_t length) { - __le32 *reg; int i; - int err; - reg = kcalloc(18, sizeof(__le32), GFP_KERNEL); - if (reg == NULL) - return -ENOMEM; + for (i = 0; i < length / 4; i++) { + u32 quad = le32_to_cpu(buf[i]); + u8 byte; + unsigned int index; + struct snd_rawmidi_substream *substream; - if (enable) { + /* Message in first port. */ /* - * Each quadlet is corresponding to data channels in a data - * blocks in reverse order. Precisely, quadlets for available - * data channels should be enabled. Here, I take second best - * to fetch PCM frames from all of data channels regardless of - * stf. + * This value may represent the index of this unit when the same + * units are on the same IEEE 1394 bus. This driver doesn't use + * it. */ - for (i = 0; i < 18; ++i) - reg[i] = cpu_to_le32(0x00000001); - } - - err = snd_fw_transaction(ff->unit, TCODE_WRITE_BLOCK_REQUEST, - FF400_FETCH_PCM_FRAMES, reg, - sizeof(__le32) * 18, 0); - kfree(reg); - return err; -} - -static void ff400_dump_sync_status(struct snd_ff *ff, - struct snd_info_buffer *buffer) -{ - __le32 reg; - u32 data; - int err; - - err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, - FF400_SYNC_STATUS, ®, sizeof(reg), 0); - if (err < 0) - return; - - data = le32_to_cpu(reg); - - snd_iprintf(buffer, "External source detection:\n"); - - snd_iprintf(buffer, "Word Clock:"); - if ((data >> 24) & 0x20) { - if ((data >> 24) & 0x40) - snd_iprintf(buffer, "sync\n"); - else - snd_iprintf(buffer, "lock\n"); - } else { - snd_iprintf(buffer, "none\n"); - } - - snd_iprintf(buffer, "S/PDIF:"); - if ((data >> 16) & 0x10) { - if ((data >> 16) & 0x04) - snd_iprintf(buffer, "sync\n"); - else - snd_iprintf(buffer, "lock\n"); - } else { - snd_iprintf(buffer, "none\n"); - } - - snd_iprintf(buffer, "ADAT:"); - if ((data >> 8) & 0x04) { - if ((data >> 8) & 0x10) - snd_iprintf(buffer, "sync\n"); - else - snd_iprintf(buffer, "lock\n"); - } else { - snd_iprintf(buffer, "none\n"); - } - - snd_iprintf(buffer, "\nUsed external source:\n"); - - if (((data >> 22) & 0x07) == 0x07) { - snd_iprintf(buffer, "None\n"); - } else { - switch ((data >> 22) & 0x07) { - case 0x00: - snd_iprintf(buffer, "ADAT:"); - break; - case 0x03: - snd_iprintf(buffer, "S/PDIF:"); - break; - case 0x04: - snd_iprintf(buffer, "Word:"); - break; - case 0x07: - snd_iprintf(buffer, "Nothing:"); - break; - case 0x01: - case 0x02: - case 0x05: - case 0x06: - default: - snd_iprintf(buffer, "unknown:"); - break; - } - - if ((data >> 25) & 0x07) { - switch ((data >> 25) & 0x07) { - case 0x01: - snd_iprintf(buffer, "32000\n"); - break; - case 0x02: - snd_iprintf(buffer, "44100\n"); - break; - case 0x03: - snd_iprintf(buffer, "48000\n"); - break; - case 0x04: - snd_iprintf(buffer, "64000\n"); - break; - case 0x05: - snd_iprintf(buffer, "88200\n"); - break; - case 0x06: - snd_iprintf(buffer, "96000\n"); - break; - case 0x07: - snd_iprintf(buffer, "128000\n"); - break; - case 0x08: - snd_iprintf(buffer, "176400\n"); - break; - case 0x09: - snd_iprintf(buffer, "192000\n"); - break; - case 0x00: - snd_iprintf(buffer, "unknown\n"); - break; + index = (quad >> 8) & 0xff; + if (index > 0) { + substream = READ_ONCE(ff->tx_midi_substreams[0]); + if (substream != NULL) { + byte = quad & 0xff; + snd_rawmidi_receive(substream, &byte, 1); } } - } - - snd_iprintf(buffer, "Multiplied:"); - snd_iprintf(buffer, "%d\n", (data & 0x3ff) * 250); -} -static void ff400_dump_clock_config(struct snd_ff *ff, - struct snd_info_buffer *buffer) -{ - __le32 reg; - u32 data; - unsigned int rate; - const char *src; - int err; - - err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST, - FF400_CLOCK_CONFIG, ®, sizeof(reg), 0); - if (err < 0) - return; - - data = le32_to_cpu(reg); - - snd_iprintf(buffer, "Output S/PDIF format: %s (Emphasis: %s)\n", - (data & 0x20) ? "Professional" : "Consumer", - (data & 0x40) ? "on" : "off"); - - snd_iprintf(buffer, "Optical output interface format: %s\n", - ((data >> 8) & 0x01) ? "S/PDIF" : "ADAT"); - - snd_iprintf(buffer, "Word output single speed: %s\n", - ((data >> 8) & 0x20) ? "on" : "off"); - - snd_iprintf(buffer, "S/PDIF input interface: %s\n", - ((data >> 8) & 0x02) ? "Optical" : "Coaxial"); - - switch ((data >> 1) & 0x03) { - case 0x01: - rate = 32000; - break; - case 0x00: - rate = 44100; - break; - case 0x03: - rate = 48000; - break; - case 0x02: - default: - return; - } - - if (data & 0x08) - rate *= 2; - else if (data & 0x10) - rate *= 4; - - snd_iprintf(buffer, "Sampling rate: %d\n", rate); - - if (data & 0x01) { - src = "Internal"; - } else { - switch ((data >> 10) & 0x07) { - case 0x00: - src = "ADAT"; - break; - case 0x03: - src = "S/PDIF"; - break; - case 0x04: - src = "Word"; - break; - case 0x05: - src = "LTC"; - break; - default: - return; + /* Message in second port. */ + index = (quad >> 24) & 0xff; + if (index > 0) { + substream = READ_ONCE(ff->tx_midi_substreams[1]); + if (substream != NULL) { + byte = (quad >> 16) & 0xff; + snd_rawmidi_receive(substream, &byte, 1); + } } } - - snd_iprintf(buffer, "Sync to clock source: %s\n", src); } const struct snd_ff_protocol snd_ff_protocol_ff400 = { - .get_clock = ff400_get_clock, + .handle_midi_msg = ff400_handle_midi_msg, .begin_session = ff400_begin_session, .finish_session = ff400_finish_session, - .switch_fetching_mode = ff400_switch_fetching_mode, - - .dump_sync_status = ff400_dump_sync_status, - .dump_clock_config = ff400_dump_clock_config, - - .midi_high_addr_reg = FF400_MIDI_HIGH_ADDR, - .midi_rx_port_0_reg = FF400_MIDI_RX_PORT_0, - .midi_rx_port_1_reg = FF400_MIDI_RX_PORT_1, }; diff --git a/sound/firewire/fireface/ff-protocol-ff800.c b/sound/firewire/fireface/ff-protocol-ff800.c new file mode 100644 index 000000000000..2acbf6039770 --- /dev/null +++ b/sound/firewire/fireface/ff-protocol-ff800.c @@ -0,0 +1,143 @@ +/* + * ff-protocol-ff800.c - a part of driver for RME Fireface series + * + * Copyright (c) 2018 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include <linux/delay.h> + +#include "ff.h" + +#define FF800_STF 0x0000fc88f000 +#define FF800_RX_PACKET_FORMAT 0x0000fc88f004 +#define FF800_ALLOC_TX_STREAM 0x0000fc88f008 +#define FF800_ISOC_COMM_START 0x0000fc88f00c +#define FF800_TX_S800_FLAG 0x00000800 +#define FF800_ISOC_COMM_STOP 0x0000fc88f010 + +#define FF800_TX_PACKET_ISOC_CH 0x0000801c0008 + +static int allocate_rx_resources(struct snd_ff *ff) +{ + u32 data; + __le32 reg; + int err; + + // Controllers should allocate isochronous resources for rx stream. + err = fw_iso_resources_allocate(&ff->rx_resources, + amdtp_stream_get_max_payload(&ff->rx_stream), + fw_parent_device(ff->unit)->max_speed); + if (err < 0) + return err; + + // Set isochronous channel and the number of quadlets of rx packets. + data = ff->rx_stream.data_block_quadlets << 3; + data = (data << 8) | ff->rx_resources.channel; + reg = cpu_to_le32(data); + return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF800_RX_PACKET_FORMAT, ®, sizeof(reg), 0); +} + +static int allocate_tx_resources(struct snd_ff *ff) +{ + __le32 reg; + unsigned int count; + unsigned int tx_isoc_channel; + int err; + + reg = cpu_to_le32(ff->tx_stream.data_block_quadlets); + err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF800_ALLOC_TX_STREAM, ®, sizeof(reg), 0); + if (err < 0) + return err; + + // Wait till the format of tx packet is available. + count = 0; + while (count++ < 10) { + u32 data; + err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, + FF800_TX_PACKET_ISOC_CH, ®, sizeof(reg), 0); + if (err < 0) + return err; + + data = le32_to_cpu(reg); + if (data != 0xffffffff) { + tx_isoc_channel = data; + break; + } + + msleep(50); + } + if (count >= 10) + return -ETIMEDOUT; + + // NOTE: this is a makeshift to start OHCI 1394 IR context in the + // channel. On the other hand, 'struct fw_iso_resources.allocated' is + // not true and it's not deallocated at stop. + ff->tx_resources.channel = tx_isoc_channel; + + return 0; +} + +static int ff800_begin_session(struct snd_ff *ff, unsigned int rate) +{ + __le32 reg; + int err; + + reg = cpu_to_le32(rate); + err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF800_STF, ®, sizeof(reg), 0); + if (err < 0) + return err; + + // If starting isochronous communication immediately, change of STF has + // no effect. In this case, the communication runs based on former STF. + // Let's sleep for a bit. + msleep(100); + + err = allocate_rx_resources(ff); + if (err < 0) + return err; + + err = allocate_tx_resources(ff); + if (err < 0) + return err; + + reg = cpu_to_le32(0x80000000); + reg |= cpu_to_le32(ff->tx_stream.data_block_quadlets); + if (fw_parent_device(ff->unit)->max_speed == SCODE_800) + reg |= cpu_to_le32(FF800_TX_S800_FLAG); + return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF800_ISOC_COMM_START, ®, sizeof(reg), 0); +} + +static void ff800_finish_session(struct snd_ff *ff) +{ + __le32 reg; + + reg = cpu_to_le32(0x80000000); + snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF800_ISOC_COMM_STOP, ®, sizeof(reg), 0); +} + +static void ff800_handle_midi_msg(struct snd_ff *ff, __le32 *buf, size_t length) +{ + int i; + + for (i = 0; i < length / 4; i++) { + u8 byte = le32_to_cpu(buf[i]) & 0xff; + struct snd_rawmidi_substream *substream; + + substream = READ_ONCE(ff->tx_midi_substreams[0]); + if (substream) + snd_rawmidi_receive(substream, &byte, 1); + } +} + +const struct snd_ff_protocol snd_ff_protocol_ff800 = { + .handle_midi_msg = ff800_handle_midi_msg, + .begin_session = ff800_begin_session, + .finish_session = ff800_finish_session, +}; diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c index 78880922120e..a490e4553721 100644 --- a/sound/firewire/fireface/ff-stream.c +++ b/sound/firewire/fireface/ff-stream.c @@ -10,73 +10,71 @@ #define CALLBACK_TIMEOUT_MS 200 -static int get_rate_mode(unsigned int rate, unsigned int *mode) +int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc, + enum snd_ff_stream_mode *mode) { - int i; - - for (i = 0; i < CIP_SFC_COUNT; i++) { - if (amdtp_rate_table[i] == rate) - break; - } - - if (i == CIP_SFC_COUNT) + static const enum snd_ff_stream_mode modes[] = { + [CIP_SFC_32000] = SND_FF_STREAM_MODE_LOW, + [CIP_SFC_44100] = SND_FF_STREAM_MODE_LOW, + [CIP_SFC_48000] = SND_FF_STREAM_MODE_LOW, + [CIP_SFC_88200] = SND_FF_STREAM_MODE_MID, + [CIP_SFC_96000] = SND_FF_STREAM_MODE_MID, + [CIP_SFC_176400] = SND_FF_STREAM_MODE_HIGH, + [CIP_SFC_192000] = SND_FF_STREAM_MODE_HIGH, + }; + + if (sfc >= CIP_SFC_COUNT) return -EINVAL; - *mode = ((int)i - 1) / 2; + *mode = modes[sfc]; return 0; } -/* - * Fireface 400 manages isochronous channel number in 3 bit field. Therefore, - * we can allocate between 0 and 7 channel. - */ -static int keep_resources(struct snd_ff *ff, unsigned int rate) +static void release_resources(struct snd_ff *ff) { - int mode; - int err; - - err = get_rate_mode(rate, &mode); - if (err < 0) - return err; + fw_iso_resources_free(&ff->tx_resources); + fw_iso_resources_free(&ff->rx_resources); +} - /* Keep resources for in-stream. */ - err = amdtp_ff_set_parameters(&ff->tx_stream, rate, - ff->spec->pcm_capture_channels[mode]); - if (err < 0) - return err; - ff->tx_resources.channels_mask = 0x00000000000000ffuLL; - err = fw_iso_resources_allocate(&ff->tx_resources, - amdtp_stream_get_max_payload(&ff->tx_stream), - fw_parent_device(ff->unit)->max_speed); - if (err < 0) - return err; +static int switch_fetching_mode(struct snd_ff *ff, bool enable) +{ + unsigned int count; + __le32 *reg; + int i; + int err; - /* Keep resources for out-stream. */ - err = amdtp_ff_set_parameters(&ff->rx_stream, rate, - ff->spec->pcm_playback_channels[mode]); - if (err < 0) - return err; - ff->rx_resources.channels_mask = 0x00000000000000ffuLL; - err = fw_iso_resources_allocate(&ff->rx_resources, - amdtp_stream_get_max_payload(&ff->rx_stream), - fw_parent_device(ff->unit)->max_speed); - if (err < 0) - fw_iso_resources_free(&ff->tx_resources); + count = 0; + for (i = 0; i < SND_FF_STREAM_MODE_COUNT; ++i) + count = max(count, ff->spec->pcm_playback_channels[i]); + + reg = kcalloc(count, sizeof(__le32), GFP_KERNEL); + if (!reg) + return -ENOMEM; + + if (!enable) { + /* + * Each quadlet is corresponding to data channels in a data + * blocks in reverse order. Precisely, quadlets for available + * data channels should be enabled. Here, I take second best + * to fetch PCM frames from all of data channels regardless of + * stf. + */ + for (i = 0; i < count; ++i) + reg[i] = cpu_to_le32(0x00000001); + } + err = snd_fw_transaction(ff->unit, TCODE_WRITE_BLOCK_REQUEST, + SND_FF_REG_FETCH_PCM_FRAMES, reg, + sizeof(__le32) * count, 0); + kfree(reg); return err; } -static void release_resources(struct snd_ff *ff) -{ - fw_iso_resources_free(&ff->tx_resources); - fw_iso_resources_free(&ff->rx_resources); -} - static inline void finish_session(struct snd_ff *ff) { ff->spec->protocol->finish_session(ff); - ff->spec->protocol->switch_fetching_mode(ff, false); + switch_fetching_mode(ff, false); } static int init_stream(struct snd_ff *ff, enum amdtp_stream_direction dir) @@ -149,7 +147,7 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) if (ff->substreams_counter == 0) return 0; - err = ff->spec->protocol->get_clock(ff, &curr_rate, &src); + err = snd_ff_transaction_get_clock(ff, &curr_rate, &src); if (err < 0) return err; if (curr_rate != rate || @@ -168,9 +166,29 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) * packets. Then, the device transfers packets. */ if (!amdtp_stream_running(&ff->rx_stream)) { - err = keep_resources(ff, rate); + enum snd_ff_stream_mode mode; + int i; + + for (i = 0; i < CIP_SFC_COUNT; ++i) { + if (amdtp_rate_table[i] == rate) + break; + } + if (i >= CIP_SFC_COUNT) + return -EINVAL; + + err = snd_ff_stream_get_multiplier_mode(i, &mode); if (err < 0) - goto error; + return err; + + err = amdtp_ff_set_parameters(&ff->tx_stream, rate, + ff->spec->pcm_capture_channels[mode]); + if (err < 0) + return err; + + err = amdtp_ff_set_parameters(&ff->rx_stream, rate, + ff->spec->pcm_playback_channels[mode]); + if (err < 0) + return err; err = ff->spec->protocol->begin_session(ff, rate); if (err < 0) @@ -188,7 +206,7 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) goto error; } - err = ff->spec->protocol->switch_fetching_mode(ff, true); + err = switch_fetching_mode(ff, true); if (err < 0) goto error; } diff --git a/sound/firewire/fireface/ff-transaction.c b/sound/firewire/fireface/ff-transaction.c index 332b29f8ed75..5f4ddfd55403 100644 --- a/sound/firewire/fireface/ff-transaction.c +++ b/sound/firewire/fireface/ff-transaction.c @@ -8,6 +8,72 @@ #include "ff.h" +#define SND_FF_REG_MIDI_RX_PORT_0 0x000080180000ull +#define SND_FF_REG_MIDI_RX_PORT_1 0x000080190000ull + +int snd_ff_transaction_get_clock(struct snd_ff *ff, unsigned int *rate, + enum snd_ff_clock_src *src) +{ + __le32 reg; + u32 data; + int err; + + err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, + SND_FF_REG_CLOCK_CONFIG, ®, sizeof(reg), 0); + if (err < 0) + return err; + data = le32_to_cpu(reg); + + /* Calculate sampling rate. */ + switch ((data >> 1) & 0x03) { + case 0x01: + *rate = 32000; + break; + case 0x00: + *rate = 44100; + break; + case 0x03: + *rate = 48000; + break; + case 0x02: + default: + return -EIO; + } + + if (data & 0x08) + *rate *= 2; + else if (data & 0x10) + *rate *= 4; + + /* Calculate source of clock. */ + if (data & 0x01) { + *src = SND_FF_CLOCK_SRC_INTERNAL; + } else { + /* TODO: 0x02, 0x06, 0x07? */ + switch ((data >> 10) & 0x07) { + case 0x00: + *src = SND_FF_CLOCK_SRC_ADAT1; + break; + case 0x01: + *src = SND_FF_CLOCK_SRC_ADAT2; + break; + case 0x03: + *src = SND_FF_CLOCK_SRC_SPDIF; + break; + case 0x04: + *src = SND_FF_CLOCK_SRC_WORD; + break; + case 0x05: + *src = SND_FF_CLOCK_SRC_LTC; + break; + default: + return -EIO; + } + } + + return 0; +} + static void finish_transmit_midi_msg(struct snd_ff *ff, unsigned int port, int rcode) { @@ -90,10 +156,10 @@ static void transmit_midi_msg(struct snd_ff *ff, unsigned int port) fill_midi_buf(ff, port, i, buf[i]); if (port == 0) { - addr = ff->spec->protocol->midi_rx_port_0_reg; + addr = SND_FF_REG_MIDI_RX_PORT_0; callback = finish_transmit_midi0_msg; } else { - addr = ff->spec->protocol->midi_rx_port_1_reg; + addr = SND_FF_REG_MIDI_RX_PORT_1; callback = finish_transmit_midi1_msg; } @@ -140,42 +206,10 @@ static void handle_midi_msg(struct fw_card *card, struct fw_request *request, { struct snd_ff *ff = callback_data; __le32 *buf = data; - u32 quad; - u8 byte; - unsigned int index; - struct snd_rawmidi_substream *substream; - int i; fw_send_response(card, request, RCODE_COMPLETE); - for (i = 0; i < length / 4; i++) { - quad = le32_to_cpu(buf[i]); - - /* Message in first port. */ - /* - * This value may represent the index of this unit when the same - * units are on the same IEEE 1394 bus. This driver doesn't use - * it. - */ - index = (quad >> 8) & 0xff; - if (index > 0) { - substream = READ_ONCE(ff->tx_midi_substreams[0]); - if (substream != NULL) { - byte = quad & 0xff; - snd_rawmidi_receive(substream, &byte, 1); - } - } - - /* Message in second port. */ - index = (quad >> 24) & 0xff; - if (index > 0) { - substream = READ_ONCE(ff->tx_midi_substreams[1]); - if (substream != NULL) { - byte = (quad >> 16) & 0xff; - snd_rawmidi_receive(substream, &byte, 1); - } - } - } + ff->spec->protocol->handle_midi_msg(ff, buf, length); } static int allocate_own_address(struct snd_ff *ff, int i) @@ -203,36 +237,33 @@ static int allocate_own_address(struct snd_ff *ff, int i) } /* - * The configuration to start asynchronous transactions for MIDI messages is in - * 0x'0000'8010'051c. This register includes the other options, thus this driver - * doesn't touch it and leaves the decision to userspace. The userspace MUST add - * 0x04000000 to write transactions to the register to receive any MIDI - * messages. - * - * Here, I just describe MIDI-related offsets of the register, in little-endian - * order. - * * Controllers are allowed to register higher 4 bytes of address to receive - * the transactions. The register is 0x'0000'8010'03f4. On the other hand, the - * controllers are not allowed to register lower 4 bytes of the address. They - * are forced to select from 4 options by writing corresponding bits to - * 0x'0000'8010'051c. + * the transactions. Different models have different registers for this purpose; + * e.g. 0x'0000'8010'03f4 for Fireface 400. + * The controllers are not allowed to register lower 4 bytes of the address. + * They are forced to select one of 4 options for the part of address by writing + * corresponding bits to 0x'0000'8010'051f. + * + * The 3rd-6th bits of this register are flags to indicate lower 4 bytes of + * address to which the device transferrs the transactions. In short: + * - 0x20: 0x'....'....'0000'0180 + * - 0x10: 0x'....'....'0000'0100 + * - 0x08: 0x'....'....'0000'0080 + * - 0x04: 0x'....'....'0000'0000 * - * The 3rd-6th bits in MSB of this register are used to indicate lower 4 bytes - * of address to which the device transferrs the transactions. - * - 6th: 0x'....'....'0000'0180 - * - 5th: 0x'....'....'0000'0100 - * - 4th: 0x'....'....'0000'0080 - * - 3rd: 0x'....'....'0000'0000 + * This driver configure 0x'....'....'0000'0000 to receive MIDI messages from + * units. The 3rd bit of the register should be configured, however this driver + * deligates this task to userspace applications due to a restriction that this + * register is write-only and the other bits have own effects. * - * This driver configure 0x'....'....'0000'0000 for units to receive MIDI - * messages. 3rd bit of the register should be configured, however this driver - * deligates this task to user space applications due to a restriction that - * this register is write-only and the other bits have own effects. + * Unlike Fireface 800, Fireface 400 cancels transferring asynchronous + * transactions when the 1st and 2nd of the register stand. These two bits have + * the same effect. + * - 0x02, 0x01: cancel transferring * - * The 1st and 2nd bits in LSB of this register are used to cancel transferring - * asynchronous transactions. These two bits have the same effect. - * - 1st/2nd: cancel transferring + * On the other hand, the bits have no effect on Fireface 800. This model + * cancels asynchronous transactions when the higher 4 bytes of address is + * overwritten with zero. */ int snd_ff_transaction_reregister(struct snd_ff *ff) { @@ -247,7 +278,7 @@ int snd_ff_transaction_reregister(struct snd_ff *ff) addr = (fw_card->node_id << 16) | (ff->async_handler.offset >> 32); reg = cpu_to_le32(addr); return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - ff->spec->protocol->midi_high_addr_reg, + ff->spec->midi_high_addr, ®, sizeof(reg), 0); } @@ -288,7 +319,7 @@ void snd_ff_transaction_unregister(struct snd_ff *ff) /* Release higher 4 bytes of address. */ reg = cpu_to_le32(0x00000000); snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - ff->spec->protocol->midi_high_addr_reg, + ff->spec->midi_high_addr, ®, sizeof(reg), 0); fw_core_remove_address_handler(&ff->async_handler); diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c index 3f61cfeace69..36575f4159d1 100644 --- a/sound/firewire/fireface/ff.c +++ b/sound/firewire/fireface/ff.c @@ -145,6 +145,16 @@ static void snd_ff_remove(struct fw_unit *unit) fw_unit_put(ff->unit); } +static const struct snd_ff_spec spec_ff800 = { + .name = "Fireface800", + .pcm_capture_channels = {28, 20, 12}, + .pcm_playback_channels = {28, 20, 12}, + .midi_in_ports = 1, + .midi_out_ports = 1, + .protocol = &snd_ff_protocol_ff800, + .midi_high_addr = 0x000200000320ull, +}; + static const struct snd_ff_spec spec_ff400 = { .name = "Fireface400", .pcm_capture_channels = {18, 14, 10}, @@ -152,9 +162,22 @@ static const struct snd_ff_spec spec_ff400 = { .midi_in_ports = 2, .midi_out_ports = 2, .protocol = &snd_ff_protocol_ff400, + .midi_high_addr = 0x0000801003f4ull, }; static const struct ieee1394_device_id snd_ff_id_table[] = { + /* Fireface 800 */ + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_RME, + .specifier_id = OUI_RME, + .version = 0x000001, + .model_id = 0x101800, + .driver_data = (kernel_ulong_t)&spec_ff800, + }, /* Fireface 400 */ { .match_flags = IEEE1394_MATCH_VENDOR_ID | @@ -162,7 +185,7 @@ static const struct ieee1394_device_id snd_ff_id_table[] = { IEEE1394_MATCH_VERSION | IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_RME, - .specifier_id = 0x000a35, + .specifier_id = OUI_RME, .version = 0x000002, .model_id = 0x101800, .driver_data = (kernel_ulong_t)&spec_ff400, diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index 64df44beb950..7dfc7745a914 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -31,23 +31,34 @@ #include "../amdtp-stream.h" #include "../iso-resources.h" -#define SND_FF_STREAM_MODES 3 - #define SND_FF_MAXIMIM_MIDI_QUADS 9 #define SND_FF_IN_MIDI_PORTS 2 #define SND_FF_OUT_MIDI_PORTS 2 +#define SND_FF_REG_SYNC_STATUS 0x0000801c0000ull +/* For block write request. */ +#define SND_FF_REG_FETCH_PCM_FRAMES 0x0000801c0000ull +#define SND_FF_REG_CLOCK_CONFIG 0x0000801c0004ull + +enum snd_ff_stream_mode { + SND_FF_STREAM_MODE_LOW = 0, + SND_FF_STREAM_MODE_MID, + SND_FF_STREAM_MODE_HIGH, + SND_FF_STREAM_MODE_COUNT, +}; + struct snd_ff_protocol; struct snd_ff_spec { const char *const name; - const unsigned int pcm_capture_channels[SND_FF_STREAM_MODES]; - const unsigned int pcm_playback_channels[SND_FF_STREAM_MODES]; + const unsigned int pcm_capture_channels[SND_FF_STREAM_MODE_COUNT]; + const unsigned int pcm_playback_channels[SND_FF_STREAM_MODE_COUNT]; unsigned int midi_in_ports; unsigned int midi_out_ports; const struct snd_ff_protocol *protocol; + u64 midi_high_addr; }; struct snd_ff { @@ -89,31 +100,24 @@ struct snd_ff { enum snd_ff_clock_src { SND_FF_CLOCK_SRC_INTERNAL, SND_FF_CLOCK_SRC_SPDIF, - SND_FF_CLOCK_SRC_ADAT, + SND_FF_CLOCK_SRC_ADAT1, + SND_FF_CLOCK_SRC_ADAT2, SND_FF_CLOCK_SRC_WORD, SND_FF_CLOCK_SRC_LTC, - /* TODO: perhaps ADAT2 and TCO exists. */ + /* TODO: perhaps TCO exists. */ }; struct snd_ff_protocol { - int (*get_clock)(struct snd_ff *ff, unsigned int *rate, - enum snd_ff_clock_src *src); + void (*handle_midi_msg)(struct snd_ff *ff, __le32 *buf, size_t length); int (*begin_session)(struct snd_ff *ff, unsigned int rate); void (*finish_session)(struct snd_ff *ff); - int (*switch_fetching_mode)(struct snd_ff *ff, bool enable); - - void (*dump_sync_status)(struct snd_ff *ff, - struct snd_info_buffer *buffer); - void (*dump_clock_config)(struct snd_ff *ff, - struct snd_info_buffer *buffer); - - u64 midi_high_addr_reg; - u64 midi_rx_port_0_reg; - u64 midi_rx_port_1_reg; }; +extern const struct snd_ff_protocol snd_ff_protocol_ff800; extern const struct snd_ff_protocol snd_ff_protocol_ff400; +int snd_ff_transaction_get_clock(struct snd_ff *ff, unsigned int *rate, + enum snd_ff_clock_src *src); int snd_ff_transaction_register(struct snd_ff *ff); int snd_ff_transaction_reregister(struct snd_ff *ff); void snd_ff_transaction_unregister(struct snd_ff *ff); @@ -125,6 +129,8 @@ int amdtp_ff_add_pcm_hw_constraints(struct amdtp_stream *s, int amdtp_ff_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir); +int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc, + enum snd_ff_stream_mode *mode); int snd_ff_stream_init_duplex(struct snd_ff *ff); void snd_ff_stream_destroy_duplex(struct snd_ff *ff); int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate); diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c index afb78d90384b..3d27f3378d5d 100644 --- a/sound/firewire/oxfw/oxfw.c +++ b/sound/firewire/oxfw/oxfw.c @@ -20,6 +20,7 @@ #define VENDOR_LACIE 0x00d04b #define VENDOR_TASCAM 0x00022e #define OUI_STANTON 0x001260 +#define OUI_APOGEE 0x0003db #define MODEL_SATELLITE 0x00200f @@ -397,6 +398,13 @@ static const struct ieee1394_device_id oxfw_id_table[] = { .vendor_id = OUI_STANTON, .model_id = 0x002000, }, + // APOGEE, duet FireWire + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_APOGEE, + .model_id = 0x01dddd, + }, { } }; MODULE_DEVICE_TABLE(ieee1394, oxfw_id_table); diff --git a/sound/firewire/tascam/amdtp-tascam.c b/sound/firewire/tascam/amdtp-tascam.c index ab482423c165..a52d1f76c610 100644 --- a/sound/firewire/tascam/amdtp-tascam.c +++ b/sound/firewire/tascam/amdtp-tascam.c @@ -117,6 +117,55 @@ int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s, return amdtp_stream_add_pcm_hw_constraints(s, runtime); } +static void read_status_messages(struct amdtp_stream *s, + __be32 *buffer, unsigned int data_blocks) +{ + struct snd_tscm *tscm = container_of(s, struct snd_tscm, tx_stream); + bool used = READ_ONCE(tscm->hwdep->used); + int i; + + for (i = 0; i < data_blocks; i++) { + unsigned int index; + __be32 before; + __be32 after; + + index = be32_to_cpu(buffer[0]) % SNDRV_FIREWIRE_TASCAM_STATE_COUNT; + before = tscm->state[index]; + after = buffer[s->data_block_quadlets - 1]; + + if (used && index > 4 && index < 16) { + __be32 mask; + + if (index == 5) + mask = cpu_to_be32(~0x0000ffff); + else if (index == 6) + mask = cpu_to_be32(~0x0000ffff); + else if (index == 8) + mask = cpu_to_be32(~0x000f0f00); + else + mask = cpu_to_be32(~0x00000000); + + if ((before ^ after) & mask) { + struct snd_firewire_tascam_change *entry = + &tscm->queue[tscm->push_pos]; + + spin_lock_irq(&tscm->lock); + entry->index = index; + entry->before = before; + entry->after = after; + if (++tscm->push_pos >= SND_TSCM_QUEUE_COUNT) + tscm->push_pos = 0; + spin_unlock_irq(&tscm->lock); + + wake_up(&tscm->hwdep_wait); + } + } + + tscm->state[index] = after; + buffer += s->data_block_quadlets; + } +} + static unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer, unsigned int data_blocks, @@ -128,7 +177,7 @@ static unsigned int process_tx_data_blocks(struct amdtp_stream *s, if (data_blocks > 0 && pcm) read_pcm_s32(s, pcm, buffer, data_blocks); - /* A place holder for control messages. */ + read_status_messages(s, buffer, data_blocks); return data_blocks; } diff --git a/sound/firewire/tascam/tascam-hwdep.c b/sound/firewire/tascam/tascam-hwdep.c index 4e4c1e9020e8..0414abf5daa8 100644 --- a/sound/firewire/tascam/tascam-hwdep.c +++ b/sound/firewire/tascam/tascam-hwdep.c @@ -16,18 +16,93 @@ #include "tascam.h" +static long tscm_hwdep_read_locked(struct snd_tscm *tscm, char __user *buf, + long count, loff_t *offset) +{ + struct snd_firewire_event_lock_status event = { + .type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS, + }; + + event.status = (tscm->dev_lock_count > 0); + tscm->dev_lock_changed = false; + count = min_t(long, count, sizeof(event)); + + spin_unlock_irq(&tscm->lock); + + if (copy_to_user(buf, &event, count)) + return -EFAULT; + + return count; +} + +static long tscm_hwdep_read_queue(struct snd_tscm *tscm, char __user *buf, + long remained, loff_t *offset) +{ + char __user *pos = buf; + unsigned int type = SNDRV_FIREWIRE_EVENT_TASCAM_CONTROL; + struct snd_firewire_tascam_change *entries = tscm->queue; + long count; + + // At least, one control event can be copied. + if (remained < sizeof(type) + sizeof(*entries)) { + spin_unlock_irq(&tscm->lock); + return -EINVAL; + } + + // Copy the type field later. + count = sizeof(type); + remained -= sizeof(type); + pos += sizeof(type); + + while (true) { + unsigned int head_pos; + unsigned int tail_pos; + unsigned int length; + + if (tscm->pull_pos == tscm->push_pos) + break; + else if (tscm->pull_pos < tscm->push_pos) + tail_pos = tscm->push_pos; + else + tail_pos = SND_TSCM_QUEUE_COUNT; + head_pos = tscm->pull_pos; + + length = (tail_pos - head_pos) * sizeof(*entries); + if (remained < length) + length = rounddown(remained, sizeof(*entries)); + if (length == 0) + break; + + spin_unlock_irq(&tscm->lock); + if (copy_to_user(pos, &entries[head_pos], length)) + return -EFAULT; + + spin_lock_irq(&tscm->lock); + + tscm->pull_pos = tail_pos % SND_TSCM_QUEUE_COUNT; + + count += length; + remained -= length; + pos += length; + } + + spin_unlock_irq(&tscm->lock); + + if (copy_to_user(buf, &type, sizeof(type))) + return -EFAULT; + + return count; +} + static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, loff_t *offset) { struct snd_tscm *tscm = hwdep->private_data; DEFINE_WAIT(wait); - union snd_firewire_event event = { - .lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS, - }; spin_lock_irq(&tscm->lock); - while (!tscm->dev_lock_changed) { + while (!tscm->dev_lock_changed && tscm->push_pos == tscm->pull_pos) { prepare_to_wait(&tscm->hwdep_wait, &wait, TASK_INTERRUPTIBLE); spin_unlock_irq(&tscm->lock); schedule(); @@ -37,15 +112,15 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, spin_lock_irq(&tscm->lock); } - event.lock_status.status = (tscm->dev_lock_count > 0); - tscm->dev_lock_changed = false; - - spin_unlock_irq(&tscm->lock); - - count = min_t(long, count, sizeof(event.lock_status)); - - if (copy_to_user(buf, &event, count)) - return -EFAULT; + // NOTE: The acquired lock should be released in callee side. + if (tscm->dev_lock_changed) { + count = tscm_hwdep_read_locked(tscm, buf, count, offset); + } else if (tscm->push_pos != tscm->pull_pos) { + count = tscm_hwdep_read_queue(tscm, buf, count, offset); + } else { + spin_unlock_irq(&tscm->lock); + count = 0; + } return count; } @@ -59,7 +134,7 @@ static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_wait(file, &tscm->hwdep_wait, wait); spin_lock_irq(&tscm->lock); - if (tscm->dev_lock_changed) + if (tscm->dev_lock_changed || tscm->push_pos != tscm->pull_pos) events = EPOLLIN | EPOLLRDNORM; else events = 0; @@ -123,6 +198,14 @@ static int hwdep_unlock(struct snd_tscm *tscm) return err; } +static int tscm_hwdep_state(struct snd_tscm *tscm, void __user *arg) +{ + if (copy_to_user(arg, tscm->state, sizeof(tscm->state))) + return -EFAULT; + + return 0; +} + static int hwdep_release(struct snd_hwdep *hwdep, struct file *file) { struct snd_tscm *tscm = hwdep->private_data; @@ -147,6 +230,8 @@ static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, return hwdep_lock(tscm); case SNDRV_FIREWIRE_IOCTL_UNLOCK: return hwdep_unlock(tscm); + case SNDRV_FIREWIRE_IOCTL_TASCAM_STATE: + return tscm_hwdep_state(tscm, (void __user *)arg); default: return -ENOIOCTLCMD; } @@ -185,5 +270,7 @@ int snd_tscm_create_hwdep_device(struct snd_tscm *tscm) hwdep->private_data = tscm; hwdep->exclusive = true; + tscm->hwdep = hwdep; + return err; } diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h index a5bd167eb5d9..6a411ee0dcf1 100644 --- a/sound/firewire/tascam/tascam.h +++ b/sound/firewire/tascam/tascam.h @@ -62,6 +62,8 @@ struct snd_fw_async_midi_port { int consume_bytes; }; +#define SND_TSCM_QUEUE_COUNT 16 + struct snd_tscm { struct snd_card *card; struct fw_unit *unit; @@ -89,6 +91,13 @@ struct snd_tscm { /* For MIDI message outgoing transactions. */ struct snd_fw_async_midi_port out_ports[TSCM_MIDI_OUT_PORT_MAX]; + + // A cache of status information in tx isoc packets. + __be32 state[SNDRV_FIREWIRE_TASCAM_STATE_COUNT]; + struct snd_hwdep *hwdep; + struct snd_firewire_tascam_change queue[SND_TSCM_QUEUE_COUNT]; + unsigned int pull_pos; + unsigned int push_pos; }; #define TSCM_ADDR_BASE 0xffff00000000ull diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c index 714a51721a31..012305177f68 100644 --- a/sound/hda/hdac_bus.c +++ b/sound/hda/hdac_bus.c @@ -9,8 +9,6 @@ #include <sound/hdaudio.h> #include "trace.h" -static void process_unsol_events(struct work_struct *work); - static const struct hdac_bus_ops default_ops = { .command = snd_hdac_bus_send_cmd, .get_response = snd_hdac_bus_get_response, @@ -37,7 +35,7 @@ int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev, bus->io_ops = io_ops; INIT_LIST_HEAD(&bus->stream_list); INIT_LIST_HEAD(&bus->codec_list); - INIT_WORK(&bus->unsol_work, process_unsol_events); + INIT_WORK(&bus->unsol_work, snd_hdac_bus_process_unsol_events); spin_lock_init(&bus->reg_lock); mutex_init(&bus->cmd_mutex); bus->irq = -1; @@ -148,7 +146,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_queue_event); /* * process queued unsolicited events */ -static void process_unsol_events(struct work_struct *work) +void snd_hdac_bus_process_unsol_events(struct work_struct *work) { struct hdac_bus *bus = container_of(work, struct hdac_bus, unsol_work); struct hdac_device *codec; @@ -171,6 +169,7 @@ static void process_unsol_events(struct work_struct *work) drv->unsol_event(codec, res); } } +EXPORT_SYMBOL_GPL(snd_hdac_bus_process_unsol_events); /** * snd_hdac_bus_add_device - Add a codec to bus diff --git a/sound/hda/hdac_component.c b/sound/hda/hdac_component.c index 6e46a9c73aed..a6d37b9d6413 100644 --- a/sound/hda/hdac_component.c +++ b/sound/hda/hdac_component.c @@ -54,41 +54,44 @@ EXPORT_SYMBOL_GPL(snd_hdac_set_codec_wakeup); /** * snd_hdac_display_power - Power up / down the power refcount * @bus: HDA core bus + * @idx: HDA codec address, pass HDA_CODEC_IDX_CONTROLLER for controller * @enable: power up or down * - * This function is supposed to be used only by a HD-audio controller - * driver that needs the interaction with graphics driver. + * This function is used by either HD-audio controller or codec driver that + * needs the interaction with graphics driver. * - * This function manages a refcount and calls the get_power() and + * This function updates the power status, and calls the get_power() and * put_power() ops accordingly, toggling the codec wakeup, too. - * - * Returns zero for success or a negative error code. */ -int snd_hdac_display_power(struct hdac_bus *bus, bool enable) +void snd_hdac_display_power(struct hdac_bus *bus, unsigned int idx, bool enable) { struct drm_audio_component *acomp = bus->audio_component; - if (!acomp || !acomp->ops) - return -ENODEV; - dev_dbg(bus->dev, "display power %s\n", enable ? "enable" : "disable"); + if (enable) + set_bit(idx, &bus->display_power_status); + else + clear_bit(idx, &bus->display_power_status); - if (enable) { - if (!bus->drm_power_refcount++) { + if (!acomp || !acomp->ops) + return; + + if (bus->display_power_status) { + if (!bus->display_power_active) { if (acomp->ops->get_power) acomp->ops->get_power(acomp->dev); snd_hdac_set_codec_wakeup(bus, true); snd_hdac_set_codec_wakeup(bus, false); + bus->display_power_active = true; } } else { - WARN_ON(!bus->drm_power_refcount); - if (!--bus->drm_power_refcount) + if (bus->display_power_active) { if (acomp->ops->put_power) acomp->ops->put_power(acomp->dev); + bus->display_power_active = false; + } } - - return 0; } EXPORT_SYMBOL_GPL(snd_hdac_display_power); @@ -321,10 +324,12 @@ int snd_hdac_acomp_exit(struct hdac_bus *bus) if (!acomp) return 0; - WARN_ON(bus->drm_power_refcount); - if (bus->drm_power_refcount > 0 && acomp->ops) + if (WARN_ON(bus->display_power_active) && acomp->ops) acomp->ops->put_power(acomp->dev); + bus->display_power_active = false; + bus->display_power_status = 0; + component_master_del(dev, &hdac_component_master_ops); bus->audio_component = NULL; diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index dbf02a3a8d2f..95b073ee4b32 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -622,23 +622,6 @@ int snd_hdac_power_down_pm(struct hdac_device *codec) EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm); #endif -/** - * snd_hdac_link_power - Enable/disable the link power for a codec - * @codec: the codec object - * @bool: enable or disable the link power - */ -int snd_hdac_link_power(struct hdac_device *codec, bool enable) -{ - if (!codec->link_power_control) - return 0; - - if (codec->bus->ops->link_power) - return codec->bus->ops->link_power(codec->bus, enable); - else - return -EINVAL; -} -EXPORT_SYMBOL_GPL(snd_hdac_link_power); - /* codec vendor labels */ struct hda_vendor_id { unsigned int id; diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index a31fe1550903..aad74e809797 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -1183,7 +1183,7 @@ static int snd_card_asihpi_capture_prepare(struct snd_pcm_substream *substream) static u64 snd_card_asihpi_capture_formats(struct snd_card_asihpi *asihpi, u32 h_stream) { - struct hpi_format hpi_format; + struct hpi_format hpi_format; u16 format; u16 err; u32 h_control; diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index 6ebe817801ea..1f25e6d029d8 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -36,6 +36,7 @@ #include <linux/init.h> #include <linux/mutex.h> #include <linux/moduleparam.h> +#include <linux/nospec.h> #include <sound/core.h> #include <sound/tlv.h> @@ -1026,6 +1027,8 @@ static int snd_emu10k1_ipcm_poke(struct snd_emu10k1 *emu, if (ipcm->substream >= EMU10K1_FX8010_PCM_COUNT) return -EINVAL; + ipcm->substream = array_index_nospec(ipcm->substream, + EMU10K1_FX8010_PCM_COUNT); if (ipcm->channels > 32) return -EINVAL; pcm = &emu->fx8010.pcm[ipcm->substream]; @@ -1072,6 +1075,8 @@ static int snd_emu10k1_ipcm_peek(struct snd_emu10k1 *emu, if (ipcm->substream >= EMU10K1_FX8010_PCM_COUNT) return -EINVAL; + ipcm->substream = array_index_nospec(ipcm->substream, + EMU10K1_FX8010_PCM_COUNT); pcm = &emu->fx8010.pcm[ipcm->substream]; mutex_lock(&emu->fx8010.lock); spin_lock_irq(&emu->reg_lock); diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 4235907b7858..0d38c006e182 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -226,6 +226,68 @@ config SND_HDA_POWER_SAVE_DEFAULT The default time-out value in seconds for HD-audio automatic power-save mode. 0 means to disable the power-save mode. +if SND_HDA_INTEL + +# The options below should not be enabled by distributions or +# users. They are selected by Intel/Skylake or SOF drivers when they +# register for a PCI ID which is also handled by the HDAudio legacy +# driver. When this option is selected and the DSP is detected based on +# the PCI class/subclass/prog-if, the probe of the HDAudio legacy +# aborts. This mechanism removes the need for distributions to use +# blacklists. It can be bypassed with module parameters should the +# Intel/Skylake or SOF drivers fail to handle a specific platform. + +config SND_HDA_INTEL_DSP_DETECTION_SKL + bool + help + This option is selected by SOF or SST drivers, not users or distros. + It enables DSP detection based on PCI class information for + Skylake machines. + +config SND_HDA_INTEL_DSP_DETECTION_APL + bool + help + This option is selected by SOF or SST drivers, not users or distros. + It enables DSP detection based on PCI class information for + Broxton/ApolloLake machines + +config SND_HDA_INTEL_DSP_DETECTION_KBL + bool + help + This option is selected by SOF or SST drivers, not users or distros. + It enables DSP detection based on PCI class information for + KabyLake machines + +config SND_HDA_INTEL_DSP_DETECTION_GLK + bool + help + This option is selected by SOF or SST drivers, not users or distros. + It enables DSP detection based on PCI class information for + GeminiLake machines + +config SND_HDA_INTEL_DSP_DETECTION_CNL + bool + help + This option is selected by SOF or SST drivers, not users or distros. + It enables DSP detection based on PCI class information for + CannonLake machines + +config SND_HDA_INTEL_DSP_DETECTION_CFL + bool + help + This option is selected by SOF or SST drivers, not users or distros. + It enables DSP detection based on PCI class information for + CoffeeLake machines + +config SND_HDA_INTEL_DSP_DETECTION_ICL + bool + help + This option is selected by SOF or SST drivers, not users or distros. + It enables DSP detection based on PCI class information for + IceLake machines + +endif ## SND_HDA_INTEL + endif endmenu diff --git a/sound/pci/hda/dell_wmi_helper.c b/sound/pci/hda/dell_wmi_helper.c deleted file mode 100644 index bbd6c87a4ed6..000000000000 --- a/sound/pci/hda/dell_wmi_helper.c +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Helper functions for Dell Mic Mute LED control; - * to be included from codec driver - */ - -#if IS_ENABLED(CONFIG_DELL_LAPTOP) -#include <linux/dell-led.h> - -static int (*dell_micmute_led_set_func)(int); - -static void dell_micmute_update(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - - dell_micmute_led_set_func(spec->micmute_led.led_value); -} - -static void alc_fixup_dell_wmi(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - bool removefunc = false; - - if (action == HDA_FIXUP_ACT_PROBE) { - if (!dell_micmute_led_set_func) - dell_micmute_led_set_func = symbol_request(dell_micmute_led_set); - if (!dell_micmute_led_set_func) { - codec_warn(codec, "Failed to find dell wmi symbol dell_micmute_led_set\n"); - return; - } - - removefunc = (dell_micmute_led_set_func(false) < 0) || - (snd_hda_gen_add_micmute_led(codec, - dell_micmute_update) < 0); - } - - if (dell_micmute_led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) { - symbol_put(dell_micmute_led_set); - dell_micmute_led_set_func = NULL; - } -} - -#else /* CONFIG_DELL_LAPTOP */ -static void alc_fixup_dell_wmi(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ -} - -#endif /* CONFIG_DELL_LAPTOP */ diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 0957813939e5..9f8d59e7e89f 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -36,6 +36,7 @@ #include "hda_beep.h" #include "hda_jack.h" #include <sound/hda_hwdep.h> +#include <sound/hda_component.h> #define codec_in_pm(codec) snd_hdac_is_in_pm(&codec->core) #define hda_codec_is_power_on(codec) snd_hdac_is_power_on(&codec->core) @@ -799,6 +800,13 @@ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec) static unsigned int hda_set_power_state(struct hda_codec *codec, unsigned int power_state); +/* enable/disable display power per codec */ +static void codec_display_power(struct hda_codec *codec, bool enable) +{ + if (codec->display_power_control) + snd_hdac_display_power(&codec->bus->core, codec->addr, enable); +} + /* also called from hda_bind.c */ void snd_hda_codec_register(struct hda_codec *codec) { @@ -806,7 +814,7 @@ void snd_hda_codec_register(struct hda_codec *codec) return; if (device_is_registered(hda_codec_dev(codec))) { snd_hda_register_beep_device(codec); - snd_hdac_link_power(&codec->core, true); + codec_display_power(codec, true); pm_runtime_enable(hda_codec_dev(codec)); /* it was powered up in snd_hda_codec_new(), now all done */ snd_hda_power_down(codec); @@ -834,7 +842,7 @@ static int snd_hda_codec_dev_free(struct snd_device *device) codec->in_freeing = 1; snd_hdac_device_unregister(&codec->core); - snd_hdac_link_power(&codec->core, false); + codec_display_power(codec, false); put_device(hda_codec_dev(codec)); return 0; } @@ -2926,7 +2934,7 @@ static int hda_codec_runtime_suspend(struct device *dev) (codec_has_clkstop(codec) && codec_has_epss(codec) && (state & AC_PWRST_CLK_STOP_OK))) snd_hdac_codec_link_down(&codec->core); - snd_hdac_link_power(&codec->core, false); + codec_display_power(codec, false); return 0; } @@ -2934,7 +2942,7 @@ static int hda_codec_runtime_resume(struct device *dev) { struct hda_codec *codec = dev_to_hda_codec(dev); - snd_hdac_link_power(&codec->core, true); + codec_display_power(codec, true); snd_hdac_codec_link_up(&codec->core); hda_call_codec_resume(codec); pm_runtime_mark_last_busy(dev); diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index fe2506672a72..532e081f8b8a 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -989,20 +989,9 @@ static int azx_get_response(struct hdac_bus *bus, unsigned int addr, return azx_rirb_get_response(bus, addr, res); } -static int azx_link_power(struct hdac_bus *bus, bool enable) -{ - struct azx *chip = bus_to_azx(bus); - - if (chip->ops->link_power) - return chip->ops->link_power(chip, enable); - else - return -EINVAL; -} - static const struct hdac_bus_ops bus_core_ops = { .command = azx_send_cmd, .get_response = azx_get_response, - .link_power = azx_link_power, }; #ifdef CONFIG_SND_HDA_DSP_LOADER diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h index c95097bb5a0c..e0c3fcbaa028 100644 --- a/sound/pci/hda/hda_controller.h +++ b/sound/pci/hda/hda_controller.h @@ -37,7 +37,7 @@ #else #define AZX_DCAPS_I915_COMPONENT 0 /* NOP */ #endif -/* 14 unused */ +#define AZX_DCAPS_INTEL_SHARED (1 << 14) /* shared with ASoC */ #define AZX_DCAPS_CTX_WORKAROUND (1 << 15) /* X-Fi workaround */ #define AZX_DCAPS_POSFIX_LPIB (1 << 16) /* Use LPIB as default */ /* 17 unused */ @@ -50,11 +50,7 @@ /* 24 unused */ #define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as delay */ #define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */ -#ifdef CONFIG_SND_HDA_I915 -#define AZX_DCAPS_I915_POWERWELL (1 << 27) /* HSW i915 powerwell support */ -#else -#define AZX_DCAPS_I915_POWERWELL 0 /* NOP */ -#endif +/* 27 unused */ #define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28) /* CORBRP clears itself after reset */ #define AZX_DCAPS_NO_MSI64 (1 << 29) /* Stick to 32-bit MSIs */ #define AZX_DCAPS_SEPARATE_STREAM_TAG (1 << 30) /* capture and playback use separate stream tag */ diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 276150f29cda..4095cd7c56c6 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -29,6 +29,7 @@ #include <linux/string.h> #include <linux/bitops.h> #include <linux/module.h> +#include <linux/leds.h> #include <sound/core.h> #include <sound/jack.h> #include <sound/tlv.h> @@ -4035,6 +4036,36 @@ int snd_hda_gen_add_micmute_led(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_gen_add_micmute_led); +#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO) +static void call_ledtrig_micmute(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + + ledtrig_audio_set(LED_AUDIO_MICMUTE, + spec->micmute_led.led_value ? LED_ON : LED_OFF); +} +#endif + +/** + * snd_hda_gen_fixup_micmute_led - A fixup for mic-mute LED trigger + * + * Pass this function to the quirk entry if another driver supports the + * audio mic-mute LED trigger. Then this will bind the mixer capture switch + * change with the LED. + * + * Note that this fixup has to be called after other fixup that sets + * cap_sync_hook. Otherwise the chaining wouldn't work. + */ +void snd_hda_gen_fixup_micmute_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ +#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO) + if (action == HDA_FIXUP_ACT_PROBE) + snd_hda_gen_add_micmute_led(codec, call_ledtrig_micmute); +#endif +} +EXPORT_SYMBOL_GPL(snd_hda_gen_fixup_micmute_led); + /* * parse digital I/Os and set up NIDs in BIOS auto-parse mode */ diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 10123664fa61..78d77042b05a 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -357,5 +357,7 @@ int snd_hda_gen_fix_pin_power(struct hda_codec *codec, hda_nid_t pin); int snd_hda_gen_add_micmute_led(struct hda_codec *codec, void (*hook)(struct hda_codec *)); +void snd_hda_gen_fixup_micmute_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action); #endif /* __SOUND_HDA_GENERIC_H */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 76f03abd15ab..e42cc2230977 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -172,6 +172,9 @@ module_param_array(beep_mode, bool, NULL, 0444); MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode " "(0=off, 1=on) (default=1)."); #endif +static int skl_pci_binding; +module_param_named(pci_binding, skl_pci_binding, int, 0444); +MODULE_PARM_DESC(pci_binding, "PCI binding (0=auto, 1=only legacy, 2=only asoc"); #ifdef CONFIG_PM static int param_set_xint(const char *val, const struct kernel_param *kp); @@ -310,31 +313,28 @@ enum { #define AZX_DCAPS_INTEL_HASWELL \ (/*AZX_DCAPS_ALIGN_BUFSIZE |*/ AZX_DCAPS_COUNT_LPIB_DELAY |\ AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_COMPONENT |\ - AZX_DCAPS_I915_POWERWELL | AZX_DCAPS_SNOOP_TYPE(SCH)) + AZX_DCAPS_SNOOP_TYPE(SCH)) /* Broadwell HDMI can't use position buffer reliably, force to use LPIB */ #define AZX_DCAPS_INTEL_BROADWELL \ (/*AZX_DCAPS_ALIGN_BUFSIZE |*/ AZX_DCAPS_POSFIX_LPIB |\ AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_COMPONENT |\ - AZX_DCAPS_I915_POWERWELL | AZX_DCAPS_SNOOP_TYPE(SCH)) + AZX_DCAPS_SNOOP_TYPE(SCH)) #define AZX_DCAPS_INTEL_BAYTRAIL \ - (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_I915_COMPONENT |\ - AZX_DCAPS_I915_POWERWELL) + (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_I915_COMPONENT) #define AZX_DCAPS_INTEL_BRASWELL \ (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_PM_RUNTIME |\ - AZX_DCAPS_I915_COMPONENT | AZX_DCAPS_I915_POWERWELL) + AZX_DCAPS_I915_COMPONENT) #define AZX_DCAPS_INTEL_SKYLAKE \ (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_PM_RUNTIME |\ - AZX_DCAPS_SEPARATE_STREAM_TAG | AZX_DCAPS_I915_COMPONENT |\ - AZX_DCAPS_I915_POWERWELL) + AZX_DCAPS_SEPARATE_STREAM_TAG | AZX_DCAPS_I915_COMPONENT) #define AZX_DCAPS_INTEL_BROXTON \ (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_PM_RUNTIME |\ - AZX_DCAPS_SEPARATE_STREAM_TAG | AZX_DCAPS_I915_COMPONENT |\ - AZX_DCAPS_I915_POWERWELL) + AZX_DCAPS_SEPARATE_STREAM_TAG | AZX_DCAPS_I915_COMPONENT) /* quirks for ATI SB / AMD Hudson */ #define AZX_DCAPS_PRESET_ATI_SB \ @@ -360,6 +360,7 @@ enum { AZX_DCAPS_NO_64BIT |\ AZX_DCAPS_4K_BDLE_BOUNDARY | AZX_DCAPS_SNOOP_OFF) +#define AZX_DCAPS_INTEL_DSP_DETECTION(conf) (IS_ENABLED(CONFIG_SND_HDA_INTEL_DSP_DETECTION_##conf) ? AZX_DCAPS_INTEL_SHARED : 0) /* * vga_switcheroo support */ @@ -591,8 +592,7 @@ static void hda_intel_init_chip(struct azx *chip, bool full_reset) struct pci_dev *pci = chip->pci; u32 val; - if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) - snd_hdac_set_codec_wakeup(bus, true); + snd_hdac_set_codec_wakeup(bus, true); if (chip->driver_type == AZX_DRIVER_SKL) { pci_read_config_dword(pci, INTEL_HDA_CGCTL, &val); val = val & ~INTEL_HDA_CGCTL_MISCBDCGE; @@ -604,8 +604,8 @@ static void hda_intel_init_chip(struct azx *chip, bool full_reset) val = val | INTEL_HDA_CGCTL_MISCBDCGE; pci_write_config_dword(pci, INTEL_HDA_CGCTL, val); } - if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) - snd_hdac_set_codec_wakeup(bus, false); + + snd_hdac_set_codec_wakeup(bus, false); /* reduce dma latency to avoid noise */ if (IS_BXT(pci)) @@ -667,13 +667,8 @@ static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev) return 0; } -/* Enable/disable i915 display power for the link */ -static int azx_intel_link_power(struct azx *chip, bool enable) -{ - struct hdac_bus *bus = azx_bus(chip); - - return snd_hdac_display_power(bus, enable); -} +#define display_power(chip, enable) \ + snd_hdac_display_power(azx_bus(chip), HDA_CODEC_IDX_CONTROLLER, enable) /* * Check whether the current DMA position is acceptable for updating @@ -930,35 +925,75 @@ static int param_set_xint(const char *val, const struct kernel_param *kp) mutex_unlock(&card_list_lock); return 0; } -#else -#define azx_add_card_list(chip) /* NOP */ -#define azx_del_card_list(chip) /* NOP */ -#endif /* CONFIG_PM */ -#ifdef CONFIG_PM_SLEEP /* * power management */ -static int azx_suspend(struct device *dev) +static bool azx_is_pm_ready(struct snd_card *card) { - struct snd_card *card = dev_get_drvdata(dev); struct azx *chip; struct hda_intel *hda; - struct hdac_bus *bus; if (!card) - return 0; - + return false; chip = card->private_data; hda = container_of(chip, struct hda_intel, chip); if (chip->disabled || hda->init_failed || !chip->running) + return false; + return true; +} + +static void __azx_runtime_suspend(struct azx *chip) +{ + azx_stop_chip(chip); + azx_enter_link_reset(chip); + azx_clear_irq_pending(chip); + display_power(chip, false); +} + +static void __azx_runtime_resume(struct azx *chip) +{ + struct hda_intel *hda = container_of(chip, struct hda_intel, chip); + struct hdac_bus *bus = azx_bus(chip); + struct hda_codec *codec; + int status; + + display_power(chip, true); + if (hda->need_i915_power) + snd_hdac_i915_set_bclk(bus); + + /* Read STATESTS before controller reset */ + status = azx_readw(chip, STATESTS); + + azx_init_pci(chip); + hda_intel_init_chip(chip, true); + + if (status) { + list_for_each_codec(codec, &chip->bus) + if (status & (1 << codec->addr)) + schedule_delayed_work(&codec->jackpoll_work, + codec->jackpoll_interval); + } + + /* power down again for link-controlled chips */ + if (!hda->need_i915_power) + display_power(chip, false); +} + +#ifdef CONFIG_PM_SLEEP +static int azx_suspend(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip; + struct hdac_bus *bus; + + if (!azx_is_pm_ready(card)) return 0; + chip = card->private_data; bus = azx_bus(chip); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - azx_clear_irq_pending(chip); - azx_stop_chip(chip); - azx_enter_link_reset(chip); + __azx_runtime_suspend(chip); if (bus->irq >= 0) { free_irq(bus->irq, chip); bus->irq = -1; @@ -966,9 +1001,6 @@ static int azx_suspend(struct device *dev) if (chip->msi) pci_disable_msi(chip->pci); - if ((chip->driver_caps & AZX_DCAPS_I915_POWERWELL) - && hda->need_i915_power) - snd_hdac_display_power(bus, false); trace_azx_suspend(chip); return 0; @@ -976,41 +1008,19 @@ static int azx_suspend(struct device *dev) static int azx_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct azx *chip; - struct hda_intel *hda; - struct hdac_bus *bus; - if (!card) + if (!azx_is_pm_ready(card)) return 0; chip = card->private_data; - hda = container_of(chip, struct hda_intel, chip); - bus = azx_bus(chip); - if (chip->disabled || hda->init_failed || !chip->running) - return 0; - - if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { - snd_hdac_display_power(bus, true); - if (hda->need_i915_power) - snd_hdac_i915_set_bclk(bus); - } - if (chip->msi) - if (pci_enable_msi(pci) < 0) + if (pci_enable_msi(chip->pci) < 0) chip->msi = 0; if (azx_acquire_irq(chip, 1) < 0) return -EIO; - azx_init_pci(chip); - - hda_intel_init_chip(chip, true); - - /* power down again for link-controlled chips */ - if ((chip->driver_caps & AZX_DCAPS_I915_POWERWELL) && - !hda->need_i915_power) - snd_hdac_display_power(bus, false); - + __azx_runtime_resume(chip); snd_power_change_state(card, SNDRV_CTL_POWER_D0); trace_azx_resume(chip); @@ -1045,21 +1055,14 @@ static int azx_thaw_noirq(struct device *dev) } #endif /* CONFIG_PM_SLEEP */ -#ifdef CONFIG_PM static int azx_runtime_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct azx *chip; - struct hda_intel *hda; - if (!card) + if (!azx_is_pm_ready(card)) return 0; - chip = card->private_data; - hda = container_of(chip, struct hda_intel, chip); - if (chip->disabled || hda->init_failed) - return 0; - if (!azx_has_pm_runtime(chip)) return 0; @@ -1067,13 +1070,7 @@ static int azx_runtime_suspend(struct device *dev) azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) | STATESTS_INT_MASK); - azx_stop_chip(chip); - azx_enter_link_reset(chip); - azx_clear_irq_pending(chip); - if ((chip->driver_caps & AZX_DCAPS_I915_POWERWELL) - && hda->need_i915_power) - snd_hdac_display_power(azx_bus(chip), false); - + __azx_runtime_suspend(chip); trace_azx_runtime_suspend(chip); return 0; } @@ -1082,51 +1079,18 @@ static int azx_runtime_resume(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct azx *chip; - struct hda_intel *hda; - struct hdac_bus *bus; - struct hda_codec *codec; - int status; - if (!card) + if (!azx_is_pm_ready(card)) return 0; - chip = card->private_data; - hda = container_of(chip, struct hda_intel, chip); - bus = azx_bus(chip); - if (chip->disabled || hda->init_failed) - return 0; - if (!azx_has_pm_runtime(chip)) return 0; - - if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { - snd_hdac_display_power(bus, true); - if (hda->need_i915_power) - snd_hdac_i915_set_bclk(bus); - } - - /* Read STATESTS before controller reset */ - status = azx_readw(chip, STATESTS); - - azx_init_pci(chip); - hda_intel_init_chip(chip, true); - - if (status) { - list_for_each_codec(codec, &chip->bus) - if (status & (1 << codec->addr)) - schedule_delayed_work(&codec->jackpoll_work, - codec->jackpoll_interval); - } + __azx_runtime_resume(chip); /* disable controller Wake Up event*/ azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) & ~STATESTS_INT_MASK); - /* power down again for link-controlled chips */ - if ((chip->driver_caps & AZX_DCAPS_I915_POWERWELL) && - !hda->need_i915_power) - snd_hdac_display_power(bus, false); - trace_azx_runtime_resume(chip); return 0; } @@ -1167,6 +1131,8 @@ static const struct dev_pm_ops azx_pm = { #define AZX_PM_OPS &azx_pm #else +#define azx_add_card_list(chip) /* NOP */ +#define azx_del_card_list(chip) /* NOP */ #define AZX_PM_OPS NULL #endif /* CONFIG_PM */ @@ -1374,11 +1340,8 @@ static int azx_free(struct azx *chip) #ifdef CONFIG_SND_HDA_PATCH_LOADER release_firmware(chip->fw); #endif + display_power(chip, false); - if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { - if (hda->need_i915_power) - snd_hdac_display_power(bus, false); - } if (chip->driver_caps & AZX_DCAPS_I915_COMPONENT) snd_hdac_i915_exit(bus); kfree(hda); @@ -1935,8 +1898,7 @@ static int azx_first_init(struct azx *chip) /* initialize chip */ azx_init_pci(chip); - if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) - snd_hdac_i915_set_bclk(bus); + snd_hdac_i915_set_bclk(bus); hda_intel_init_chip(chip, (probe_only[dev] & 2) == 0); @@ -2078,7 +2040,6 @@ static const struct hda_controller_ops pci_hda_ops = { .disable_msi_reset_irq = disable_msi_reset_irq, .pcm_mmap_prepare = pcm_mmap_prepare, .position_check = azx_position_check, - .link_power = azx_intel_link_power, }; static int azx_probe(struct pci_dev *pci, @@ -2091,6 +2052,28 @@ static int azx_probe(struct pci_dev *pci, bool schedule_probe; int err; + /* check if this driver can be used on SKL+ Intel platforms */ + if (pci_id->driver_data & AZX_DCAPS_INTEL_SHARED) { + switch (skl_pci_binding) { + case SND_SKL_PCI_BIND_AUTO: + if (pci->class != 0x040300) { + dev_info(&pci->dev, "The DSP is enabled on this platform, aborting probe\n"); + return -ENODEV; + } + dev_info(&pci->dev, "No DSP detected, continuing HDaudio legacy probe\n"); + break; + case SND_SKL_PCI_BIND_LEGACY: + dev_info(&pci->dev, "Module parameter forced binding with HDaudio legacy, bypassed detection logic\n"); + break; + case SND_SKL_PCI_BIND_ASOC: + dev_info(&pci->dev, "Module parameter forced binding with SKL+ ASoC driver, aborting probe\n"); + return -ENODEV; + default: + dev_err(&pci->dev, "invalid value for skl_pci_binding module parameter, ignored\n"); + break; + } + } + if (dev >= SNDRV_CARDS) return -ENODEV; if (!enable[dev]) { @@ -2245,10 +2228,13 @@ static int azx_probe_continue(struct azx *chip) goto out_free; } else { /* don't bother any longer */ - chip->driver_caps &= - ~(AZX_DCAPS_I915_COMPONENT | AZX_DCAPS_I915_POWERWELL); + chip->driver_caps &= ~AZX_DCAPS_I915_COMPONENT; } } + + /* HSW/BDW controllers need this power */ + if (CONTROLLER_IN_GPU(pci)) + hda->need_i915_power = 1; } /* Request display power well for the HDA controller or codec. For @@ -2256,18 +2242,7 @@ static int azx_probe_continue(struct azx *chip) * this power. For other platforms, like Baytrail/Braswell, only the * display codec needs the power and it can be released after probe. */ - if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { - /* HSW/BDW controllers need this power */ - if (CONTROLLER_IN_GPU(pci)) - hda->need_i915_power = 1; - - err = snd_hdac_display_power(bus, true); - if (err < 0) { - dev_err(chip->card->dev, - "Cannot turn on display power on i915\n"); - goto i915_power_fail; - } - } + display_power(chip, true); err = azx_first_init(chip); if (err < 0) @@ -2315,11 +2290,8 @@ static int azx_probe_continue(struct azx *chip) pm_runtime_put_autosuspend(&pci->dev); out_free: - if ((chip->driver_caps & AZX_DCAPS_I915_POWERWELL) - && !hda->need_i915_power) - snd_hdac_display_power(bus, false); - -i915_power_fail: + if (err < 0 || !hda->need_i915_power) + display_power(chip, false); if (err < 0) hda->init_failed = 1; complete_all(&hda->probe_wait); @@ -2408,34 +2380,48 @@ static const struct pci_device_id azx_ids[] = { .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE }, /* Sunrise Point-LP */ { PCI_DEVICE(0x8086, 0x9d70), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE }, + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE | + AZX_DCAPS_INTEL_DSP_DETECTION(SKL) + }, /* Kabylake */ { PCI_DEVICE(0x8086, 0xa171), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE }, /* Kabylake-LP */ { PCI_DEVICE(0x8086, 0x9d71), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE }, + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE | + AZX_DCAPS_INTEL_DSP_DETECTION(KBL) + }, /* Kabylake-H */ { PCI_DEVICE(0x8086, 0xa2f0), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE }, /* Coffelake */ { PCI_DEVICE(0x8086, 0xa348), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE | + AZX_DCAPS_INTEL_DSP_DETECTION(CFL) + }, /* Cannonlake */ { PCI_DEVICE(0x8086, 0x9dc8), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE | + AZX_DCAPS_INTEL_DSP_DETECTION(CNL) + }, /* Icelake */ { PCI_DEVICE(0x8086, 0x34c8), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE | + AZX_DCAPS_INTEL_DSP_DETECTION(ICL) + }, /* Broxton-P(Apollolake) */ { PCI_DEVICE(0x8086, 0x5a98), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON }, + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON | + AZX_DCAPS_INTEL_DSP_DETECTION(APL) + }, /* Broxton-T */ { PCI_DEVICE(0x8086, 0x1a98), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON }, /* Gemini-Lake */ { PCI_DEVICE(0x8086, 0x3198), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON }, + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON | + AZX_DCAPS_INTEL_DSP_DETECTION(GLK) + }, /* Haswell */ { PCI_DEVICE(0x8086, 0x0a0c), .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL }, diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index c499727920e6..74b46952fc98 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -339,9 +339,15 @@ void snd_hda_jack_report_sync(struct hda_codec *codec) if (jack->nid) { if (!jack->jack || jack->block_report) continue; - state = get_jack_plug_state(jack->pin_sense); - snd_jack_report(jack->jack, - state ? jack->type : 0); + state = jack->button_state; + if (get_jack_plug_state(jack->pin_sense)) + state |= jack->type; + snd_jack_report(jack->jack, state); + if (jack->button_state) { + snd_jack_report(jack->jack, + state & ~jack->button_state); + jack->button_state = 0; /* button released */ + } } } EXPORT_SYMBOL_GPL(snd_hda_jack_report_sync); @@ -379,15 +385,19 @@ static void hda_free_jack_priv(struct snd_jack *jack) * @nid: pin NID to assign * @name: string name for the jack * @phantom_jack: flag to deal as a phantom jack + * @type: jack type bits to be reported, 0 for guessing from pincfg + * @keymap: optional jack / key mapping * * This assigns a jack-detection kctl to the given pin. The kcontrol * will have the given name and index. */ int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, - const char *name, bool phantom_jack) + const char *name, bool phantom_jack, + int type, const struct hda_jack_keymap *keymap) { struct hda_jack_tbl *jack; - int err, state, type; + const struct hda_jack_keymap *map; + int err, state, buttons; jack = snd_hda_jack_tbl_new(codec, nid); if (!jack) @@ -395,16 +405,30 @@ int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, if (jack->jack) return 0; /* already created */ - type = get_input_jack_type(codec, nid); - err = snd_jack_new(codec->card, name, type, + if (!type) + type = get_input_jack_type(codec, nid); + + buttons = 0; + if (keymap) { + for (map = keymap; map->type; map++) + buttons |= map->type; + } + + err = snd_jack_new(codec->card, name, type | buttons, &jack->jack, true, phantom_jack); if (err < 0) return err; jack->phantom_jack = !!phantom_jack; jack->type = type; + jack->button_state = 0; jack->jack->private_data = jack; jack->jack->private_free = hda_free_jack_priv; + if (keymap) { + for (map = keymap; map->type; map++) + snd_jack_set_key(jack->jack, map->type, map->key); + } + state = snd_hda_jack_detect(codec, nid); snd_jack_report(jack->jack, state ? jack->type : 0); @@ -437,7 +461,7 @@ static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, if (phantom_jack) /* Example final name: "Internal Mic Phantom Jack" */ strncat(name, " Phantom", sizeof(name) - strlen(name) - 1); - err = snd_hda_jack_add_kctl(codec, nid, name, phantom_jack); + err = snd_hda_jack_add_kctl(codec, nid, name, phantom_jack, 0, NULL); if (err < 0) return err; @@ -508,19 +532,25 @@ int snd_hda_jack_add_kctls(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_jack_add_kctls); -static void call_jack_callback(struct hda_codec *codec, +static void call_jack_callback(struct hda_codec *codec, unsigned int res, struct hda_jack_tbl *jack) { struct hda_jack_callback *cb; - for (cb = jack->callback; cb; cb = cb->next) + for (cb = jack->callback; cb; cb = cb->next) { + cb->jack = jack; + cb->unsol_res = res; cb->func(codec, cb); + } if (jack->gated_jack) { struct hda_jack_tbl *gated = snd_hda_jack_tbl_get(codec, jack->gated_jack); if (gated) { - for (cb = gated->callback; cb; cb = cb->next) + for (cb = gated->callback; cb; cb = cb->next) { + cb->jack = gated; + cb->unsol_res = res; cb->func(codec, cb); + } } } } @@ -540,7 +570,7 @@ void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res) return; event->jack_dirty = 1; - call_jack_callback(codec, event); + call_jack_callback(codec, res, event); snd_hda_jack_report_sync(codec); } EXPORT_SYMBOL_GPL(snd_hda_jack_unsol_event); @@ -566,7 +596,7 @@ void snd_hda_jack_poll_all(struct hda_codec *codec) if (old_sense == get_jack_plug_state(jack->pin_sense)) continue; changes = 1; - call_jack_callback(codec, jack); + call_jack_callback(codec, 0, jack); } if (changes) snd_hda_jack_report_sync(codec); diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index e9814c0168ea..1d713201c160 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -13,6 +13,7 @@ #define __SOUND_HDA_JACK_H #include <linux/err.h> +#include <sound/jack.h> struct auto_pin_cfg; struct hda_jack_tbl; @@ -24,6 +25,8 @@ struct hda_jack_callback { hda_nid_t nid; hda_jack_callback_fn func; unsigned int private_data; /* arbitrary data */ + unsigned int unsol_res; /* unsolicited event bits */ + struct hda_jack_tbl *jack; /* associated jack entry */ struct hda_jack_callback *next; }; @@ -40,9 +43,15 @@ struct hda_jack_tbl { hda_nid_t gating_jack; /* valid when gating jack plugged */ hda_nid_t gated_jack; /* gated is dependent on this jack */ int type; + int button_state; struct snd_jack *jack; }; +struct hda_jack_keymap { + enum snd_jack_types type; + int key; +}; + struct hda_jack_tbl * snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid); struct hda_jack_tbl * @@ -82,7 +91,8 @@ static inline bool snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid) bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid); int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, - const char *name, bool phantom_jack); + const char *name, bool phantom_jack, + int type, const struct hda_jack_keymap *keymap); int snd_hda_jack_add_kctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg); diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index dd7d4242d6d2..83befd8d43e8 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -31,6 +31,7 @@ #include <linux/of_device.h> #include <linux/slab.h> #include <linux/time.h> +#include <linux/string.h> #include <sound/core.h> #include <sound/initval.h> @@ -344,6 +345,8 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) int err; unsigned short gcap; int irq_id = platform_get_irq(pdev, 0); + const char *sname; + struct device_node *root; err = hda_tegra_init_chip(chip, pdev); if (err) @@ -401,8 +404,23 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) return -ENODEV; } + /* driver name */ strcpy(card->driver, "tegra-hda"); - strcpy(card->shortname, "tegra-hda"); + + root = of_find_node_by_path("/"); + sname = of_get_property(root, "compatible", NULL); + of_node_put(root); + if (!sname) { + dev_err(card->dev, + "failed to get compatible property from root node\n"); + return -ENODEV; + } + /* shortname for card */ + if (strlen(sname) > sizeof(card->shortname)) + dev_info(card->dev, "truncating shortname for card\n"); + strncpy(card->shortname, sname, sizeof(card->shortname)); + + /* longname for card */ snprintf(card->longname, sizeof(card->longname), "%s at 0x%lx irq %i", card->shortname, bus->addr, bus->irq); @@ -513,7 +531,7 @@ static void hda_tegra_probe_work(struct work_struct *work) goto out_free; /* create codec instances */ - err = azx_probe_codecs(chip, 0); + err = azx_probe_codecs(chip, 8); if (err < 0) goto out_free; diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 0a567634e5fa..e5bdbc245682 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -1081,6 +1081,18 @@ enum { QUIRK_AE5, }; +#ifdef CONFIG_PCI +#define ca0132_quirk(spec) ((spec)->quirk) +#define ca0132_use_pci_mmio(spec) ((spec)->use_pci_mmio) +#define ca0132_use_alt_functions(spec) ((spec)->use_alt_functions) +#define ca0132_use_alt_controls(spec) ((spec)->use_alt_controls) +#else +#define ca0132_quirk(spec) ({ (void)(spec); QUIRK_NONE; }) +#define ca0132_use_alt_functions(spec) ({ (void)(spec); false; }) +#define ca0132_use_pci_mmio(spec) ({ (void)(spec); false; }) +#define ca0132_use_alt_controls(spec) ({ (void)(spec); false; }) +#endif + static const struct hda_pintbl alienware_pincfgs[] = { { 0x0b, 0x90170110 }, /* Builtin Speaker */ { 0x0c, 0x411111f0 }, /* N/A */ @@ -3101,7 +3113,7 @@ static void dspload_post_setup(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; codec_dbg(codec, "---- dspload_post_setup ------\n"); - if (!spec->use_alt_functions) { + if (!ca0132_use_alt_functions(spec)) { /*set DSP speaker to 2.0 configuration*/ chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x18), 0x08080080); chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x19), 0x3f800000); @@ -3333,7 +3345,7 @@ static void ca0132_gpio_init(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: case QUIRK_AE5: snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00); @@ -3344,6 +3356,8 @@ static void ca0132_gpio_init(struct hda_codec *codec) snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00); snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x5B); break; + default: + break; } } @@ -3353,7 +3367,7 @@ static void ca0132_gpio_setup(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, 0x07); @@ -3372,6 +3386,8 @@ static void ca0132_gpio_setup(struct hda_codec *codec) snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0x0C); break; + default: + break; } } @@ -4172,7 +4188,7 @@ static void ca0132_alt_select_out_quirk_handler(struct hda_codec *codec) switch (spec->cur_out_type) { case SPEAKER_OUT: - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: ca0113_mmio_gpio_set(codec, 7, false); ca0113_mmio_gpio_set(codec, 4, true); @@ -4203,10 +4219,12 @@ static void ca0132_alt_select_out_quirk_handler(struct hda_codec *codec) chipio_set_control_param(codec, 0x0d, 0xa4); chipio_write(codec, 0x18b03c, 0x00000012); break; + default: + break; } break; case HEADPHONE_OUT: - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: ca0113_mmio_gpio_set(codec, 7, true); ca0113_mmio_gpio_set(codec, 4, true); @@ -4238,10 +4256,12 @@ static void ca0132_alt_select_out_quirk_handler(struct hda_codec *codec) chipio_set_control_param(codec, 0x0d, 0xa1); chipio_write(codec, 0x18b03c, 0x00000012); break; + default: + break; } break; case SURROUND_OUT: - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: ca0113_mmio_gpio_set(codec, 7, false); ca0113_mmio_gpio_set(codec, 4, true); @@ -4272,6 +4292,8 @@ static void ca0132_alt_select_out_quirk_handler(struct hda_codec *codec) chipio_set_control_param(codec, 0x0d, 0xa4); chipio_write(codec, 0x18b03c, 0x00000012); break; + default: + break; } break; } @@ -4446,7 +4468,7 @@ static void ca0132_unsol_hp_delayed(struct work_struct *work) to_delayed_work(work), struct ca0132_spec, unsol_hp_work); struct hda_jack_tbl *jack; - if (spec->use_alt_functions) + if (ca0132_use_alt_functions(spec)) ca0132_alt_select_out(spec->codec); else ca0132_select_out(spec->codec); @@ -4530,14 +4552,14 @@ static int ca0132_alt_set_vipsource(struct hda_codec *codec, int val) chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); - if (spec->quirk == QUIRK_R3DI) + if (ca0132_quirk(spec) == QUIRK_R3DI) chipio_set_conn_rate(codec, 0x0F, SR_96_000); if (spec->in_enum_val == REAR_LINE_IN) tmp = FLOAT_ZERO; else { - if (spec->quirk == QUIRK_SBZ) + if (ca0132_quirk(spec) == QUIRK_SBZ) tmp = FLOAT_THREE; else tmp = FLOAT_ONE; @@ -4549,7 +4571,7 @@ static int ca0132_alt_set_vipsource(struct hda_codec *codec, int val) codec_dbg(codec, "%s: on.", __func__); chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_16_000); chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_16_000); - if (spec->quirk == QUIRK_R3DI) + if (ca0132_quirk(spec) == QUIRK_R3DI) chipio_set_conn_rate(codec, 0x0F, SR_16_000); if (spec->effects_switch[VOICE_FOCUS - EFFECT_START_NID]) @@ -4645,7 +4667,7 @@ static int ca0132_alt_select_in(struct hda_codec *codec) switch (spec->cur_mic_type) { case REAR_MIC: - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: case QUIRK_R3D: ca0113_mmio_gpio_set(codec, 0, false); @@ -4669,14 +4691,14 @@ static int ca0132_alt_select_in(struct hda_codec *codec) chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); - if (spec->quirk == QUIRK_R3DI) + if (ca0132_quirk(spec) == QUIRK_R3DI) chipio_set_conn_rate(codec, 0x0F, SR_96_000); dspio_set_uint_param(codec, 0x80, 0x00, tmp); chipio_set_stream_control(codec, 0x03, 1); chipio_set_stream_control(codec, 0x04, 1); - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: chipio_write(codec, 0x18B098, 0x0000000C); chipio_write(codec, 0x18B09C, 0x0000000C); @@ -4689,12 +4711,14 @@ static int ca0132_alt_select_in(struct hda_codec *codec) chipio_write(codec, 0x18B098, 0x0000000C); chipio_write(codec, 0x18B09C, 0x0000004C); break; + default: + break; } ca0132_alt_mic_boost_set(codec, spec->mic_boost_enum_val); break; case REAR_LINE_IN: ca0132_mic_boost_set(codec, 0); - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: case QUIRK_R3D: ca0113_mmio_gpio_set(codec, 0, false); @@ -4705,28 +4729,32 @@ static int ca0132_alt_select_in(struct hda_codec *codec) case QUIRK_AE5: ca0113_mmio_command_set(codec, 0x48, 0x28, 0x00); break; + default: + break; } chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); - if (spec->quirk == QUIRK_R3DI) + if (ca0132_quirk(spec) == QUIRK_R3DI) chipio_set_conn_rate(codec, 0x0F, SR_96_000); tmp = FLOAT_ZERO; dspio_set_uint_param(codec, 0x80, 0x00, tmp); - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: case QUIRK_AE5: chipio_write(codec, 0x18B098, 0x00000000); chipio_write(codec, 0x18B09C, 0x00000000); break; + default: + break; } chipio_set_stream_control(codec, 0x03, 1); chipio_set_stream_control(codec, 0x04, 1); break; case FRONT_MIC: - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: case QUIRK_R3D: ca0113_mmio_gpio_set(codec, 0, true); @@ -4748,7 +4776,7 @@ static int ca0132_alt_select_in(struct hda_codec *codec) chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); - if (spec->quirk == QUIRK_R3DI) + if (ca0132_quirk(spec) == QUIRK_R3DI) chipio_set_conn_rate(codec, 0x0F, SR_96_000); dspio_set_uint_param(codec, 0x80, 0x00, tmp); @@ -4756,7 +4784,7 @@ static int ca0132_alt_select_in(struct hda_codec *codec) chipio_set_stream_control(codec, 0x03, 1); chipio_set_stream_control(codec, 0x04, 1); - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: chipio_write(codec, 0x18B098, 0x0000000C); chipio_write(codec, 0x18B09C, 0x000000CC); @@ -4765,6 +4793,8 @@ static int ca0132_alt_select_in(struct hda_codec *codec) chipio_write(codec, 0x18B098, 0x0000000C); chipio_write(codec, 0x18B09C, 0x0000004C); break; + default: + break; } ca0132_alt_mic_boost_set(codec, spec->mic_boost_enum_val); break; @@ -4859,7 +4889,7 @@ static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val) val = 0; /* If Voice Focus on SBZ, set to two channel. */ - if ((nid == VOICE_FOCUS) && (spec->use_pci_mmio) + if ((nid == VOICE_FOCUS) && ca0132_use_pci_mmio(spec) && (spec->cur_mic_type != REAR_LINE_IN)) { if (spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID]) { @@ -4878,7 +4908,7 @@ static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val) * For SBZ noise reduction, there's an extra command * to module ID 0x47. No clue why. */ - if ((nid == NOISE_REDUCTION) && (spec->use_pci_mmio) + if ((nid == NOISE_REDUCTION) && ca0132_use_pci_mmio(spec) && (spec->cur_mic_type != REAR_LINE_IN)) { if (spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID]) { @@ -4894,7 +4924,7 @@ static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val) } /* If rear line in disable effects. */ - if (spec->use_alt_functions && + if (ca0132_use_alt_functions(spec) && spec->in_enum_val == REAR_LINE_IN) val = 0; } @@ -4924,7 +4954,7 @@ static int ca0132_pe_switch_set(struct hda_codec *codec) codec_dbg(codec, "ca0132_pe_switch_set: val=%ld\n", spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]); - if (spec->use_alt_functions) + if (ca0132_use_alt_functions(spec)) ca0132_alt_select_out(codec); i = OUT_EFFECT_START_NID - EFFECT_START_NID; @@ -4984,7 +5014,7 @@ static int ca0132_cvoice_switch_set(struct hda_codec *codec) /* set correct vipsource */ oldval = stop_mic1(codec); - if (spec->use_alt_functions) + if (ca0132_use_alt_functions(spec)) ret |= ca0132_alt_set_vipsource(codec, 1); else ret |= ca0132_set_vipsource(codec, 1); @@ -5053,7 +5083,7 @@ static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol, auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID]; if (!auto_jack) { - if (spec->use_alt_functions) + if (ca0132_use_alt_functions(spec)) ca0132_alt_select_out(codec); else ca0132_select_out(codec); @@ -5070,7 +5100,7 @@ static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol, } if (nid == VNID_HP_ASEL) { - if (spec->use_alt_functions) + if (ca0132_use_alt_functions(spec)) ca0132_alt_select_out(codec); else ca0132_select_out(codec); @@ -5784,7 +5814,7 @@ static int ca0132_switch_put(struct snd_kcontrol *kcontrol, /* mic boost */ if (nid == spec->input_pins[0]) { spec->cur_mic_boost = *valp; - if (spec->use_alt_functions) { + if (ca0132_use_alt_functions(spec)) { if (spec->in_enum_val != REAR_LINE_IN) changed = ca0132_mic_boost_set(codec, *valp); } else { @@ -6080,7 +6110,7 @@ static int add_fx_switch(struct hda_codec *codec, hda_nid_t nid, /* If using alt_controls, add FX: prefix. But, don't add FX: * prefix to OutFX or InFX enable controls. */ - if ((spec->use_alt_controls) && (nid <= IN_EFFECT_END_NID)) + if (ca0132_use_alt_controls(spec) && (nid <= IN_EFFECT_END_NID)) sprintf(namestr, "FX: %s %s Switch", pfx, dirstr[dir]); else sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]); @@ -6357,7 +6387,7 @@ static int ca0132_build_controls(struct hda_codec *codec) return err; } /* Setup vmaster with surround slaves for desktop ca0132 devices */ - if (spec->use_alt_functions) { + if (ca0132_use_alt_functions(spec)) { snd_hda_set_vmaster_tlv(codec, spec->dacs[0], HDA_OUTPUT, spec->tlv); snd_hda_add_vmaster(codec, "Master Playback Volume", @@ -6377,7 +6407,7 @@ static int ca0132_build_controls(struct hda_codec *codec) num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT; for (i = 0; i < num_fx; i++) { /* Desktop cards break if Echo Cancellation is used. */ - if (spec->use_pci_mmio) { + if (ca0132_use_pci_mmio(spec)) { if (i == (ECHO_CANCELLATION - IN_EFFECT_START_NID + OUT_EFFECTS_COUNT)) continue; @@ -6394,7 +6424,7 @@ static int ca0132_build_controls(struct hda_codec *codec) * EQ presets, and Smart Volume presets. Also, change names to add FX * prefix, and change PlayEnhancement and CrystalVoice to match. */ - if (spec->use_alt_controls) { + if (ca0132_use_alt_controls(spec)) { err = ca0132_alt_add_svm_enum(codec); if (err < 0) return err; @@ -6448,7 +6478,7 @@ static int ca0132_build_controls(struct hda_codec *codec) * to select the new outputs and inputs, plus add the new mic boost * setting control. */ - if (spec->use_alt_functions) { + if (ca0132_use_alt_functions(spec)) { err = ca0132_alt_add_output_enum(codec); if (err < 0) return err; @@ -6459,14 +6489,14 @@ static int ca0132_build_controls(struct hda_codec *codec) * ZxR only has microphone input, there is no front panel * header on the card, and aux-in is handled by the DBPro board. */ - if (spec->quirk != QUIRK_ZXR) { + if (ca0132_quirk(spec) != QUIRK_ZXR) { err = ca0132_alt_add_input_enum(codec); if (err < 0) return err; } } - if (spec->quirk == QUIRK_AE5) { + if (ca0132_quirk(spec) == QUIRK_AE5) { err = ae5_add_headphone_gain_enum(codec); if (err < 0) return err; @@ -6475,7 +6505,7 @@ static int ca0132_build_controls(struct hda_codec *codec) return err; } - if (spec->quirk == QUIRK_ZXR) { + if (ca0132_quirk(spec) == QUIRK_ZXR) { err = zxr_add_headphone_gain_switch(codec); if (err < 0) return err; @@ -6505,7 +6535,7 @@ static int ca0132_build_controls(struct hda_codec *codec) return err; } - if (spec->use_alt_functions) + if (ca0132_use_alt_functions(spec)) ca0132_alt_add_chmap_ctls(codec); return 0; @@ -6583,7 +6613,7 @@ static int ca0132_build_pcms(struct hda_codec *codec) info = snd_hda_codec_pcm_new(codec, "CA0132 Analog"); if (!info) return -ENOMEM; - if (spec->use_alt_functions) { + if (ca0132_use_alt_functions(spec)) { info->own_chmap = true; info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = ca0132_alt_chmaps; @@ -6597,7 +6627,7 @@ static int ca0132_build_pcms(struct hda_codec *codec) info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0]; /* With the DSP enabled, desktops don't use this ADC. */ - if (!spec->use_alt_functions) { + if (!ca0132_use_alt_functions(spec)) { info = snd_hda_codec_pcm_new(codec, "CA0132 Analog Mic-In2"); if (!info) return -ENOMEM; @@ -6795,7 +6825,7 @@ static void ca0132_init_dmic(struct hda_codec *codec) * Bit 6: set to select Data2, clear for Data1 * Bit 7: set to enable DMic, clear for AMic */ - if (spec->quirk == QUIRK_ALIENWARE_M17XR4) + if (ca0132_quirk(spec) == QUIRK_ALIENWARE_M17XR4) val = 0x33; else val = 0x23; @@ -6877,7 +6907,7 @@ static void ca0132_alt_init_analog_mics(struct hda_codec *codec) /* Mic 1 Setup */ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); - if (spec->quirk == QUIRK_R3DI) { + if (ca0132_quirk(spec) == QUIRK_R3DI) { chipio_set_conn_rate(codec, 0x0F, SR_96_000); tmp = FLOAT_ONE; } else @@ -6887,7 +6917,7 @@ static void ca0132_alt_init_analog_mics(struct hda_codec *codec) /* Mic 2 setup (not present on desktop cards) */ chipio_set_conn_rate(codec, MEM_CONNID_MICIN2, SR_96_000); chipio_set_conn_rate(codec, MEM_CONNID_MICOUT2, SR_96_000); - if (spec->quirk == QUIRK_R3DI) + if (ca0132_quirk(spec) == QUIRK_R3DI) chipio_set_conn_rate(codec, 0x0F, SR_96_000); tmp = FLOAT_ZERO; dspio_set_uint_param(codec, 0x80, 0x01, tmp); @@ -6949,7 +6979,7 @@ static void sbz_chipio_startup_data(struct hda_codec *codec) chipio_set_stream_channels(codec, 0x0C, 6); chipio_set_stream_control(codec, 0x0C, 1); /* No clue what these control */ - if (spec->quirk == QUIRK_SBZ) { + if (ca0132_quirk(spec) == QUIRK_SBZ) { chipio_write_no_mutex(codec, 0x190030, 0x0001e0c0); chipio_write_no_mutex(codec, 0x190034, 0x0001e1c1); chipio_write_no_mutex(codec, 0x190038, 0x0001e4c2); @@ -6962,7 +6992,7 @@ static void sbz_chipio_startup_data(struct hda_codec *codec) chipio_write_no_mutex(codec, 0x190054, 0x0001edc9); chipio_write_no_mutex(codec, 0x190058, 0x0001eaca); chipio_write_no_mutex(codec, 0x19005c, 0x0001ebcb); - } else if (spec->quirk == QUIRK_ZXR) { + } else if (ca0132_quirk(spec) == QUIRK_ZXR) { chipio_write_no_mutex(codec, 0x190038, 0x000140c2); chipio_write_no_mutex(codec, 0x19003c, 0x000141c3); chipio_write_no_mutex(codec, 0x190040, 0x000150c4); @@ -6992,7 +7022,7 @@ static void ca0132_alt_dsp_scp_startup(struct hda_codec *codec) * why this is, but multiple tests have confirmed it. */ for (i = 0; i < 2; i++) { - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: case QUIRK_AE5: tmp = 0x00000003; @@ -7021,6 +7051,8 @@ static void ca0132_alt_dsp_scp_startup(struct hda_codec *codec) tmp = 0x00000000; dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); break; + default: + break; } msleep(100); } @@ -7043,7 +7075,7 @@ static void ca0132_alt_dsp_initial_mic_setup(struct hda_codec *codec) chipio_set_stream_control(codec, 0x03, 1); chipio_set_stream_control(codec, 0x04, 1); - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: chipio_write(codec, 0x18b098, 0x0000000c); chipio_write(codec, 0x18b09C, 0x0000000c); @@ -7052,6 +7084,8 @@ static void ca0132_alt_dsp_initial_mic_setup(struct hda_codec *codec) chipio_write(codec, 0x18b098, 0x0000000c); chipio_write(codec, 0x18b09c, 0x0000004c); break; + default: + break; } } @@ -7273,7 +7307,7 @@ static void r3d_setup_defaults(struct hda_codec *codec) /* Set speaker source? */ dspio_set_uint_param(codec, 0x32, 0x00, tmp); - if (spec->quirk == QUIRK_R3DI) + if (ca0132_quirk(spec) == QUIRK_R3DI) r3di_gpio_dsp_status_set(codec, R3DI_DSP_DOWNLOADED); /* Setup effect defaults */ @@ -7420,7 +7454,7 @@ static void ca0132_init_flags(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; - if (spec->use_alt_functions) { + if (ca0132_use_alt_functions(spec)) { chipio_set_control_flag(codec, CONTROL_FLAG_DSP_96KHZ, 1); chipio_set_control_flag(codec, CONTROL_FLAG_DAC_96KHZ, 1); chipio_set_control_flag(codec, CONTROL_FLAG_ADC_B_96KHZ, 1); @@ -7453,7 +7487,7 @@ static void ca0132_init_params(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; - if (spec->use_alt_functions) { + if (ca0132_use_alt_functions(spec)) { chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000); chipio_set_conn_rate(codec, 0x0B, SR_48_000); chipio_set_control_param(codec, CONTROL_PARAM_SPDIF1_SOURCE, 0); @@ -7490,7 +7524,7 @@ static bool ca0132_download_dsp_images(struct hda_codec *codec) * can use the default firmware, but I'll leave the option in case * it needs it again. */ - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: case QUIRK_R3D: case QUIRK_AE5: @@ -7564,7 +7598,7 @@ static void ca0132_download_dsp(struct hda_codec *codec) } /* For codecs using alt functions, this is already done earlier */ - if (spec->dsp_state == DSP_DOWNLOADED && (!spec->use_alt_functions)) + if (spec->dsp_state == DSP_DOWNLOADED && !ca0132_use_alt_functions(spec)) ca0132_set_dsp_msr(codec, true); } @@ -7601,7 +7635,7 @@ static void amic_callback(struct hda_codec *codec, struct hda_jack_callback *cb) { struct ca0132_spec *spec = codec->spec; - if (spec->use_alt_functions) + if (ca0132_use_alt_functions(spec)) ca0132_alt_select_in(codec); else ca0132_select_mic(codec); @@ -7616,7 +7650,7 @@ static void ca0132_init_unsol(struct hda_codec *codec) snd_hda_jack_detect_enable_callback(codec, UNSOL_TAG_DSP, ca0132_process_dsp_response); /* Front headphone jack detection */ - if (spec->use_alt_functions) + if (ca0132_use_alt_functions(spec)) snd_hda_jack_detect_enable_callback(codec, spec->unsol_tag_front_hp, hp_callback); } @@ -7706,7 +7740,7 @@ static void ca0132_init_chip(struct hda_codec *codec) mutex_init(&spec->chipio_mutex); spec->cur_out_type = SPEAKER_OUT; - if (!spec->use_alt_functions) + if (!ca0132_use_alt_functions(spec)) spec->cur_mic_type = DIGITAL_MIC; else spec->cur_mic_type = REAR_MIC; @@ -7732,7 +7766,7 @@ static void ca0132_init_chip(struct hda_codec *codec) * Sets defaults for the effect slider controls, only for alternative * ca0132 codecs. Also sets x-bass crossover frequency to 80hz. */ - if (spec->use_alt_controls) { + if (ca0132_use_alt_controls(spec)) { spec->xbass_xover_freq = 8; for (i = 0; i < EFFECT_LEVEL_SLIDERS; i++) spec->fx_ctl_val[i] = effect_slider_defaults[i]; @@ -7747,7 +7781,7 @@ static void ca0132_init_chip(struct hda_codec *codec) * the daughter board. So, there is no input enum control, and we need * to make sure that spec->in_enum_val is set properly. */ - if (spec->quirk == QUIRK_ZXR) + if (ca0132_quirk(spec) == QUIRK_ZXR) spec->in_enum_val = REAR_MIC; #ifdef ENABLE_TUNING_CONTROLS @@ -8088,27 +8122,27 @@ static void ca0132_mmio_init(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; - if (spec->quirk == QUIRK_AE5) + if (ca0132_quirk(spec) == QUIRK_AE5) writel(0x00000001, spec->mem_base + 0x400); else writel(0x00000000, spec->mem_base + 0x400); - if (spec->quirk == QUIRK_AE5) + if (ca0132_quirk(spec) == QUIRK_AE5) writel(0x00000001, spec->mem_base + 0x408); else writel(0x00000000, spec->mem_base + 0x408); - if (spec->quirk == QUIRK_AE5) + if (ca0132_quirk(spec) == QUIRK_AE5) writel(0x00000001, spec->mem_base + 0x40c); else writel(0x00000000, spec->mem_base + 0x40C); - if (spec->quirk == QUIRK_ZXR) + if (ca0132_quirk(spec) == QUIRK_ZXR) writel(0x00880640, spec->mem_base + 0x01C); else writel(0x00880680, spec->mem_base + 0x01C); - if (spec->quirk == QUIRK_AE5) + if (ca0132_quirk(spec) == QUIRK_AE5) writel(0x00000080, spec->mem_base + 0xC0C); else writel(0x00000083, spec->mem_base + 0xC0C); @@ -8116,7 +8150,7 @@ static void ca0132_mmio_init(struct hda_codec *codec) writel(0x00000030, spec->mem_base + 0xC00); writel(0x00000000, spec->mem_base + 0xC04); - if (spec->quirk == QUIRK_AE5) + if (ca0132_quirk(spec) == QUIRK_AE5) writel(0x00000000, spec->mem_base + 0xC0C); else writel(0x00000003, spec->mem_base + 0xC0C); @@ -8125,7 +8159,7 @@ static void ca0132_mmio_init(struct hda_codec *codec) writel(0x00000003, spec->mem_base + 0xC0C); writel(0x00000003, spec->mem_base + 0xC0C); - if (spec->quirk == QUIRK_AE5) + if (ca0132_quirk(spec) == QUIRK_AE5) writel(0x00000001, spec->mem_base + 0xC08); else writel(0x000000C1, spec->mem_base + 0xC08); @@ -8136,7 +8170,7 @@ static void ca0132_mmio_init(struct hda_codec *codec) writel(0x000000C1, spec->mem_base + 0xC08); writel(0x00000080, spec->mem_base + 0xC04); - if (spec->quirk == QUIRK_AE5) { + if (ca0132_quirk(spec) == QUIRK_AE5) { writel(0x00000000, spec->mem_base + 0x42c); writel(0x00000000, spec->mem_base + 0x46c); writel(0x00000000, spec->mem_base + 0x4ac); @@ -8211,7 +8245,7 @@ static void ca0132_alt_init(struct hda_codec *codec) ca0132_alt_vol_setup(codec); - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: codec_dbg(codec, "SBZ alt_init"); ca0132_gpio_init(codec); @@ -8248,6 +8282,8 @@ static void ca0132_alt_init(struct hda_codec *codec) snd_hda_sequence_write(codec, spec->chip_init_verbs); snd_hda_sequence_write(codec, spec->desktop_init_verbs); break; + default: + break; } } @@ -8274,7 +8310,7 @@ static int ca0132_init(struct hda_codec *codec) spec->dsp_reload = true; spec->dsp_state = DSP_DOWNLOAD_INIT; } else { - if (spec->quirk == QUIRK_SBZ) + if (ca0132_quirk(spec) == QUIRK_SBZ) sbz_dsp_startup_check(codec); return 0; } @@ -8284,12 +8320,12 @@ static int ca0132_init(struct hda_codec *codec) spec->dsp_state = DSP_DOWNLOAD_INIT; spec->curr_chip_addx = INVALID_CHIP_ADDRESS; - if (spec->use_pci_mmio) + if (ca0132_use_pci_mmio(spec)) ca0132_mmio_init(codec); snd_hda_power_up_pm(codec); - if (spec->quirk == QUIRK_AE5) + if (ca0132_quirk(spec) == QUIRK_AE5) ae5_register_set(codec); ca0132_init_unsol(codec); @@ -8298,14 +8334,14 @@ static int ca0132_init(struct hda_codec *codec) snd_hda_sequence_write(codec, spec->base_init_verbs); - if (spec->use_alt_functions) + if (ca0132_use_alt_functions(spec)) ca0132_alt_init(codec); ca0132_download_dsp(codec); ca0132_refresh_widget_caps(codec); - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_R3DI: case QUIRK_R3D: r3d_setup_defaults(codec); @@ -8334,7 +8370,7 @@ static int ca0132_init(struct hda_codec *codec) init_input(codec, cfg->dig_in_pin, spec->dig_in); - if (!spec->use_alt_functions) { + if (!ca0132_use_alt_functions(spec)) { snd_hda_sequence_write(codec, spec->chip_init_verbs); snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, VENDOR_CHIPIO_PARAM_EX_ID_SET, 0x0D); @@ -8342,11 +8378,11 @@ static int ca0132_init(struct hda_codec *codec) VENDOR_CHIPIO_PARAM_EX_VALUE_SET, 0x20); } - if (spec->quirk == QUIRK_SBZ) + if (ca0132_quirk(spec) == QUIRK_SBZ) ca0132_gpio_setup(codec); snd_hda_sequence_write(codec, spec->spec_init_verbs); - if (spec->use_alt_functions) { + if (ca0132_use_alt_functions(spec)) { ca0132_alt_select_out(codec); ca0132_alt_select_in(codec); } else { @@ -8391,7 +8427,7 @@ static void ca0132_free(struct hda_codec *codec) cancel_delayed_work_sync(&spec->unsol_hp_work); snd_hda_power_up(codec); - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: sbz_exit_chip(codec); break; @@ -8407,13 +8443,15 @@ static void ca0132_free(struct hda_codec *codec) case QUIRK_R3DI: r3di_gpio_shutdown(codec); break; + default: + break; } snd_hda_sequence_write(codec, spec->base_exit_verbs); ca0132_exit_chip(codec); snd_hda_power_down(codec); - if (spec->mem_base) + if (IS_ENABLED(CONFIG_PCI) && spec->mem_base) pci_iounmap(codec->bus->pci, spec->mem_base); kfree(spec->spec_init_verbs); kfree(codec->spec); @@ -8461,12 +8499,12 @@ static void ca0132_config(struct hda_codec *codec) spec->multiout.dac_nids = spec->dacs; spec->multiout.num_dacs = 3; - if (!spec->use_alt_functions) + if (!ca0132_use_alt_functions(spec)) spec->multiout.max_channels = 2; else spec->multiout.max_channels = 6; - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_ALIENWARE: codec_dbg(codec, "%s: QUIRK_ALIENWARE applied.\n", __func__); snd_hda_apply_pincfgs(codec, alienware_pincfgs); @@ -8491,9 +8529,11 @@ static void ca0132_config(struct hda_codec *codec) codec_dbg(codec, "%s: QUIRK_AE5 applied.\n", __func__); snd_hda_apply_pincfgs(codec, ae5_pincfgs); break; + default: + break; } - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_ALIENWARE: spec->num_outputs = 2; spec->out_pins[0] = 0x0b; /* speaker out */ @@ -8654,7 +8694,7 @@ static int ca0132_prepare_verbs(struct hda_codec *codec) * Since desktop cards use pci_mmio, this can be used to determine * whether or not to use these verbs instead of a separate bool. */ - if (spec->use_pci_mmio) + if (ca0132_use_pci_mmio(spec)) spec->desktop_init_verbs = ca0132_init_verbs1; spec->spec_init_verbs = kcalloc(NUM_SPEC_VERBS, sizeof(struct hda_verb), @@ -8729,11 +8769,10 @@ static int patch_ca0132(struct hda_codec *codec) spec->quirk = quirk->value; else spec->quirk = QUIRK_NONE; - - if (spec->quirk == QUIRK_SBZ) + if (ca0132_quirk(spec) == QUIRK_SBZ) sbz_detect_quirk(codec); - if (spec->quirk == QUIRK_ZXR_DBPRO) + if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO) codec->patch_ops = dbpro_patch_ops; else codec->patch_ops = ca0132_patch_ops; @@ -8746,7 +8785,7 @@ static int patch_ca0132(struct hda_codec *codec) spec->num_mixers = 1; /* Set which mixers each quirk uses. */ - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: spec->mixers[0] = desktop_mixer; snd_hda_codec_set_name(codec, "Sound Blaster Z"); @@ -8775,7 +8814,7 @@ static int patch_ca0132(struct hda_codec *codec) } /* Setup whether or not to use alt functions/controls/pci_mmio */ - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: case QUIRK_R3D: case QUIRK_AE5: @@ -8796,6 +8835,7 @@ static int patch_ca0132(struct hda_codec *codec) break; } +#ifdef CONFIG_PCI if (spec->use_pci_mmio) { spec->mem_base = pci_iomap(codec->bus->pci, 2, 0xC20); if (spec->mem_base == NULL) { @@ -8803,6 +8843,7 @@ static int patch_ca0132(struct hda_codec *codec) spec->quirk = QUIRK_NONE; } } +#endif spec->base_init_verbs = ca0132_base_init_verbs; spec->base_exit_verbs = ca0132_base_exit_verbs; diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 950e02e71766..51cc6589443f 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -923,6 +923,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x103c, 0x8079, "HP EliteBook 840 G3", CXT_FIXUP_HP_DOCK), SND_PCI_QUIRK(0x103c, 0x807C, "HP EliteBook 820 G3", CXT_FIXUP_HP_DOCK), SND_PCI_QUIRK(0x103c, 0x80FD, "HP ProBook 640 G2", CXT_FIXUP_HP_DOCK), + SND_PCI_QUIRK(0x103c, 0x828c, "HP EliteBook 840 G4", CXT_FIXUP_HP_DOCK), SND_PCI_QUIRK(0x103c, 0x83b3, "HP EliteBook 830 G5", CXT_FIXUP_HP_DOCK), SND_PCI_QUIRK(0x103c, 0x83d3, "HP ProBook 640 G4", CXT_FIXUP_HP_DOCK), SND_PCI_QUIRK(0x103c, 0x8174, "HP Spectre x360", CXT_FIXUP_HP_SPECTRE), diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 67099cbb6be2..46f88dc7b7e8 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2142,7 +2142,7 @@ static int generic_hdmi_build_jack(struct hda_codec *codec, int pcm_idx) strncat(hdmi_str, " Phantom", sizeof(hdmi_str) - strlen(hdmi_str) - 1); ret = snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str, - phantom_jack); + phantom_jack, 0, NULL); if (ret < 0) return ret; jack = snd_hda_jack_tbl_get(codec, per_pin->pin_nid); @@ -2616,11 +2616,7 @@ static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid) intel_haswell_enable_all_pins(codec, true); intel_haswell_fixup_enable_dp12(codec); - /* For Haswell/Broadwell, the controller is also in the power well and - * can cover the codec power request, and so need not set this flag. - */ - if (!is_haswell(codec) && !is_broadwell(codec)) - codec->core.link_power_control = 1; + codec->display_power_control = 1; codec->patch_ops.set_power_state = haswell_set_power_state; codec->depop_delay = 0; @@ -2656,7 +2652,7 @@ static int patch_i915_byt_hdmi(struct hda_codec *codec) /* For Valleyview/Cherryview, only the display codec is in the display * power well and can use link_power ops to request/release the power. */ - codec->core.link_power_control = 1; + codec->display_power_control = 1; codec->depop_delay = 0; codec->auto_runtime_pm = 1; @@ -3834,6 +3830,10 @@ HDA_CODEC_ENTRY(0x10de0020, "Tegra30 HDMI", patch_tegra_hdmi), HDA_CODEC_ENTRY(0x10de0022, "Tegra114 HDMI", patch_tegra_hdmi), HDA_CODEC_ENTRY(0x10de0028, "Tegra124 HDMI", patch_tegra_hdmi), HDA_CODEC_ENTRY(0x10de0029, "Tegra210 HDMI/DP", patch_tegra_hdmi), +HDA_CODEC_ENTRY(0x10de002d, "Tegra186 HDMI/DP0", patch_tegra_hdmi), +HDA_CODEC_ENTRY(0x10de002e, "Tegra186 HDMI/DP1", patch_tegra_hdmi), +HDA_CODEC_ENTRY(0x10de002f, "Tegra194 HDMI/DP2", patch_tegra_hdmi), +HDA_CODEC_ENTRY(0x10de0030, "Tegra194 HDMI/DP3", patch_tegra_hdmi), HDA_CODEC_ENTRY(0x10de0040, "GPU 40 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de0041, "GPU 41 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de0042, "GPU 42 HDMI/DP", patch_nvhdmi), diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8d75597028ee..a4f4a9dd488d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5380,6 +5380,66 @@ static void alc285_fixup_invalidate_dacs(struct hda_codec *codec, snd_hda_override_wcaps(codec, 0x03, 0); } +static const struct hda_jack_keymap alc_headset_btn_keymap[] = { + { SND_JACK_BTN_0, KEY_PLAYPAUSE }, + { SND_JACK_BTN_1, KEY_VOICECOMMAND }, + { SND_JACK_BTN_2, KEY_VOLUMEUP }, + { SND_JACK_BTN_3, KEY_VOLUMEDOWN }, + {} +}; + +static void alc_headset_btn_callback(struct hda_codec *codec, + struct hda_jack_callback *jack) +{ + int report = 0; + + if (jack->unsol_res & (7 << 13)) + report |= SND_JACK_BTN_0; + + if (jack->unsol_res & (1 << 16 | 3 << 8)) + report |= SND_JACK_BTN_1; + + /* Volume up key */ + if (jack->unsol_res & (7 << 23)) + report |= SND_JACK_BTN_2; + + /* Volume down key */ + if (jack->unsol_res & (7 << 10)) + report |= SND_JACK_BTN_3; + + jack->jack->button_state = report; +} + +static void alc_fixup_headset_jack(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_jack_detect_enable_callback(codec, 0x55, + alc_headset_btn_callback); + snd_hda_jack_add_kctl(codec, 0x55, "Headset Jack", false, + SND_JACK_HEADSET, alc_headset_btn_keymap); + break; + case HDA_FIXUP_ACT_INIT: + switch (codec->core.vendor_id) { + case 0x10ec0225: + case 0x10ec0295: + case 0x10ec0299: + alc_write_coef_idx(codec, 0x48, 0xd011); + alc_update_coef_idx(codec, 0x49, 0x007f, 0x0045); + alc_update_coef_idx(codec, 0x44, 0x007f << 8, 0x0045 << 8); + break; + case 0x10ec0236: + case 0x10ec0256: + alc_write_coef_idx(codec, 0x48, 0xd011); + alc_update_coef_idx(codec, 0x49, 0x007f, 0x0045); + break; + } + break; + } +} + /* for hda_fixup_thinkpad_acpi() */ #include "thinkpad_helper.c" @@ -5390,9 +5450,6 @@ static void alc_fixup_thinkpad_acpi(struct hda_codec *codec, hda_fixup_thinkpad_acpi(codec, fix, action); } -/* for dell wmi mic mute led */ -#include "dell_wmi_helper.c" - /* for alc295_fixup_hp_top_speakers */ #include "hp_x360_helper.c" @@ -5470,7 +5527,7 @@ enum { ALC292_FIXUP_TPT440_DOCK, ALC292_FIXUP_TPT440, ALC283_FIXUP_HEADSET_MIC, - ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED, + ALC255_FIXUP_MIC_MUTE_LED, ALC282_FIXUP_ASPIRE_V5_PINS, ALC280_FIXUP_HP_GPIO4, ALC286_FIXUP_HP_GPIO_LED, @@ -5515,11 +5572,16 @@ enum { ALC298_FIXUP_TPT470_DOCK, ALC255_FIXUP_DUMMY_LINEOUT_VERB, ALC255_FIXUP_DELL_HEADSET_MIC, + ALC256_FIXUP_HUAWEI_MBXP_PINS, ALC295_FIXUP_HP_X360, ALC221_FIXUP_HP_HEADSET_MIC, ALC285_FIXUP_LENOVO_HEADPHONE_NOISE, ALC295_FIXUP_HP_AUTO_MUTE, ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE, + ALC294_FIXUP_ASUS_MIC, + ALC294_FIXUP_ASUS_HEADSET_MIC, + ALC294_FIXUP_ASUS_SPK, + ALC225_FIXUP_HEADSET_JACK, }; static const struct hda_fixup alc269_fixups[] = { @@ -5767,7 +5829,7 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_headset_mode, .chained = true, - .chain_id = ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED + .chain_id = ALC255_FIXUP_MIC_MUTE_LED }, [ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC] = { .type = HDA_FIXUP_FUNC, @@ -5791,6 +5853,24 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_HEADSET_MIC }, + [ALC256_FIXUP_HUAWEI_MBXP_PINS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + {0x12, 0x90a60130}, + {0x13, 0x40000000}, + {0x14, 0x90170110}, + {0x18, 0x411111f0}, + {0x19, 0x04a11040}, + {0x1a, 0x411111f0}, + {0x1b, 0x90170112}, + {0x1d, 0x40759a05}, + {0x1e, 0x411111f0}, + {0x21, 0x04211020}, + { } + }, + .chained = true, + .chain_id = ALC255_FIXUP_MIC_MUTE_LED + }, [ALC269_FIXUP_ASUS_X101_FUNC] = { .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_x101_headset_mic, @@ -5993,7 +6073,7 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_headset_mode_alc255, .chained = true, - .chain_id = ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED + .chain_id = ALC255_FIXUP_MIC_MUTE_LED }, [ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC] = { .type = HDA_FIXUP_FUNC, @@ -6028,9 +6108,9 @@ static const struct hda_fixup alc269_fixups[] = { { }, }, }, - [ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED] = { + [ALC255_FIXUP_MIC_MUTE_LED] = { .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_dell_wmi, + .v.func = snd_hda_gen_fixup_micmute_led, }, [ALC282_FIXUP_ASPIRE_V5_PINS] = { .type = HDA_FIXUP_PINS, @@ -6089,7 +6169,7 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_headset_mode_dell_alc288, .chained = true, - .chain_id = ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED + .chain_id = ALC255_FIXUP_MIC_MUTE_LED }, [ALC288_FIXUP_DELL1_MIC_NO_PRESENCE] = { .type = HDA_FIXUP_PINS, @@ -6392,6 +6472,8 @@ static const struct hda_fixup alc269_fixups[] = { [ALC285_FIXUP_LENOVO_HEADPHONE_NOISE] = { .type = HDA_FIXUP_FUNC, .v.func = alc285_fixup_invalidate_dacs, + .chained = true, + .chain_id = ALC269_FIXUP_THINKPAD_ACPI }, [ALC295_FIXUP_HP_AUTO_MUTE] = { .type = HDA_FIXUP_FUNC, @@ -6406,6 +6488,40 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_HEADSET_MIC }, + [ALC294_FIXUP_ASUS_MIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x13, 0x90a60160 }, /* use as internal mic */ + { 0x19, 0x04a11120 }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC + }, + [ALC294_FIXUP_ASUS_HEADSET_MIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x01a1113c }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC + }, + [ALC294_FIXUP_ASUS_SPK] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* Set EAPD high */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x40 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x8800 }, + { } + }, + .chained = true, + .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC + }, + [ALC225_FIXUP_HEADSET_JACK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_jack, + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -6538,6 +6654,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x1043, 0x10a1, "ASUS UX391UA", ALC294_FIXUP_ASUS_SPK), SND_PCI_QUIRK(0x1043, 0x10c0, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x10d0, "ASUS X540LA/X540LJ", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), @@ -6548,6 +6665,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x12e0, "ASUS X541SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x13b0, "ASUS Z550SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_ASUS_ZENBOOK), + SND_PCI_QUIRK(0x1043, 0x14a1, "ASUS UX533FD", ALC294_FIXUP_ASUS_SPK), SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A), SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), @@ -6644,6 +6762,9 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x511f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K), SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD), + SND_PCI_QUIRK(0x19e5, 0x3200, "Huawei MBX", ALC255_FIXUP_MIC_MUTE_LED), + SND_PCI_QUIRK(0x19e5, 0x3201, "Huawei MBX", ALC255_FIXUP_MIC_MUTE_LED), + SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MBXP", ALC256_FIXUP_HUAWEI_MBXP_PINS), SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */ #if 0 @@ -6769,7 +6890,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC255_FIXUP_DELL2_MIC_NO_PRESENCE, .name = "alc255-dell2"}, {.id = ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc293-dell1"}, {.id = ALC283_FIXUP_HEADSET_MIC, .name = "alc283-headset"}, - {.id = ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED, .name = "alc255-dell-mute"}, + {.id = ALC255_FIXUP_MIC_MUTE_LED, .name = "alc255-dell-mute"}, {.id = ALC282_FIXUP_ASPIRE_V5_PINS, .name = "aspire-v5"}, {.id = ALC280_FIXUP_HP_GPIO4, .name = "hp-gpio4"}, {.id = ALC286_FIXUP_HP_GPIO_LED, .name = "hp-gpio-led"}, @@ -6808,6 +6929,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC255_FIXUP_DUMMY_LINEOUT_VERB, .name = "alc255-dummy-lineout"}, {.id = ALC255_FIXUP_DELL_HEADSET_MIC, .name = "alc255-dell-headset"}, {.id = ALC295_FIXUP_HP_X360, .name = "alc295-hp-x360"}, + {.id = ALC225_FIXUP_HEADSET_JACK, .name = "alc-sense-combo"}, {} }; #define ALC225_STANDARD_PINS \ @@ -7155,6 +7277,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(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_MIC, + {0x14, 0x90170110}, + {0x1b, 0x90a70130}, + {0x21, 0x04211020}), + SND_HDA_PIN_QUIRK(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_SPK, + {0x12, 0x90a60130}, + {0x17, 0x90170110}, + {0x21, 0x04211020}), SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, ALC295_STANDARD_PINS, {0x17, 0x21014020}, @@ -7227,6 +7357,37 @@ static void alc269_fill_coef(struct hda_codec *codec) alc_update_coef_idx(codec, 0x4, 0, 1<<11); } +static void alc294_hp_init(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; + int i, val; + + if (!hp_pin) + return; + + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + + msleep(100); + + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + + alc_update_coef_idx(codec, 0x6f, 0x000f, 0);/* Set HP depop to manual mode */ + alc_update_coefex_idx(codec, 0x58, 0x00, 0x8000, 0x8000); /* HP depop procedure start */ + + /* Wait for depop procedure finish */ + val = alc_read_coefex_idx(codec, 0x58, 0x01); + for (i = 0; i < 20 && val & 0x0080; i++) { + msleep(50); + val = alc_read_coefex_idx(codec, 0x58, 0x01); + } + /* Set HP depop to auto mode */ + alc_update_coef_idx(codec, 0x6f, 0x000f, 0x000b); + msleep(50); +} + /* */ static int patch_alc269(struct hda_codec *codec) @@ -7352,6 +7513,7 @@ static int patch_alc269(struct hda_codec *codec) spec->codec_variant = ALC269_TYPE_ALC294; spec->gen.mixer_nid = 0; /* ALC2x4 does not have any loopback mixer path */ alc_update_coef_idx(codec, 0x6b, 0x0018, (1<<4) | (1<<3)); /* UAJ MIC Vref control by verb */ + alc294_hp_init(codec); break; case 0x10ec0300: spec->codec_variant = ALC269_TYPE_ALC300; @@ -7363,6 +7525,7 @@ static int patch_alc269(struct hda_codec *codec) spec->codec_variant = ALC269_TYPE_ALC700; spec->gen.mixer_nid = 0; /* ALC700 does not have any loopback mixer path */ alc_update_coef_idx(codec, 0x4a, 1 << 15, 0); /* Combo jack auto trigger control */ + alc294_hp_init(codec); break; } diff --git a/sound/pci/hda/thinkpad_helper.c b/sound/pci/hda/thinkpad_helper.c index 568575b72f2f..4089feb8c68e 100644 --- a/sound/pci/hda/thinkpad_helper.c +++ b/sound/pci/hda/thinkpad_helper.c @@ -3,12 +3,11 @@ * to be included from codec driver */ -#if IS_ENABLED(CONFIG_THINKPAD_ACPI) +#if IS_ENABLED(CONFIG_THINKPAD_ACPI) && IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO) #include <linux/acpi.h> -#include <linux/thinkpad_acpi.h> +#include <linux/leds.h> -static int (*led_set_func)(int, bool); static void (*old_vmaster_hook)(void *, int); static bool is_thinkpad(struct hda_codec *codec) @@ -23,50 +22,20 @@ static void update_tpacpi_mute_led(void *private_data, int enabled) if (old_vmaster_hook) old_vmaster_hook(private_data, enabled); - if (led_set_func) - led_set_func(TPACPI_LED_MUTE, !enabled); -} - -static void update_tpacpi_micmute(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - - led_set_func(TPACPI_LED_MICMUTE, spec->micmute_led.led_value); + ledtrig_audio_set(LED_AUDIO_MUTE, enabled ? LED_OFF : LED_ON); } static void hda_fixup_thinkpad_acpi(struct hda_codec *codec, const struct hda_fixup *fix, int action) { struct hda_gen_spec *spec = codec->spec; - bool removefunc = false; if (action == HDA_FIXUP_ACT_PROBE) { if (!is_thinkpad(codec)) return; - if (!led_set_func) - led_set_func = symbol_request(tpacpi_led_set); - if (!led_set_func) { - codec_warn(codec, - "Failed to find thinkpad-acpi symbol tpacpi_led_set\n"); - return; - } - - removefunc = true; - if (led_set_func(TPACPI_LED_MUTE, false) >= 0) { - old_vmaster_hook = spec->vmaster_mute.hook; - spec->vmaster_mute.hook = update_tpacpi_mute_led; - removefunc = false; - } - if (led_set_func(TPACPI_LED_MICMUTE, false) >= 0 && - !snd_hda_gen_add_micmute_led(codec, - update_tpacpi_micmute)) - removefunc = false; - } - - if (led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) { - symbol_put(tpacpi_led_set); - led_set_func = NULL; - old_vmaster_hook = NULL; + old_vmaster_hook = spec->vmaster_mute.hook; + spec->vmaster_mute.hook = update_tpacpi_mute_led; + snd_hda_gen_fixup_micmute_led(codec, fix, action); } } diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 1bff4b1b39cd..ba99ff0e93e0 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -30,6 +30,7 @@ #include <linux/math64.h> #include <linux/vmalloc.h> #include <linux/io.h> +#include <linux/nospec.h> #include <sound/core.h> #include <sound/control.h> @@ -4092,15 +4093,16 @@ static int snd_hdsp_channel_info(struct snd_pcm_substream *substream, struct snd_pcm_channel_info *info) { struct hdsp *hdsp = snd_pcm_substream_chip(substream); - int mapped_channel; + unsigned int channel = info->channel; - if (snd_BUG_ON(info->channel >= hdsp->max_channels)) + if (snd_BUG_ON(channel >= hdsp->max_channels)) return -EINVAL; + channel = array_index_nospec(channel, hdsp->max_channels); - if ((mapped_channel = hdsp->channel_map[info->channel]) < 0) + if (hdsp->channel_map[channel] < 0) return -EINVAL; - info->offset = mapped_channel * HDSP_CHANNEL_BUFFER_BYTES; + info->offset = hdsp->channel_map[channel] * HDSP_CHANNEL_BUFFER_BYTES; info->first = 0; info->step = 32; return 0; diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index 48dd44f8e914..d692e4070167 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -908,7 +908,7 @@ static void detect_byte_swap(struct snd_pmac *chip) /* if seems that Keylargo can't byte-swap */ for (mio = chip->node->parent; mio; mio = mio->parent) { - if (strcmp(mio->name, "mac-io") == 0) { + if (of_node_name_eq(mio, "mac-io")) { if (of_device_is_compatible(mio, "Keylargo")) chip->can_byte_swap = 0; break; @@ -1313,7 +1313,7 @@ int snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return) } else if (chip->is_pbook_G3) { struct device_node* mio; for (mio = chip->node->parent; mio; mio = mio->parent) { - if (strcmp(mio->name, "mac-io") == 0) { + if (of_node_name_eq(mio, "mac-io")) { struct resource r; if (of_address_to_resource(mio, 0, &r) == 0) chip->macio_base = diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c index 0779a2912237..6d7ffffcce95 100644 --- a/sound/ppc/tumbler.c +++ b/sound/ppc/tumbler.c @@ -1365,8 +1365,8 @@ int snd_pmac_tumbler_init(struct snd_pmac *chip) mix->anded_reset = 0; mix->reset_on_sleep = 1; - for (np = chip->node->child; np; np = np->sibling) { - if (!strcmp(np->name, "sound")) { + for_each_child_of_node(chip->node, np) { + if (of_node_name_eq(np, "sound")) { if (of_get_property(np, "has-anded-reset", NULL)) mix->anded_reset = 1; if (of_get_property(np, "layout-id", NULL)) diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 1cf11cf51e1d..6592a422a047 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -46,13 +46,11 @@ source "sound/soc/atmel/Kconfig" source "sound/soc/au1x/Kconfig" source "sound/soc/bcm/Kconfig" source "sound/soc/cirrus/Kconfig" -source "sound/soc/davinci/Kconfig" source "sound/soc/dwc/Kconfig" source "sound/soc/fsl/Kconfig" source "sound/soc/hisilicon/Kconfig" source "sound/soc/jz4740/Kconfig" source "sound/soc/nuc900/Kconfig" -source "sound/soc/omap/Kconfig" source "sound/soc/kirkwood/Kconfig" source "sound/soc/img/Kconfig" source "sound/soc/intel/Kconfig" @@ -70,9 +68,11 @@ source "sound/soc/sti/Kconfig" source "sound/soc/stm/Kconfig" source "sound/soc/sunxi/Kconfig" source "sound/soc/tegra/Kconfig" +source "sound/soc/ti/Kconfig" source "sound/soc/txx9/Kconfig" source "sound/soc/uniphier/Kconfig" source "sound/soc/ux500/Kconfig" +source "sound/soc/xilinx/Kconfig" source "sound/soc/xtensa/Kconfig" source "sound/soc/zte/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 62a5f87c3cfc..48c48c1c893c 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -30,7 +30,6 @@ obj-$(CONFIG_SND_SOC) += atmel/ obj-$(CONFIG_SND_SOC) += au1x/ obj-$(CONFIG_SND_SOC) += bcm/ obj-$(CONFIG_SND_SOC) += cirrus/ -obj-$(CONFIG_SND_SOC) += davinci/ obj-$(CONFIG_SND_SOC) += dwc/ obj-$(CONFIG_SND_SOC) += fsl/ obj-$(CONFIG_SND_SOC) += hisilicon/ @@ -41,7 +40,6 @@ obj-$(CONFIG_SND_SOC) += mediatek/ obj-$(CONFIG_SND_SOC) += meson/ obj-$(CONFIG_SND_SOC) += mxs/ obj-$(CONFIG_SND_SOC) += nuc900/ -obj-$(CONFIG_SND_SOC) += omap/ obj-$(CONFIG_SND_SOC) += kirkwood/ obj-$(CONFIG_SND_SOC) += pxa/ obj-$(CONFIG_SND_SOC) += qcom/ @@ -54,8 +52,10 @@ obj-$(CONFIG_SND_SOC) += sti/ obj-$(CONFIG_SND_SOC) += stm/ obj-$(CONFIG_SND_SOC) += sunxi/ obj-$(CONFIG_SND_SOC) += tegra/ +obj-$(CONFIG_SND_SOC) += ti/ obj-$(CONFIG_SND_SOC) += txx9/ obj-$(CONFIG_SND_SOC) += uniphier/ obj-$(CONFIG_SND_SOC) += ux500/ +obj-$(CONFIG_SND_SOC) += xilinx/ obj-$(CONFIG_SND_SOC) += xtensa/ obj-$(CONFIG_SND_SOC) += zte/ diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index 58c1dcb4d255..33ebec990c2f 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -19,3 +19,9 @@ config SND_SOC_AMD_CZ_RT5645_MACH depends on SND_SOC_AMD_ACP && I2C help This option enables machine driver for rt5645. + +config SND_SOC_AMD_ACP3x + tristate "AMD Audio Coprocessor-v3.x support" + depends on X86 && PCI + help + This option enables ACP v3.x I2S support on AMD platform diff --git a/sound/soc/amd/Makefile b/sound/soc/amd/Makefile index 79b0622fa5d3..8e1c571c3161 100644 --- a/sound/soc/amd/Makefile +++ b/sound/soc/amd/Makefile @@ -5,3 +5,4 @@ snd-soc-acp-rt5645-mach-objs := acp-rt5645.o obj-$(CONFIG_SND_SOC_AMD_ACP) += acp_audio_dma.o obj-$(CONFIG_SND_SOC_AMD_CZ_DA7219MX98357_MACH) += snd-soc-acp-da7219mx98357-mach.o obj-$(CONFIG_SND_SOC_AMD_CZ_RT5645_MACH) += snd-soc-acp-rt5645-mach.o +obj-$(CONFIG_SND_SOC_AMD_ACP3x) += raven/ diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c index 3f813ea5210a..a5daad973ce5 100644 --- a/sound/soc/amd/acp-da7219-max98357a.c +++ b/sound/soc/amd/acp-da7219-max98357a.c @@ -403,7 +403,7 @@ static struct regulator_config acp_da7219_cfg = { static struct regulator_ops acp_da7219_ops = { }; -static struct regulator_desc acp_da7219_desc = { +static const struct regulator_desc acp_da7219_desc = { .name = "reg-fixed-1.8V", .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c index cdebab2f8ce5..f4011bebc7ec 100644 --- a/sound/soc/amd/acp-pcm-dma.c +++ b/sound/soc/amd/acp-pcm-dma.c @@ -303,11 +303,10 @@ static void set_acp_to_i2s_dma_descriptors(void __iomem *acp_mmio, u32 size, } /* Create page table entries in ACP SRAM for the allocated memory */ -static void acp_pte_config(void __iomem *acp_mmio, struct page *pg, +static void acp_pte_config(void __iomem *acp_mmio, dma_addr_t addr, u16 num_of_pages, u32 pte_offset) { u16 page_idx; - u64 addr; u32 low; u32 high; u32 offset; @@ -317,7 +316,6 @@ static void acp_pte_config(void __iomem *acp_mmio, struct page *pg, /* Load the low address of page int ACP SRAM through SRBM */ acp_reg_write((offset + (page_idx * 8)), acp_mmio, mmACP_SRBM_Targ_Idx_Addr); - addr = page_to_phys(pg); low = lower_32_bits(addr); high = upper_32_bits(addr); @@ -333,7 +331,7 @@ static void acp_pte_config(void __iomem *acp_mmio, struct page *pg, acp_reg_write(high, acp_mmio, mmACP_SRBM_Targ_Idx_Data); /* Move to next physically contiguos page */ - pg++; + addr += PAGE_SIZE; } } @@ -343,7 +341,7 @@ static void config_acp_dma(void __iomem *acp_mmio, { u16 ch_acp_sysmem, ch_acp_i2s; - acp_pte_config(acp_mmio, rtd->pg, rtd->num_of_pages, + acp_pte_config(acp_mmio, rtd->dma_addr, rtd->num_of_pages, rtd->pte_offset); if (rtd->direction == SNDRV_PCM_STREAM_PLAYBACK) { @@ -850,7 +848,6 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream, int status; uint64_t size; u32 val = 0; - struct page *pg; struct snd_pcm_runtime *runtime; struct audio_substream_data *rtd; struct snd_soc_pcm_runtime *prtd = substream->private_data; @@ -986,16 +983,14 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream, return status; memset(substream->runtime->dma_area, 0, params_buffer_bytes(params)); - pg = virt_to_page(substream->dma_buffer.area); - if (pg) { + if (substream->dma_buffer.area) { acp_set_sram_bank_state(rtd->acp_mmio, 0, true); /* Save for runtime private data */ - rtd->pg = pg; + rtd->dma_addr = substream->dma_buffer.addr; rtd->order = get_order(size); /* Fill the page table entries in ACP SRAM */ - rtd->pg = pg; rtd->size = size; rtd->num_of_pages = PAGE_ALIGN(size) >> PAGE_SHIFT; rtd->direction = substream->stream; @@ -1151,18 +1146,21 @@ static int acp_dma_new(struct snd_soc_pcm_runtime *rtd) struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct audio_drv_data *adata = dev_get_drvdata(component->dev); + struct device *parent = component->dev->parent; switch (adata->asic_type) { case CHIP_STONEY: ret = snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, - NULL, ST_MIN_BUFFER, + parent, + ST_MIN_BUFFER, ST_MAX_BUFFER); break; default: ret = snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, - NULL, MIN_BUFFER, + parent, + MIN_BUFFER, MAX_BUFFER); break; } diff --git a/sound/soc/amd/acp.h b/sound/soc/amd/acp.h index dbbb1a85638d..e5ab6c6040a6 100644 --- a/sound/soc/amd/acp.h +++ b/sound/soc/amd/acp.h @@ -123,7 +123,7 @@ enum acp_dma_priority_level { }; struct audio_substream_data { - struct page *pg; + dma_addr_t dma_addr; unsigned int order; u16 num_of_pages; u16 i2s_instance; diff --git a/sound/soc/amd/raven/Makefile b/sound/soc/amd/raven/Makefile new file mode 100644 index 000000000000..108d1acf189b --- /dev/null +++ b/sound/soc/amd/raven/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Raven Ridge platform Support +snd-pci-acp3x-objs := pci-acp3x.o +snd-acp3x-pcm-dma-objs := acp3x-pcm-dma.o +obj-$(CONFIG_SND_SOC_AMD_ACP3x) += snd-pci-acp3x.o +obj-$(CONFIG_SND_SOC_AMD_ACP3x) += snd-acp3x-pcm-dma.o diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c new file mode 100644 index 000000000000..022a8912c8a2 --- /dev/null +++ b/sound/soc/amd/raven/acp3x-pcm-dma.c @@ -0,0 +1,777 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// AMD ALSA SoC PCM Driver +// +//Copyright 2016 Advanced Micro Devices, Inc. + +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/pm_runtime.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "acp3x.h" + +#define DRV_NAME "acp3x-i2s-audio" + +struct i2s_dev_data { + bool tdm_mode; + unsigned int i2s_irq; + u32 tdm_fmt; + void __iomem *acp3x_base; + struct snd_pcm_substream *play_stream; + struct snd_pcm_substream *capture_stream; +}; + +struct i2s_stream_instance { + u16 num_pages; + u16 channels; + u32 xfer_resolution; + struct page *pg; + void __iomem *acp3x_base; +}; + +static const struct snd_pcm_hardware acp3x_pcm_hardware_playback = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_96000, + .rate_min = 8000, + .rate_max = 96000, + .buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE, + .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE, + .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE, + .periods_min = PLAYBACK_MIN_NUM_PERIODS, + .periods_max = PLAYBACK_MAX_NUM_PERIODS, +}; + +static const struct snd_pcm_hardware acp3x_pcm_hardware_capture = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE, + .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE, + .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE, + .periods_min = CAPTURE_MIN_NUM_PERIODS, + .periods_max = CAPTURE_MAX_NUM_PERIODS, +}; + +static int acp3x_power_on(void __iomem *acp3x_base, bool on) +{ + u16 val, mask; + u32 timeout; + + if (on == true) { + val = 1; + mask = ACP3x_POWER_ON; + } else { + val = 0; + mask = ACP3x_POWER_OFF; + } + + rv_writel(val, acp3x_base + mmACP_PGFSM_CONTROL); + timeout = 0; + while (true) { + val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS); + if ((val & ACP3x_POWER_OFF_IN_PROGRESS) == mask) + break; + if (timeout > 100) { + pr_err("ACP3x power state change failure\n"); + return -ENODEV; + } + timeout++; + cpu_relax(); + } + return 0; +} + +static int acp3x_reset(void __iomem *acp3x_base) +{ + u32 val, timeout; + + rv_writel(1, acp3x_base + mmACP_SOFT_RESET); + timeout = 0; + while (true) { + val = rv_readl(acp3x_base + mmACP_SOFT_RESET); + if ((val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK) || + timeout > 100) { + if (val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK) + break; + return -ENODEV; + } + timeout++; + cpu_relax(); + } + + rv_writel(0, acp3x_base + mmACP_SOFT_RESET); + timeout = 0; + while (true) { + val = rv_readl(acp3x_base + mmACP_SOFT_RESET); + if (!val || timeout > 100) { + if (!val) + break; + return -ENODEV; + } + timeout++; + cpu_relax(); + } + return 0; +} + +static int acp3x_init(void __iomem *acp3x_base) +{ + int ret; + + /* power on */ + ret = acp3x_power_on(acp3x_base, true); + if (ret) { + pr_err("ACP3x power on failed\n"); + return ret; + } + /* Reset */ + ret = acp3x_reset(acp3x_base); + if (ret) { + pr_err("ACP3x reset failed\n"); + return ret; + } + return 0; +} + +static int acp3x_deinit(void __iomem *acp3x_base) +{ + int ret; + + /* Reset */ + ret = acp3x_reset(acp3x_base); + if (ret) { + pr_err("ACP3x reset failed\n"); + return ret; + } + /* power off */ + ret = acp3x_power_on(acp3x_base, false); + if (ret) { + pr_err("ACP3x power off failed\n"); + return ret; + } + return 0; +} + +static irqreturn_t i2s_irq_handler(int irq, void *dev_id) +{ + u16 play_flag, cap_flag; + u32 val; + struct i2s_dev_data *rv_i2s_data = dev_id; + + if (!rv_i2s_data) + return IRQ_NONE; + + play_flag = 0; + cap_flag = 0; + val = rv_readl(rv_i2s_data->acp3x_base + mmACP_EXTERNAL_INTR_STAT); + if ((val & BIT(BT_TX_THRESHOLD)) && rv_i2s_data->play_stream) { + rv_writel(BIT(BT_TX_THRESHOLD), rv_i2s_data->acp3x_base + + mmACP_EXTERNAL_INTR_STAT); + snd_pcm_period_elapsed(rv_i2s_data->play_stream); + play_flag = 1; + } + + if ((val & BIT(BT_RX_THRESHOLD)) && rv_i2s_data->capture_stream) { + rv_writel(BIT(BT_RX_THRESHOLD), rv_i2s_data->acp3x_base + + mmACP_EXTERNAL_INTR_STAT); + snd_pcm_period_elapsed(rv_i2s_data->capture_stream); + cap_flag = 1; + } + + if (play_flag | cap_flag) + return IRQ_HANDLED; + else + return IRQ_NONE; +} + +static void config_acp3x_dma(struct i2s_stream_instance *rtd, int direction) +{ + u16 page_idx; + u64 addr; + u32 low, high, val, acp_fifo_addr; + struct page *pg = rtd->pg; + + /* 8 scratch registers used to map one 64 bit address */ + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + val = 0; + else + val = rtd->num_pages * 8; + + /* Group Enable */ + rv_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp3x_base + + mmACPAXI2AXI_ATU_BASE_ADDR_GRP_1); + rv_writel(PAGE_SIZE_4K_ENABLE, rtd->acp3x_base + + mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_1); + + for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) { + /* Load the low address of page int ACP SRAM through SRBM */ + addr = page_to_phys(pg); + low = lower_32_bits(addr); + high = upper_32_bits(addr); + + rv_writel(low, rtd->acp3x_base + mmACP_SCRATCH_REG_0 + val); + high |= BIT(31); + rv_writel(high, rtd->acp3x_base + mmACP_SCRATCH_REG_0 + val + + 4); + /* Move to next physically contiguos page */ + val += 8; + pg++; + } + + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + /* Config ringbuffer */ + rv_writel(MEM_WINDOW_START, rtd->acp3x_base + + mmACP_BT_TX_RINGBUFADDR); + rv_writel(MAX_BUFFER, rtd->acp3x_base + + mmACP_BT_TX_RINGBUFSIZE); + rv_writel(DMA_SIZE, rtd->acp3x_base + mmACP_BT_TX_DMA_SIZE); + + /* Config audio fifo */ + acp_fifo_addr = ACP_SRAM_PTE_OFFSET + (rtd->num_pages * 8) + + PLAYBACK_FIFO_ADDR_OFFSET; + rv_writel(acp_fifo_addr, rtd->acp3x_base + + mmACP_BT_TX_FIFOADDR); + rv_writel(FIFO_SIZE, rtd->acp3x_base + mmACP_BT_TX_FIFOSIZE); + } else { + /* Config ringbuffer */ + rv_writel(MEM_WINDOW_START + MAX_BUFFER, rtd->acp3x_base + + mmACP_BT_RX_RINGBUFADDR); + rv_writel(MAX_BUFFER, rtd->acp3x_base + + mmACP_BT_RX_RINGBUFSIZE); + rv_writel(DMA_SIZE, rtd->acp3x_base + mmACP_BT_RX_DMA_SIZE); + + /* Config audio fifo */ + acp_fifo_addr = ACP_SRAM_PTE_OFFSET + + (rtd->num_pages * 8) + CAPTURE_FIFO_ADDR_OFFSET; + rv_writel(acp_fifo_addr, rtd->acp3x_base + + mmACP_BT_RX_FIFOADDR); + rv_writel(FIFO_SIZE, rtd->acp3x_base + mmACP_BT_RX_FIFOSIZE); + } + + /* Enable watermark/period interrupt to host */ + rv_writel(BIT(BT_TX_THRESHOLD) | BIT(BT_RX_THRESHOLD), + rtd->acp3x_base + mmACP_EXTERNAL_INTR_CNTL); +} + +static int acp3x_dma_open(struct snd_pcm_substream *substream) +{ + int ret = 0; + + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *prtd = substream->private_data; + struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd, + DRV_NAME); + struct i2s_dev_data *adata = dev_get_drvdata(component->dev); + + struct i2s_stream_instance *i2s_data = kzalloc(sizeof(struct i2s_stream_instance), + GFP_KERNEL); + if (!i2s_data) + return -EINVAL; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + runtime->hw = acp3x_pcm_hardware_playback; + else + runtime->hw = acp3x_pcm_hardware_capture; + + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) { + dev_err(component->dev, "set integer constraint failed\n"); + kfree(i2s_data); + return ret; + } + + if (!adata->play_stream && !adata->capture_stream) + rv_writel(1, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + adata->play_stream = substream; + else + adata->capture_stream = substream; + + i2s_data->acp3x_base = adata->acp3x_base; + runtime->private_data = i2s_data; + return 0; +} + +static int acp3x_dma_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + int status; + u64 size; + struct page *pg; + struct snd_pcm_runtime *runtime = substream->runtime; + struct i2s_stream_instance *rtd = runtime->private_data; + + if (!rtd) + return -EINVAL; + + size = params_buffer_bytes(params); + status = snd_pcm_lib_malloc_pages(substream, size); + if (status < 0) + return status; + + memset(substream->runtime->dma_area, 0, params_buffer_bytes(params)); + pg = virt_to_page(substream->dma_buffer.area); + if (pg) { + rtd->pg = pg; + rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT); + config_acp3x_dma(rtd, substream->stream); + status = 0; + } else { + status = -ENOMEM; + } + return status; +} + +static snd_pcm_uframes_t acp3x_dma_pointer(struct snd_pcm_substream *substream) +{ + u32 pos = 0; + struct i2s_stream_instance *rtd = substream->runtime->private_data; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + pos = rv_readl(rtd->acp3x_base + + mmACP_BT_TX_LINKPOSITIONCNTR); + else + pos = rv_readl(rtd->acp3x_base + + mmACP_BT_RX_LINKPOSITIONCNTR); + + if (pos >= MAX_BUFFER) + pos = 0; + + return bytes_to_frames(substream->runtime, pos); +} + +static int acp3x_dma_new(struct snd_soc_pcm_runtime *rtd) +{ + return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, + SNDRV_DMA_TYPE_DEV, + NULL, MIN_BUFFER, + MAX_BUFFER); +} + +static int acp3x_dma_hw_free(struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int acp3x_dma_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + return snd_pcm_lib_default_mmap(substream, vma); +} + +static int acp3x_dma_close(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *prtd = substream->private_data; + struct i2s_stream_instance *rtd = substream->runtime->private_data; + struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd, + DRV_NAME); + struct i2s_dev_data *adata = dev_get_drvdata(component->dev); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + adata->play_stream = NULL; + else + adata->capture_stream = NULL; + + /* Disable ACP irq, when the current stream is being closed and + * another stream is also not active. + */ + if (!adata->play_stream && !adata->capture_stream) + rv_writel(0, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB); + kfree(rtd); + return 0; +} + +static struct snd_pcm_ops acp3x_dma_ops = { + .open = acp3x_dma_open, + .close = acp3x_dma_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = acp3x_dma_hw_params, + .hw_free = acp3x_dma_hw_free, + .pointer = acp3x_dma_pointer, + .mmap = acp3x_dma_mmap, +}; + + +static int acp3x_dai_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) +{ + + struct i2s_dev_data *adata = snd_soc_dai_get_drvdata(cpu_dai); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + adata->tdm_mode = false; + break; + case SND_SOC_DAIFMT_DSP_A: + adata->tdm_mode = true; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int acp3x_dai_set_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, + u32 rx_mask, int slots, int slot_width) +{ + u32 val = 0; + u16 slot_len; + + struct i2s_dev_data *adata = snd_soc_dai_get_drvdata(cpu_dai); + + switch (slot_width) { + case SLOT_WIDTH_8: + slot_len = 8; + break; + case SLOT_WIDTH_16: + slot_len = 16; + break; + case SLOT_WIDTH_24: + slot_len = 24; + break; + case SLOT_WIDTH_32: + slot_len = 0; + break; + default: + return -EINVAL; + } + + val = rv_readl(adata->acp3x_base + mmACP_BTTDM_ITER); + rv_writel((val | 0x2), adata->acp3x_base + mmACP_BTTDM_ITER); + val = rv_readl(adata->acp3x_base + mmACP_BTTDM_IRER); + rv_writel((val | 0x2), adata->acp3x_base + mmACP_BTTDM_IRER); + + val = (FRM_LEN | (slots << 15) | (slot_len << 18)); + rv_writel(val, adata->acp3x_base + mmACP_BTTDM_TXFRMT); + rv_writel(val, adata->acp3x_base + mmACP_BTTDM_RXFRMT); + + adata->tdm_fmt = val; + return 0; +} + +static int acp3x_dai_i2s_hwparams(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + u32 val = 0; + struct i2s_stream_instance *rtd = substream->runtime->private_data; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_U8: + case SNDRV_PCM_FORMAT_S8: + rtd->xfer_resolution = 0x0; + break; + case SNDRV_PCM_FORMAT_S16_LE: + rtd->xfer_resolution = 0x02; + break; + case SNDRV_PCM_FORMAT_S24_LE: + rtd->xfer_resolution = 0x04; + break; + case SNDRV_PCM_FORMAT_S32_LE: + rtd->xfer_resolution = 0x05; + break; + default: + return -EINVAL; + } + val = rv_readl(rtd->acp3x_base + mmACP_BTTDM_ITER); + val = val | (rtd->xfer_resolution << 3); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rv_writel(val, rtd->acp3x_base + mmACP_BTTDM_ITER); + else + rv_writel(val, rtd->acp3x_base + mmACP_BTTDM_IRER); + + return 0; +} + +static int acp3x_dai_i2s_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + int ret = 0; + struct i2s_stream_instance *rtd = substream->runtime->private_data; + u32 val, period_bytes; + + period_bytes = frames_to_bytes(substream->runtime, + substream->runtime->period_size); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + rv_writel(period_bytes, rtd->acp3x_base + + mmACP_BT_TX_INTR_WATERMARK_SIZE); + val = rv_readl(rtd->acp3x_base + mmACP_BTTDM_ITER); + val = val | BIT(0); + rv_writel(val, rtd->acp3x_base + mmACP_BTTDM_ITER); + } else { + rv_writel(period_bytes, rtd->acp3x_base + + mmACP_BT_RX_INTR_WATERMARK_SIZE); + val = rv_readl(rtd->acp3x_base + mmACP_BTTDM_IRER); + val = val | BIT(0); + rv_writel(val, rtd->acp3x_base + mmACP_BTTDM_IRER); + } + rv_writel(1, rtd->acp3x_base + mmACP_BTTDM_IER); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + val = rv_readl(rtd->acp3x_base + mmACP_BTTDM_ITER); + val = val & ~BIT(0); + rv_writel(val, rtd->acp3x_base + mmACP_BTTDM_ITER); + } else { + val = rv_readl(rtd->acp3x_base + mmACP_BTTDM_IRER); + val = val & ~BIT(0); + rv_writel(val, rtd->acp3x_base + mmACP_BTTDM_IRER); + } + rv_writel(0, rtd->acp3x_base + mmACP_BTTDM_IER); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +struct snd_soc_dai_ops acp3x_dai_i2s_ops = { + .hw_params = acp3x_dai_i2s_hwparams, + .trigger = acp3x_dai_i2s_trigger, + .set_fmt = acp3x_dai_i2s_set_fmt, + .set_tdm_slot = acp3x_dai_set_tdm_slot, +}; + +static struct snd_soc_dai_driver acp3x_i2s_dai_driver = { + .playback = { + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 8, + + .rate_min = 8000, + .rate_max = 96000, + }, + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &acp3x_dai_i2s_ops, +}; + +static const struct snd_soc_component_driver acp3x_i2s_component = { + .name = DRV_NAME, + .ops = &acp3x_dma_ops, + .pcm_new = acp3x_dma_new, +}; + +static int acp3x_audio_probe(struct platform_device *pdev) +{ + int status; + struct resource *res; + struct i2s_dev_data *adata; + unsigned int irqflags; + + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "platform_data not retrieved\n"); + return -ENODEV; + } + irqflags = *((unsigned int *)(pdev->dev.platform_data)); + + adata = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dev_data), + GFP_KERNEL); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n"); + return -ENODEV; + } + + adata->acp3x_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n"); + return -ENODEV; + } + + adata->i2s_irq = res->start; + adata->play_stream = NULL; + adata->capture_stream = NULL; + + dev_set_drvdata(&pdev->dev, adata); + /* Initialize ACP */ + status = acp3x_init(adata->acp3x_base); + if (status) + return -ENODEV; + status = devm_snd_soc_register_component(&pdev->dev, + &acp3x_i2s_component, + &acp3x_i2s_dai_driver, 1); + if (status) { + dev_err(&pdev->dev, "Fail to register acp i2s dai\n"); + goto dev_err; + } + status = devm_request_irq(&pdev->dev, adata->i2s_irq, i2s_irq_handler, + irqflags, "ACP3x_I2S_IRQ", adata); + if (status) { + dev_err(&pdev->dev, "ACP3x I2S IRQ request failed\n"); + goto dev_err; + } + + pm_runtime_set_autosuspend_delay(&pdev->dev, 10000); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); + return 0; +dev_err: + status = acp3x_deinit(adata->acp3x_base); + if (status) + dev_err(&pdev->dev, "ACP de-init failed\n"); + else + dev_info(&pdev->dev, "ACP de-initialized\n"); + /*ignore device status and return driver probe error*/ + return -ENODEV; +} + +static int acp3x_audio_remove(struct platform_device *pdev) +{ + int ret; + struct i2s_dev_data *adata = dev_get_drvdata(&pdev->dev); + + ret = acp3x_deinit(adata->acp3x_base); + if (ret) + dev_err(&pdev->dev, "ACP de-init failed\n"); + else + dev_info(&pdev->dev, "ACP de-initialized\n"); + + pm_runtime_disable(&pdev->dev); + return 0; +} + +static int acp3x_resume(struct device *dev) +{ + int status; + u32 val; + struct i2s_dev_data *adata = dev_get_drvdata(dev); + + status = acp3x_init(adata->acp3x_base); + if (status) + return -ENODEV; + + if (adata->play_stream && adata->play_stream->runtime) { + struct i2s_stream_instance *rtd = + adata->play_stream->runtime->private_data; + config_acp3x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK); + rv_writel((rtd->xfer_resolution << 3), + rtd->acp3x_base + mmACP_BTTDM_ITER); + if (adata->tdm_mode == true) { + rv_writel(adata->tdm_fmt, adata->acp3x_base + + mmACP_BTTDM_TXFRMT); + val = rv_readl(adata->acp3x_base + mmACP_BTTDM_ITER); + rv_writel((val | 0x2), adata->acp3x_base + + mmACP_BTTDM_ITER); + } + } + + if (adata->capture_stream && adata->capture_stream->runtime) { + struct i2s_stream_instance *rtd = + adata->capture_stream->runtime->private_data; + config_acp3x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE); + rv_writel((rtd->xfer_resolution << 3), + rtd->acp3x_base + mmACP_BTTDM_IRER); + if (adata->tdm_mode == true) { + rv_writel(adata->tdm_fmt, adata->acp3x_base + + mmACP_BTTDM_RXFRMT); + val = rv_readl(adata->acp3x_base + mmACP_BTTDM_IRER); + rv_writel((val | 0x2), adata->acp3x_base + + mmACP_BTTDM_IRER); + } + } + + rv_writel(1, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB); + return 0; +} + + +static int acp3x_pcm_runtime_suspend(struct device *dev) +{ + int status; + struct i2s_dev_data *adata = dev_get_drvdata(dev); + + status = acp3x_deinit(adata->acp3x_base); + if (status) + dev_err(dev, "ACP de-init failed\n"); + else + dev_info(dev, "ACP de-initialized\n"); + + rv_writel(0, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB); + + return 0; +} + +static int acp3x_pcm_runtime_resume(struct device *dev) +{ + int status; + struct i2s_dev_data *adata = dev_get_drvdata(dev); + + status = acp3x_init(adata->acp3x_base); + if (status) + return -ENODEV; + rv_writel(1, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB); + return 0; +} + +static const struct dev_pm_ops acp3x_pm_ops = { + .runtime_suspend = acp3x_pcm_runtime_suspend, + .runtime_resume = acp3x_pcm_runtime_resume, + .resume = acp3x_resume, +}; + +static struct platform_driver acp3x_dma_driver = { + .probe = acp3x_audio_probe, + .remove = acp3x_audio_remove, + .driver = { + .name = "acp3x_rv_i2s", + .pm = &acp3x_pm_ops, + }, +}; + +module_platform_driver(acp3x_dma_driver); + +MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com"); +MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); +MODULE_DESCRIPTION("AMD ACP 3.x PCM Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/amd/raven/acp3x.h b/sound/soc/amd/raven/acp3x.h new file mode 100644 index 000000000000..4f2cadd90a87 --- /dev/null +++ b/sound/soc/amd/raven/acp3x.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * AMD ALSA SoC PCM Driver + * + * Copyright 2016 Advanced Micro Devices, Inc. + */ + +#include "chip_offset_byte.h" + +#define ACP3x_PHY_BASE_ADDRESS 0x1240000 +#define ACP3x_I2S_MODE 0 +#define ACP3x_REG_START 0x1240000 +#define ACP3x_REG_END 0x1250200 +#define I2S_MODE 0x04 +#define BT_TX_THRESHOLD 26 +#define BT_RX_THRESHOLD 25 +#define ACP3x_POWER_ON 0x00 +#define ACP3x_POWER_ON_IN_PROGRESS 0x01 +#define ACP3x_POWER_OFF 0x02 +#define ACP3x_POWER_OFF_IN_PROGRESS 0x03 +#define ACP3x_SOFT_RESET__SoftResetAudDone_MASK 0x00010001 + +#define ACP_SRAM_PTE_OFFSET 0x02050000 +#define PAGE_SIZE_4K_ENABLE 0x2 +#define MEM_WINDOW_START 0x4000000 +#define PLAYBACK_FIFO_ADDR_OFFSET 0x400 +#define CAPTURE_FIFO_ADDR_OFFSET 0x500 + +#define PLAYBACK_MIN_NUM_PERIODS 2 +#define PLAYBACK_MAX_NUM_PERIODS 8 +#define PLAYBACK_MAX_PERIOD_SIZE 16384 +#define PLAYBACK_MIN_PERIOD_SIZE 4096 +#define CAPTURE_MIN_NUM_PERIODS 2 +#define CAPTURE_MAX_NUM_PERIODS 8 +#define CAPTURE_MAX_PERIOD_SIZE 16384 +#define CAPTURE_MIN_PERIOD_SIZE 4096 + +#define MAX_BUFFER (PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS) +#define MIN_BUFFER MAX_BUFFER +#define FIFO_SIZE 0x100 +#define DMA_SIZE 0x40 +#define FRM_LEN 0x100 + +#define SLOT_WIDTH_8 0x08 +#define SLOT_WIDTH_16 0x10 +#define SLOT_WIDTH_24 0x18 +#define SLOT_WIDTH_32 0x20 + + +static inline u32 rv_readl(void __iomem *base_addr) +{ + return readl(base_addr - ACP3x_PHY_BASE_ADDRESS); +} + +static inline void rv_writel(u32 val, void __iomem *base_addr) +{ + writel(val, base_addr - ACP3x_PHY_BASE_ADDRESS); +} diff --git a/sound/soc/amd/raven/chip_offset_byte.h b/sound/soc/amd/raven/chip_offset_byte.h new file mode 100644 index 000000000000..9c1fac58fb2a --- /dev/null +++ b/sound/soc/amd/raven/chip_offset_byte.h @@ -0,0 +1,639 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * AMD ACP 3.0 Register Documentation + * + * Copyright 2016 Advanced Micro Devices, Inc. + */ + +#ifndef _acp_ip_OFFSET_HEADER +#define _acp_ip_OFFSET_HEADER +// Registers from ACP_DMA block + +#define mmACP_DMA_CNTL_0 0x1240000 +#define mmACP_DMA_CNTL_1 0x1240004 +#define mmACP_DMA_CNTL_2 0x1240008 +#define mmACP_DMA_CNTL_3 0x124000C +#define mmACP_DMA_CNTL_4 0x1240010 +#define mmACP_DMA_CNTL_5 0x1240014 +#define mmACP_DMA_CNTL_6 0x1240018 +#define mmACP_DMA_CNTL_7 0x124001C +#define mmACP_DMA_DSCR_STRT_IDX_0 0x1240020 +#define mmACP_DMA_DSCR_STRT_IDX_1 0x1240024 +#define mmACP_DMA_DSCR_STRT_IDX_2 0x1240028 +#define mmACP_DMA_DSCR_STRT_IDX_3 0x124002C +#define mmACP_DMA_DSCR_STRT_IDX_4 0x1240030 +#define mmACP_DMA_DSCR_STRT_IDX_5 0x1240034 +#define mmACP_DMA_DSCR_STRT_IDX_6 0x1240038 +#define mmACP_DMA_DSCR_STRT_IDX_7 0x124003C +#define mmACP_DMA_DSCR_CNT_0 0x1240040 +#define mmACP_DMA_DSCR_CNT_1 0x1240044 +#define mmACP_DMA_DSCR_CNT_2 0x1240048 +#define mmACP_DMA_DSCR_CNT_3 0x124004C +#define mmACP_DMA_DSCR_CNT_4 0x1240050 +#define mmACP_DMA_DSCR_CNT_5 0x1240054 +#define mmACP_DMA_DSCR_CNT_6 0x1240058 +#define mmACP_DMA_DSCR_CNT_7 0x124005C +#define mmACP_DMA_PRIO_0 0x1240060 +#define mmACP_DMA_PRIO_1 0x1240064 +#define mmACP_DMA_PRIO_2 0x1240068 +#define mmACP_DMA_PRIO_3 0x124006C +#define mmACP_DMA_PRIO_4 0x1240070 +#define mmACP_DMA_PRIO_5 0x1240074 +#define mmACP_DMA_PRIO_6 0x1240078 +#define mmACP_DMA_PRIO_7 0x124007C +#define mmACP_DMA_CUR_DSCR_0 0x1240080 +#define mmACP_DMA_CUR_DSCR_1 0x1240084 +#define mmACP_DMA_CUR_DSCR_2 0x1240088 +#define mmACP_DMA_CUR_DSCR_3 0x124008C +#define mmACP_DMA_CUR_DSCR_4 0x1240090 +#define mmACP_DMA_CUR_DSCR_5 0x1240094 +#define mmACP_DMA_CUR_DSCR_6 0x1240098 +#define mmACP_DMA_CUR_DSCR_7 0x124009C +#define mmACP_DMA_CUR_TRANS_CNT_0 0x12400A0 +#define mmACP_DMA_CUR_TRANS_CNT_1 0x12400A4 +#define mmACP_DMA_CUR_TRANS_CNT_2 0x12400A8 +#define mmACP_DMA_CUR_TRANS_CNT_3 0x12400AC +#define mmACP_DMA_CUR_TRANS_CNT_4 0x12400B0 +#define mmACP_DMA_CUR_TRANS_CNT_5 0x12400B4 +#define mmACP_DMA_CUR_TRANS_CNT_6 0x12400B8 +#define mmACP_DMA_CUR_TRANS_CNT_7 0x12400BC +#define mmACP_DMA_ERR_STS_0 0x12400C0 +#define mmACP_DMA_ERR_STS_1 0x12400C4 +#define mmACP_DMA_ERR_STS_2 0x12400C8 +#define mmACP_DMA_ERR_STS_3 0x12400CC +#define mmACP_DMA_ERR_STS_4 0x12400D0 +#define mmACP_DMA_ERR_STS_5 0x12400D4 +#define mmACP_DMA_ERR_STS_6 0x12400D8 +#define mmACP_DMA_ERR_STS_7 0x12400DC +#define mmACP_DMA_DESC_BASE_ADDR 0x12400E0 +#define mmACP_DMA_DESC_MAX_NUM_DSCR 0x12400E4 +#define mmACP_DMA_CH_STS 0x12400E8 +#define mmACP_DMA_CH_GROUP 0x12400EC +#define mmACP_DMA_CH_RST_STS 0x12400F0 + + +// Registers from ACP_AXI2AXIATU block + +#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_1 0x1240C00 +#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_1 0x1240C04 +#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_2 0x1240C08 +#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_2 0x1240C0C +#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_3 0x1240C10 +#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_3 0x1240C14 +#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_4 0x1240C18 +#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_4 0x1240C1C +#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0x1240C20 +#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0x1240C24 +#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_6 0x1240C28 +#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_6 0x1240C2C +#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_7 0x1240C30 +#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_7 0x1240C34 +#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_8 0x1240C38 +#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_8 0x1240C3C +#define mmACPAXI2AXI_ATU_CTRL 0x1240C40 + + +// Registers from ACP_CLKRST block + +#define mmACP_SOFT_RESET 0x1241000 +#define mmACP_CONTROL 0x1241004 +#define mmACP_STATUS 0x1241008 +#define mmACP_DSP0_OCD_HALT_ON_RST 0x124100C +#define mmACP_DYNAMIC_CG_MASTER_CONTROL 0x1241010 + + +// Registers from ACP_MISC block + +#define mmACP_EXTERNAL_INTR_ENB 0x1241800 +#define mmACP_EXTERNAL_INTR_CNTL 0x1241804 +#define mmACP_EXTERNAL_INTR_STAT 0x1241808 +#define mmACP_DSP0_INTR_CNTL 0x124180C +#define mmACP_DSP0_INTR_STAT 0x1241810 +#define mmACP_DSP_SW_INTR_CNTL 0x1241814 +#define mmACP_DSP_SW_INTR_STAT 0x1241818 +#define mmACP_SW_INTR_TRIG 0x124181C +#define mmACP_SMU_MAILBOX 0x1241820 +#define mmDSP_INTERRUPT_ROUTING_CTRL 0x1241824 +#define mmACP_DSP0_WATCHDOG_TIMER_CNTL 0x1241828 +#define mmACP_DSP0_EXT_TIMER1_CNTL 0x124182C +#define mmACP_DSP0_EXT_TIMER2_CNTL 0x1241830 +#define mmACP_DSP0_EXT_TIMER3_CNTL 0x1241834 +#define mmACP_DSP0_EXT_TIMER4_CNTL 0x1241838 +#define mmACP_DSP0_EXT_TIMER5_CNTL 0x124183C +#define mmACP_DSP0_EXT_TIMER6_CNTL 0x1241840 +#define mmACP_DSP0_EXT_TIMER1_CURR_VALUE 0x1241844 +#define mmACP_DSP0_EXT_TIMER2_CURR_VALUE 0x1241848 +#define mmACP_DSP0_EXT_TIMER3_CURR_VALUE 0x124184C +#define mmACP_DSP0_EXT_TIMER4_CURR_VALUE 0x1241850 +#define mmACP_DSP0_EXT_TIMER5_CURR_VALUE 0x1241854 +#define mmACP_DSP0_EXT_TIMER6_CURR_VALUE 0x1241858 +#define mmACP_FW_STATUS 0x124185C +#define mmACP_TIMER 0x1241874 +#define mmACP_TIMER_CNTL 0x1241878 +#define mmACP_PGMEM_CTRL 0x12418C0 +#define mmACP_ERROR_STATUS 0x12418C4 +#define mmACP_SW_I2S_ERROR_REASON 0x12418C8 +#define mmACP_MEM_PG_STS 0x12418CC + + +// Registers from ACP_PGFSM block + +#define mmACP_I2S_PIN_CONFIG 0x1241400 +#define mmACP_PAD_PULLUP_PULLDOWN_CTRL 0x1241404 +#define mmACP_PAD_DRIVE_STRENGTH_CTRL 0x1241408 +#define mmACP_SW_PAD_KEEPER_EN 0x124140C +#define mmACP_SW_WAKE_EN 0x1241410 +#define mmACP_I2S_WAKE_EN 0x1241414 +#define mmACP_PME_EN 0x1241418 +#define mmACP_PGFSM_CONTROL 0x124141C +#define mmACP_PGFSM_STATUS 0x1241420 + + +// Registers from ACP_SCRATCH block + +#define mmACP_SCRATCH_REG_0 0x1250000 +#define mmACP_SCRATCH_REG_1 0x1250004 +#define mmACP_SCRATCH_REG_2 0x1250008 +#define mmACP_SCRATCH_REG_3 0x125000C +#define mmACP_SCRATCH_REG_4 0x1250010 +#define mmACP_SCRATCH_REG_5 0x1250014 +#define mmACP_SCRATCH_REG_6 0x1250018 +#define mmACP_SCRATCH_REG_7 0x125001C +#define mmACP_SCRATCH_REG_8 0x1250020 +#define mmACP_SCRATCH_REG_9 0x1250024 +#define mmACP_SCRATCH_REG_10 0x1250028 +#define mmACP_SCRATCH_REG_11 0x125002C +#define mmACP_SCRATCH_REG_12 0x1250030 +#define mmACP_SCRATCH_REG_13 0x1250034 +#define mmACP_SCRATCH_REG_14 0x1250038 +#define mmACP_SCRATCH_REG_15 0x125003C +#define mmACP_SCRATCH_REG_16 0x1250040 +#define mmACP_SCRATCH_REG_17 0x1250044 +#define mmACP_SCRATCH_REG_18 0x1250048 +#define mmACP_SCRATCH_REG_19 0x125004C +#define mmACP_SCRATCH_REG_20 0x1250050 +#define mmACP_SCRATCH_REG_21 0x1250054 +#define mmACP_SCRATCH_REG_22 0x1250058 +#define mmACP_SCRATCH_REG_23 0x125005C +#define mmACP_SCRATCH_REG_24 0x1250060 +#define mmACP_SCRATCH_REG_25 0x1250064 +#define mmACP_SCRATCH_REG_26 0x1250068 +#define mmACP_SCRATCH_REG_27 0x125006C +#define mmACP_SCRATCH_REG_28 0x1250070 +#define mmACP_SCRATCH_REG_29 0x1250074 +#define mmACP_SCRATCH_REG_30 0x1250078 +#define mmACP_SCRATCH_REG_31 0x125007C +#define mmACP_SCRATCH_REG_32 0x1250080 +#define mmACP_SCRATCH_REG_33 0x1250084 +#define mmACP_SCRATCH_REG_34 0x1250088 +#define mmACP_SCRATCH_REG_35 0x125008C +#define mmACP_SCRATCH_REG_36 0x1250090 +#define mmACP_SCRATCH_REG_37 0x1250094 +#define mmACP_SCRATCH_REG_38 0x1250098 +#define mmACP_SCRATCH_REG_39 0x125009C +#define mmACP_SCRATCH_REG_40 0x12500A0 +#define mmACP_SCRATCH_REG_41 0x12500A4 +#define mmACP_SCRATCH_REG_42 0x12500A8 +#define mmACP_SCRATCH_REG_43 0x12500AC +#define mmACP_SCRATCH_REG_44 0x12500B0 +#define mmACP_SCRATCH_REG_45 0x12500B4 +#define mmACP_SCRATCH_REG_46 0x12500B8 +#define mmACP_SCRATCH_REG_47 0x12500BC +#define mmACP_SCRATCH_REG_48 0x12500C0 +#define mmACP_SCRATCH_REG_49 0x12500C4 +#define mmACP_SCRATCH_REG_50 0x12500C8 +#define mmACP_SCRATCH_REG_51 0x12500CC +#define mmACP_SCRATCH_REG_52 0x12500D0 +#define mmACP_SCRATCH_REG_53 0x12500D4 +#define mmACP_SCRATCH_REG_54 0x12500D8 +#define mmACP_SCRATCH_REG_55 0x12500DC +#define mmACP_SCRATCH_REG_56 0x12500E0 +#define mmACP_SCRATCH_REG_57 0x12500E4 +#define mmACP_SCRATCH_REG_58 0x12500E8 +#define mmACP_SCRATCH_REG_59 0x12500EC +#define mmACP_SCRATCH_REG_60 0x12500F0 +#define mmACP_SCRATCH_REG_61 0x12500F4 +#define mmACP_SCRATCH_REG_62 0x12500F8 +#define mmACP_SCRATCH_REG_63 0x12500FC +#define mmACP_SCRATCH_REG_64 0x1250100 +#define mmACP_SCRATCH_REG_65 0x1250104 +#define mmACP_SCRATCH_REG_66 0x1250108 +#define mmACP_SCRATCH_REG_67 0x125010C +#define mmACP_SCRATCH_REG_68 0x1250110 +#define mmACP_SCRATCH_REG_69 0x1250114 +#define mmACP_SCRATCH_REG_70 0x1250118 +#define mmACP_SCRATCH_REG_71 0x125011C +#define mmACP_SCRATCH_REG_72 0x1250120 +#define mmACP_SCRATCH_REG_73 0x1250124 +#define mmACP_SCRATCH_REG_74 0x1250128 +#define mmACP_SCRATCH_REG_75 0x125012C +#define mmACP_SCRATCH_REG_76 0x1250130 +#define mmACP_SCRATCH_REG_77 0x1250134 +#define mmACP_SCRATCH_REG_78 0x1250138 +#define mmACP_SCRATCH_REG_79 0x125013C +#define mmACP_SCRATCH_REG_80 0x1250140 +#define mmACP_SCRATCH_REG_81 0x1250144 +#define mmACP_SCRATCH_REG_82 0x1250148 +#define mmACP_SCRATCH_REG_83 0x125014C +#define mmACP_SCRATCH_REG_84 0x1250150 +#define mmACP_SCRATCH_REG_85 0x1250154 +#define mmACP_SCRATCH_REG_86 0x1250158 +#define mmACP_SCRATCH_REG_87 0x125015C +#define mmACP_SCRATCH_REG_88 0x1250160 +#define mmACP_SCRATCH_REG_89 0x1250164 +#define mmACP_SCRATCH_REG_90 0x1250168 +#define mmACP_SCRATCH_REG_91 0x125016C +#define mmACP_SCRATCH_REG_92 0x1250170 +#define mmACP_SCRATCH_REG_93 0x1250174 +#define mmACP_SCRATCH_REG_94 0x1250178 +#define mmACP_SCRATCH_REG_95 0x125017C +#define mmACP_SCRATCH_REG_96 0x1250180 +#define mmACP_SCRATCH_REG_97 0x1250184 +#define mmACP_SCRATCH_REG_98 0x1250188 +#define mmACP_SCRATCH_REG_99 0x125018C +#define mmACP_SCRATCH_REG_100 0x1250190 +#define mmACP_SCRATCH_REG_101 0x1250194 +#define mmACP_SCRATCH_REG_102 0x1250198 +#define mmACP_SCRATCH_REG_103 0x125019C +#define mmACP_SCRATCH_REG_104 0x12501A0 +#define mmACP_SCRATCH_REG_105 0x12501A4 +#define mmACP_SCRATCH_REG_106 0x12501A8 +#define mmACP_SCRATCH_REG_107 0x12501AC +#define mmACP_SCRATCH_REG_108 0x12501B0 +#define mmACP_SCRATCH_REG_109 0x12501B4 +#define mmACP_SCRATCH_REG_110 0x12501B8 +#define mmACP_SCRATCH_REG_111 0x12501BC +#define mmACP_SCRATCH_REG_112 0x12501C0 +#define mmACP_SCRATCH_REG_113 0x12501C4 +#define mmACP_SCRATCH_REG_114 0x12501C8 +#define mmACP_SCRATCH_REG_115 0x12501CC +#define mmACP_SCRATCH_REG_116 0x12501D0 +#define mmACP_SCRATCH_REG_117 0x12501D4 +#define mmACP_SCRATCH_REG_118 0x12501D8 +#define mmACP_SCRATCH_REG_119 0x12501DC +#define mmACP_SCRATCH_REG_120 0x12501E0 +#define mmACP_SCRATCH_REG_121 0x12501E4 +#define mmACP_SCRATCH_REG_122 0x12501E8 +#define mmACP_SCRATCH_REG_123 0x12501EC +#define mmACP_SCRATCH_REG_124 0x12501F0 +#define mmACP_SCRATCH_REG_125 0x12501F4 +#define mmACP_SCRATCH_REG_126 0x12501F8 +#define mmACP_SCRATCH_REG_127 0x12501FC +#define mmACP_SCRATCH_REG_128 0x1250200 + + +// Registers from ACP_SW_ACLK block + +#define mmSW_CORB_Base_Address 0x1243200 +#define mmSW_CORB_Write_Pointer 0x1243204 +#define mmSW_CORB_Read_Pointer 0x1243208 +#define mmSW_CORB_Control 0x124320C +#define mmSW_CORB_Size 0x1243214 +#define mmSW_RIRB_Base_Address 0x1243218 +#define mmSW_RIRB_Write_Pointer 0x124321C +#define mmSW_RIRB_Response_Interrupt_Count 0x1243220 +#define mmSW_RIRB_Control 0x1243224 +#define mmSW_RIRB_Size 0x1243228 +#define mmSW_RIRB_FIFO_MIN_THDL 0x124322C +#define mmSW_imm_cmd_UPPER_WORD 0x1243230 +#define mmSW_imm_cmd_LOWER_QWORD 0x1243234 +#define mmSW_imm_resp_UPPER_WORD 0x1243238 +#define mmSW_imm_resp_LOWER_QWORD 0x124323C +#define mmSW_imm_cmd_sts 0x1243240 +#define mmSW_BRA_BASE_ADDRESS 0x1243244 +#define mmSW_BRA_TRANSFER_SIZE 0x1243248 +#define mmSW_BRA_DMA_BUSY 0x124324C +#define mmSW_BRA_RESP 0x1243250 +#define mmSW_BRA_RESP_FRAME_ADDR 0x1243254 +#define mmSW_BRA_CURRENT_TRANSFER_SIZE 0x1243258 +#define mmSW_STATE_CHANGE_STATUS_0TO7 0x124325C +#define mmSW_STATE_CHANGE_STATUS_8TO11 0x1243260 +#define mmSW_STATE_CHANGE_STATUS_MASK_0to7 0x1243264 +#define mmSW_STATE_CHANGE_STATUS_MASK_8to11 0x1243268 +#define mmSW_CLK_FREQUENCY_CTRL 0x124326C +#define mmSW_ERROR_INTR_MASK 0x1243270 +#define mmSW_PHY_TEST_MODE_DATA_OFF 0x1243274 + + +// Registers from ACP_SW_SWCLK block + +#define mmACP_SW_EN 0x1243000 +#define mmACP_SW_EN_STATUS 0x1243004 +#define mmACP_SW_FRAMESIZE 0x1243008 +#define mmACP_SW_SSP_Counter 0x124300C +#define mmACP_SW_Audio_TX_EN 0x1243010 +#define mmACP_SW_Audio_TX_EN_STATUS 0x1243014 +#define mmACP_SW_Audio_TX_Frame_Format 0x1243018 +#define mmACP_SW_Audio_TX_SampleInterval 0x124301C +#define mmACP_SW_Audio_TX_Hctrl_DP0 0x1243020 +#define mmACP_SW_Audio_TX_Hctrl_DP1 0x1243024 +#define mmACP_SW_Audio_TX_Hctrl_DP2 0x1243028 +#define mmACP_SW_Audio_TX_Hctrl_DP3 0x124302C +#define mmACP_SW_Audio_TX_offset_DP0 0x1243030 +#define mmACP_SW_Audio_TX_offset_DP1 0x1243034 +#define mmACP_SW_Audio_TX_offset_DP2 0x1243038 +#define mmACP_SW_Audio_TX_offset_DP3 0x124303C +#define mmACP_SW_Audio_TX_Channel_Enable_DP0 0x1243040 +#define mmACP_SW_Audio_TX_Channel_Enable_DP1 0x1243044 +#define mmACP_SW_Audio_TX_Channel_Enable_DP2 0x1243048 +#define mmACP_SW_Audio_TX_Channel_Enable_DP3 0x124304C +#define mmACP_SW_BT_TX_EN 0x1243050 +#define mmACP_SW_BT_TX_EN_STATUS 0x1243054 +#define mmACP_SW_BT_TX_Frame_Format 0x1243058 +#define mmACP_SW_BT_TX_SampleInterval 0x124305C +#define mmACP_SW_BT_TX_Hctrl 0x1243060 +#define mmACP_SW_BT_TX_offset 0x1243064 +#define mmACP_SW_BT_TX_Channel_Enable_DP0 0x1243068 +#define mmACP_SW_Headset_TX_EN 0x124306C +#define mmACP_SW_Headset_TX_EN_STATUS 0x1243070 +#define mmACP_SW_Headset_TX_Frame_Format 0x1243074 +#define mmACP_SW_Headset_TX_SampleInterval 0x1243078 +#define mmACP_SW_Headset_TX_Hctrl 0x124307C +#define mmACP_SW_Headset_TX_offset 0x1243080 +#define mmACP_SW_Headset_TX_Channel_Enable_DP0 0x1243084 +#define mmACP_SW_Audio_RX_EN 0x1243088 +#define mmACP_SW_Audio_RX_EN_STATUS 0x124308C +#define mmACP_SW_Audio_RX_Frame_Format 0x1243090 +#define mmACP_SW_Audio_RX_SampleInterval 0x1243094 +#define mmACP_SW_Audio_RX_Hctrl_DP0 0x1243098 +#define mmACP_SW_Audio_RX_Hctrl_DP1 0x124309C +#define mmACP_SW_Audio_RX_Hctrl_DP2 0x1243100 +#define mmACP_SW_Audio_RX_Hctrl_DP3 0x1243104 +#define mmACP_SW_Audio_RX_offset_DP0 0x1243108 +#define mmACP_SW_Audio_RX_offset_DP1 0x124310C +#define mmACP_SW_Audio_RX_offset_DP2 0x1243110 +#define mmACP_SW_Audio_RX_offset_DP3 0x1243114 +#define mmACP_SW_Audio_RX_Channel_Enable_DP0 0x1243118 +#define mmACP_SW_Audio_RX_Channel_Enable_DP1 0x124311C +#define mmACP_SW_Audio_RX_Channel_Enable_DP2 0x1243120 +#define mmACP_SW_Audio_RX_Channel_Enable_DP3 0x1243124 +#define mmACP_SW_BT_RX_EN 0x1243128 +#define mmACP_SW_BT_RX_EN_STATUS 0x124312C +#define mmACP_SW_BT_RX_Frame_Format 0x1243130 +#define mmACP_SW_BT_RX_SampleInterval 0x1243134 +#define mmACP_SW_BT_RX_Hctrl 0x1243138 +#define mmACP_SW_BT_RX_offset 0x124313C +#define mmACP_SW_BT_RX_Channel_Enable_DP0 0x1243140 +#define mmACP_SW_Headset_RX_EN 0x1243144 +#define mmACP_SW_Headset_RX_EN_STATUS 0x1243148 +#define mmACP_SW_Headset_RX_Frame_Format 0x124314C +#define mmACP_SW_Headset_RX_SampleInterval 0x1243150 +#define mmACP_SW_Headset_RX_Hctrl 0x1243154 +#define mmACP_SW_Headset_RX_offset 0x1243158 +#define mmACP_SW_Headset_RX_Channel_Enable_DP0 0x124315C +#define mmACP_SW_BPT_PORT_EN 0x1243160 +#define mmACP_SW_BPT_PORT_EN_STATUS 0x1243164 +#define mmACP_SW_BPT_PORT_Frame_Format 0x1243168 +#define mmACP_SW_BPT_PORT_SampleInterval 0x124316C +#define mmACP_SW_BPT_PORT_Hctrl 0x1243170 +#define mmACP_SW_BPT_PORT_offset 0x1243174 +#define mmACP_SW_BPT_PORT_Channel_Enable 0x1243178 +#define mmACP_SW_BPT_PORT_First_byte_addr 0x124317C +#define mmACP_SW_CLK_RESUME_CTRL 0x1243180 +#define mmACP_SW_CLK_RESUME_Delay_Cntr 0x1243184 +#define mmACP_SW_BUS_RESET_CTRL 0x1243188 +#define mmACP_SW_PRBS_ERR_STATUS 0x124318C + + +// Registers from ACP_AUDIO_BUFFERS block + +#define mmACP_I2S_RX_RINGBUFADDR 0x1242000 +#define mmACP_I2S_RX_RINGBUFSIZE 0x1242004 +#define mmACP_I2S_RX_LINKPOSITIONCNTR 0x1242008 +#define mmACP_I2S_RX_FIFOADDR 0x124200C +#define mmACP_I2S_RX_FIFOSIZE 0x1242010 +#define mmACP_I2S_RX_DMA_SIZE 0x1242014 +#define mmACP_I2S_RX_LINEARPOSITIONCNTR_HIGH 0x1242018 +#define mmACP_I2S_RX_LINEARPOSITIONCNTR_LOW 0x124201C +#define mmACP_I2S_RX_INTR_WATERMARK_SIZE 0x1242020 +#define mmACP_I2S_TX_RINGBUFADDR 0x1242024 +#define mmACP_I2S_TX_RINGBUFSIZE 0x1242028 +#define mmACP_I2S_TX_LINKPOSITIONCNTR 0x124202C +#define mmACP_I2S_TX_FIFOADDR 0x1242030 +#define mmACP_I2S_TX_FIFOSIZE 0x1242034 +#define mmACP_I2S_TX_DMA_SIZE 0x1242038 +#define mmACP_I2S_TX_LINEARPOSITIONCNTR_HIGH 0x124203C +#define mmACP_I2S_TX_LINEARPOSITIONCNTR_LOW 0x1242040 +#define mmACP_I2S_TX_INTR_WATERMARK_SIZE 0x1242044 +#define mmACP_BT_RX_RINGBUFADDR 0x1242048 +#define mmACP_BT_RX_RINGBUFSIZE 0x124204C +#define mmACP_BT_RX_LINKPOSITIONCNTR 0x1242050 +#define mmACP_BT_RX_FIFOADDR 0x1242054 +#define mmACP_BT_RX_FIFOSIZE 0x1242058 +#define mmACP_BT_RX_DMA_SIZE 0x124205C +#define mmACP_BT_RX_LINEARPOSITIONCNTR_HIGH 0x1242060 +#define mmACP_BT_RX_LINEARPOSITIONCNTR_LOW 0x1242064 +#define mmACP_BT_RX_INTR_WATERMARK_SIZE 0x1242068 +#define mmACP_BT_TX_RINGBUFADDR 0x124206C +#define mmACP_BT_TX_RINGBUFSIZE 0x1242070 +#define mmACP_BT_TX_LINKPOSITIONCNTR 0x1242074 +#define mmACP_BT_TX_FIFOADDR 0x1242078 +#define mmACP_BT_TX_FIFOSIZE 0x124207C +#define mmACP_BT_TX_DMA_SIZE 0x1242080 +#define mmACP_BT_TX_LINEARPOSITIONCNTR_HIGH 0x1242084 +#define mmACP_BT_TX_LINEARPOSITIONCNTR_LOW 0x1242088 +#define mmACP_BT_TX_INTR_WATERMARK_SIZE 0x124208C +#define mmACP_HS_RX_RINGBUFADDR 0x1242090 +#define mmACP_HS_RX_RINGBUFSIZE 0x1242094 +#define mmACP_HS_RX_LINKPOSITIONCNTR 0x1242098 +#define mmACP_HS_RX_FIFOADDR 0x124209C +#define mmACP_HS_RX_FIFOSIZE 0x12420A0 +#define mmACP_HS_RX_DMA_SIZE 0x12420A4 +#define mmACP_HS_RX_LINEARPOSITIONCNTR_HIGH 0x12420A8 +#define mmACP_HS_RX_LINEARPOSITIONCNTR_LOW 0x12420AC +#define mmACP_HS_RX_INTR_WATERMARK_SIZE 0x12420B0 +#define mmACP_HS_TX_RINGBUFADDR 0x12420B4 +#define mmACP_HS_TX_RINGBUFSIZE 0x12420B8 +#define mmACP_HS_TX_LINKPOSITIONCNTR 0x12420BC +#define mmACP_HS_TX_FIFOADDR 0x12420C0 +#define mmACP_HS_TX_FIFOSIZE 0x12420C4 +#define mmACP_HS_TX_DMA_SIZE 0x12420C8 +#define mmACP_HS_TX_LINEARPOSITIONCNTR_HIGH 0x12420CC +#define mmACP_HS_TX_LINEARPOSITIONCNTR_LOW 0x12420D0 +#define mmACP_HS_TX_INTR_WATERMARK_SIZE 0x12420D4 + + +// Registers from ACP_I2S_TDM block + +#define mmACP_I2STDM_IER 0x1242400 +#define mmACP_I2STDM_IRER 0x1242404 +#define mmACP_I2STDM_RXFRMT 0x1242408 +#define mmACP_I2STDM_ITER 0x124240C +#define mmACP_I2STDM_TXFRMT 0x1242410 + + +// Registers from ACP_BT_TDM block + +#define mmACP_BTTDM_IER 0x1242800 +#define mmACP_BTTDM_IRER 0x1242804 +#define mmACP_BTTDM_RXFRMT 0x1242808 +#define mmACP_BTTDM_ITER 0x124280C +#define mmACP_BTTDM_TXFRMT 0x1242810 + + +// Registers from AZALIA_IP block + +#define mmAudio_Az_Global_Capabilities 0x1200000 +#define mmAudio_Az_Minor_Version 0x1200002 +#define mmAudio_Az_Major_Version 0x1200003 +#define mmAudio_Az_Output_Payload_Capability 0x1200004 +#define mmAudio_Az_Input_Payload_Capability 0x1200006 +#define mmAudio_Az_Global_Control 0x1200008 +#define mmAudio_Az_Wake_Enable 0x120000C +#define mmAudio_Az_State_Change_Status 0x120000E +#define mmAudio_Az_Global_Status 0x1200010 +#define mmAudio_Az_Linked_List_Capability_Header 0x1200014 +#define mmAudio_Az_Output_Stream_Payload_Capability 0x1200018 +#define mmAudio_Az_Input_Stream_Payload_Capability 0x120001A +#define mmAudio_Az_Interrupt_Control 0x1200020 +#define mmAudio_Az_Interrupt_Status 0x1200024 +#define mmAudio_Az_Wall_Clock_Counter 0x1200030 +#define mmAudio_Az_Stream_Synchronization 0x1200038 +#define mmAudio_Az_CORB_Lower_Base_Address 0x1200040 +#define mmAudio_Az_CORB_Upper_Base_Address 0x1200044 +#define mmAudio_Az_CORB_Write_Pointer 0x1200048 +#define mmAudio_Az_CORB_Read_Pointer 0x120004A +#define mmAudio_Az_CORB_Control 0x120004C +#define mmAudio_Az_CORB_Status 0x120004D +#define mmAudio_Az_CORB_Size 0x120004E +#define mmAudio_Az_RIRB_Lower_Base_Address 0x1200050 +#define mmAudio_Az_RIRB_Upper_Base_Address 0x1200054 +#define mmAudio_Az_RIRB_Write_Pointer 0x1200058 +#define mmAudio_Az_RIRB_Response_Interrupt_Count 0x120005A +#define mmAudio_Az_RIRB_Control 0x120005C +#define mmAudio_Az_RIRB_Status 0x120005D +#define mmAudio_Az_RIRB_Size 0x120005E +#define mmAudio_Az_Immediate_Command_Output_Interface 0x1200060 +#define mmAudio_Az_Immediate_Response_Input_Interface 0x1200064 +#define mmAudio_Az_Immediate_Command_Status 0x1200068 +#define mmAudio_Az_DPLBASE 0x1200070 +#define mmAudio_Az_DPUBASE 0x1200074 +#define mmAudio_Az_Input_SD0CTL_and_STS 0x1200080 +#define mmAudio_Az_Input_SD0LPIB 0x1200084 +#define mmAudio_Az_Input_SD0CBL 0x1200088 +#define mmAudio_Az_Input_SD0LVI 0x120008C +#define mmAudio_Az_Input_SD0FIFOS 0x1200090 +#define mmAudio_Az_Input_SD0FMT 0x1200092 +#define mmAudio_Az_Input_SD0BDPL 0x1200098 +#define mmAudio_Az_Input_SD0BDPU 0x120009C +#define mmAudio_Az_Input_SD1CTL_and_STS 0x12000A0 +#define mmAudio_Az_Input_SD1LPIB 0x12000A4 +#define mmAudio_Az_Input_SD1CBL 0x12000A8 +#define mmAudio_Az_Input_SD1LVI 0x12000AC +#define mmAudio_Az_Input_SD1FIFOS 0x12000B0 +#define mmAudio_Az_Input_SD1FMT 0x12000B2 +#define mmAudio_Az_Input_SD1BDPL 0x12000B8 +#define mmAudio_Az_Input_SD1BDPU 0x12000BC +#define mmAudio_Az_Input_SD2CTL_and_STS 0x12000C0 +#define mmAudio_Az_Input_SD2LPIB 0x12000C4 +#define mmAudio_Az_Input_SD2CBL 0x12000C8 +#define mmAudio_Az_Input_SD2LVI 0x12000CC +#define mmAudio_Az_Input_SD2FIFOS 0x12000D0 +#define mmAudio_Az_Input_SD2FMT 0x12000D2 +#define mmAudio_Az_Input_SD2BDPL 0x12000D8 +#define mmAudio_Az_Input_SD2BDPU 0x12000DC +#define mmAudio_Az_Input_SD3CTL_and_STS 0x12000E0 +#define mmAudio_Az_Input_SD3LPIB 0x12000E4 +#define mmAudio_Az_Input_SD3CBL 0x12000E8 +#define mmAudio_Az_Input_SD3LVI 0x12000EC +#define mmAudio_Az_Input_SD3FIFOS 0x12000F0 +#define mmAudio_Az_Input_SD3FMT 0x12000F2 +#define mmAudio_Az_Input_SD3BDPL 0x12000F8 +#define mmAudio_Az_Input_SD3BDPU 0x12000FC +#define mmAudio_Az_Output_SD0CTL_and_STS 0x1200100 +#define mmAudio_Az_Output_SD0LPIB 0x1200104 +#define mmAudio_Az_Output_SD0CBL 0x1200108 +#define mmAudio_Az_Output_SD0LVI 0x120010C +#define mmAudio_Az_Output_SD0FIFOS 0x1200110 +#define mmAudio_Az_Output_SD0FMT 0x1200112 +#define mmAudio_Az_Output_SD0BDPL 0x1200118 +#define mmAudio_Az_Output_SD0BDPU 0x120011C +#define mmAudio_Az_Output_SD1CTL_and_STS 0x1200120 +#define mmAudio_Az_Output_SD1LPIB 0x1200124 +#define mmAudio_Az_Output_SD1CBL 0x1200128 +#define mmAudio_Az_Output_SD1LVI 0x120012C +#define mmAudio_Az_Output_SD1FIFOS 0x1200130 +#define mmAudio_Az_Output_SD1FMT 0x1200132 +#define mmAudio_Az_Output_SD1BDPL 0x1200138 +#define mmAudio_Az_Output_SD1BDPU 0x120013C +#define mmAudio_Az_Output_SD2CTL_and_STS 0x1200140 +#define mmAudio_Az_Output_SD2LPIB 0x1200144 +#define mmAudio_Az_Output_SD2CBL 0x1200148 +#define mmAudio_Az_Output_SD2LVI 0x120014C +#define mmAudio_Az_Output_SD2FIFOS 0x1200150 +#define mmAudio_Az_Output_SD2FMT 0x1200152 +#define mmAudio_Az_Output_SD2BDPL 0x1200158 +#define mmAudio_Az_Output_SD2BDPU 0x120015C +#define mmAudio_Az_Output_SD3CTL_and_STS 0x1200160 +#define mmAudio_Az_Output_SD3LPIB 0x1200164 +#define mmAudio_Az_Output_SD3CBL 0x1200168 +#define mmAudio_Az_Output_SD3LVI 0x120016C +#define mmAudio_Az_Output_SD3FIFOS 0x1200170 +#define mmAudio_Az_Output_SD3FMT 0x1200172 +#define mmAudio_Az_Output_SD3BDPL 0x1200178 +#define mmAudio_Az_Output_SD3BDPU 0x120017C +#define mmAudioAZ_Misc_Control_Register_1 0x1200180 +#define mmAudioAZ_Misc_Control_Register_2 0x1200182 +#define mmAudioAZ_Misc_Control_Register_3 0x1200183 +#define mmAudio_AZ_Multiple_Links_Capability_Header 0x1200200 +#define mmAudio_AZ_Multiple_Links_Capability_Declaration 0x1200204 +#define mmAudio_AZ_Link0_Capabilities 0x1200240 +#define mmAudio_AZ_Link0_Control 0x1200244 +#define mmAudio_AZ_Link0_Output_Stream_ID 0x1200248 +#define mmAudio_AZ_Link0_SDI_Identifier 0x120024C +#define mmAudio_AZ_Link0_Per_Stream_Overhead 0x1200250 +#define mmAudio_AZ_Link0_Wall_Frame_Counter 0x1200258 +#define mmAudio_AZ_Link0_Output_Payload_Capability_L 0x1200260 +#define mmAudio_AZ_Link0_Output_Payload_Capability_U 0x1200264 +#define mmAudio_AZ_Link0_Input_Payload_Capability_L 0x1200270 +#define mmAudio_AZ_Link0_Input_Payload_Capability_U 0x1200274 +#define mmAudio_Az_Input_SD0LICBA 0x1202084 +#define mmAudio_Az_Input_SD1LICBA 0x12020A4 +#define mmAudio_Az_Input_SD2LICBA 0x12020C4 +#define mmAudio_Az_Input_SD3LICBA 0x12020E4 +#define mmAudio_Az_Output_SD0LICBA 0x1202104 +#define mmAudio_Az_Output_SD1LICBA 0x1202124 +#define mmAudio_Az_Output_SD2LICBA 0x1202144 +#define mmAudio_Az_Output_SD3LICBA 0x1202164 +#define mmAUDIO_AZ_POWER_MANAGEMENT_CONTROL 0x1204000 +#define mmAUDIO_AZ_IOC_SOFTRST_CONTROL 0x1204004 +#define mmAUDIO_AZ_IOC_CLKGATE_CONTROL 0x1204008 + + +// Registers from ACP_AZALIA block + +#define mmACP_AZ_PAGE0_LBASE_ADDR 0x1243800 +#define mmACP_AZ_PAGE0_UBASE_ADDR 0x1243804 +#define mmACP_AZ_PAGE0_PGEN_SIZE 0x1243808 +#define mmACP_AZ_PAGE0_OFFSET 0x124380C +#define mmACP_AZ_PAGE1_LBASE_ADDR 0x1243810 +#define mmACP_AZ_PAGE1_UBASE_ADDR 0x1243814 +#define mmACP_AZ_PAGE1_PGEN_SIZE 0x1243818 +#define mmACP_AZ_PAGE1_OFFSET 0x124381C +#define mmACP_AZ_PAGE2_LBASE_ADDR 0x1243820 +#define mmACP_AZ_PAGE2_UBASE_ADDR 0x1243824 +#define mmACP_AZ_PAGE2_PGEN_SIZE 0x1243828 +#define mmACP_AZ_PAGE2_OFFSET 0x124382C +#define mmACP_AZ_PAGE3_LBASE_ADDR 0x1243830 +#define mmACP_AZ_PAGE3_UBASE_ADDR 0x1243834 +#define mmACP_AZ_PAGE3_PGEN_SIZE 0x1243838 +#define mmACP_AZ_PAGE3_OFFSET 0x124383C +#define mmACP_AZ_PAGE4_LBASE_ADDR 0x1243840 +#define mmACP_AZ_PAGE4_UBASE_ADDR 0x1243844 +#define mmACP_AZ_PAGE4_PGEN_SIZE 0x1243848 +#define mmACP_AZ_PAGE4_OFFSET 0x124384C +#define mmACP_AZ_PAGE5_LBASE_ADDR 0x1243850 +#define mmACP_AZ_PAGE5_UBASE_ADDR 0x1243854 +#define mmACP_AZ_PAGE5_PGEN_SIZE 0x1243858 +#define mmACP_AZ_PAGE5_OFFSET 0x124385C +#define mmACP_AZ_PAGE6_LBASE_ADDR 0x1243860 +#define mmACP_AZ_PAGE6_UBASE_ADDR 0x1243864 +#define mmACP_AZ_PAGE6_PGEN_SIZE 0x1243868 +#define mmACP_AZ_PAGE6_OFFSET 0x124386C +#define mmACP_AZ_PAGE7_LBASE_ADDR 0x1243870 +#define mmACP_AZ_PAGE7_UBASE_ADDR 0x1243874 +#define mmACP_AZ_PAGE7_PGEN_SIZE 0x1243878 +#define mmACP_AZ_PAGE7_OFFSET 0x124387C + + +#endif diff --git a/sound/soc/amd/raven/pci-acp3x.c b/sound/soc/amd/raven/pci-acp3x.c new file mode 100644 index 000000000000..facec2472b34 --- /dev/null +++ b/sound/soc/amd/raven/pci-acp3x.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// AMD ACP PCI Driver +// +//Copyright 2016 Advanced Micro Devices, Inc. + +#include <linux/pci.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> + +#include "acp3x.h" + +struct acp3x_dev_data { + void __iomem *acp3x_base; + bool acp3x_audio_mode; + struct resource *res; + struct platform_device *pdev; +}; + +static int snd_acp3x_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + int ret; + u32 addr, val; + struct acp3x_dev_data *adata; + struct platform_device_info pdevinfo; + unsigned int irqflags; + + if (pci_enable_device(pci)) { + dev_err(&pci->dev, "pci_enable_device failed\n"); + return -ENODEV; + } + + ret = pci_request_regions(pci, "AMD ACP3x audio"); + if (ret < 0) { + dev_err(&pci->dev, "pci_request_regions failed\n"); + goto disable_pci; + } + + adata = devm_kzalloc(&pci->dev, sizeof(struct acp3x_dev_data), + GFP_KERNEL); + if (!adata) { + ret = -ENOMEM; + goto release_regions; + } + + /* check for msi interrupt support */ + ret = pci_enable_msi(pci); + if (ret) + /* msi is not enabled */ + irqflags = IRQF_SHARED; + else + /* msi is enabled */ + irqflags = 0; + + addr = pci_resource_start(pci, 0); + adata->acp3x_base = ioremap(addr, pci_resource_len(pci, 0)); + if (!adata->acp3x_base) { + ret = -ENOMEM; + goto release_regions; + } + pci_set_master(pci); + pci_set_drvdata(pci, adata); + + val = rv_readl(adata->acp3x_base + mmACP_I2S_PIN_CONFIG); + switch (val) { + case I2S_MODE: + adata->res = devm_kzalloc(&pci->dev, + sizeof(struct resource) * 2, + GFP_KERNEL); + if (!adata->res) { + ret = -ENOMEM; + goto unmap_mmio; + } + + adata->res[0].name = "acp3x_i2s_iomem"; + adata->res[0].flags = IORESOURCE_MEM; + adata->res[0].start = addr; + adata->res[0].end = addr + (ACP3x_REG_END - ACP3x_REG_START); + + adata->res[1].name = "acp3x_i2s_irq"; + adata->res[1].flags = IORESOURCE_IRQ; + adata->res[1].start = pci->irq; + adata->res[1].end = pci->irq; + + adata->acp3x_audio_mode = ACP3x_I2S_MODE; + + memset(&pdevinfo, 0, sizeof(pdevinfo)); + pdevinfo.name = "acp3x_rv_i2s"; + pdevinfo.id = 0; + pdevinfo.parent = &pci->dev; + pdevinfo.num_res = 2; + pdevinfo.res = adata->res; + pdevinfo.data = &irqflags; + pdevinfo.size_data = sizeof(irqflags); + + adata->pdev = platform_device_register_full(&pdevinfo); + if (IS_ERR(adata->pdev)) { + dev_err(&pci->dev, "cannot register %s device\n", + pdevinfo.name); + ret = PTR_ERR(adata->pdev); + goto unmap_mmio; + } + break; + default: + dev_err(&pci->dev, "Invalid ACP audio mode : %d\n", val); + ret = -ENODEV; + goto unmap_mmio; + } + return 0; + +unmap_mmio: + pci_disable_msi(pci); + iounmap(adata->acp3x_base); +release_regions: + pci_release_regions(pci); +disable_pci: + pci_disable_device(pci); + + return ret; +} + +static void snd_acp3x_remove(struct pci_dev *pci) +{ + struct acp3x_dev_data *adata = pci_get_drvdata(pci); + + platform_device_unregister(adata->pdev); + iounmap(adata->acp3x_base); + + pci_disable_msi(pci); + pci_release_regions(pci); + pci_disable_device(pci); +} + +static const struct pci_device_id snd_acp3x_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x15e2), + .class = PCI_CLASS_MULTIMEDIA_OTHER << 8, + .class_mask = 0xffffff }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, snd_acp3x_ids); + +static struct pci_driver acp3x_driver = { + .name = KBUILD_MODNAME, + .id_table = snd_acp3x_ids, + .probe = snd_acp3x_probe, + .remove = snd_acp3x_remove, +}; + +module_pci_driver(acp3x_driver); + +MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com"); +MODULE_DESCRIPTION("AMD ACP3x PCI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 9cc4f1848c9b..62bdb7e333b8 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -35,6 +35,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_ADAU7002 select SND_SOC_ADS117X select SND_SOC_AK4104 if SPI_MASTER + select SND_SOC_AK4118 if I2C select SND_SOC_AK4458 if I2C select SND_SOC_AK4535 if I2C select SND_SOC_AK4554 @@ -392,6 +393,11 @@ config SND_SOC_AK4104 tristate "AKM AK4104 CODEC" depends on SPI_MASTER +config SND_SOC_AK4118 + tristate "AKM AK4118 CODEC" + depends on I2C + select REGMAP_I2C + config SND_SOC_AK4458 tristate "AKM AK4458 CODEC" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 8ffab8c8dbfa..66f55d185620 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -27,6 +27,7 @@ snd-soc-adav801-objs := adav801.o snd-soc-adav803-objs := adav803.o snd-soc-ads117x-objs := ads117x.o snd-soc-ak4104-objs := ak4104.o +snd-soc-ak4118-objs := ak4118.o snd-soc-ak4458-objs := ak4458.o snd-soc-ak4535-objs := ak4535.o snd-soc-ak4554-objs := ak4554.o @@ -290,6 +291,7 @@ obj-$(CONFIG_SND_SOC_ADAV801) += snd-soc-adav801.o obj-$(CONFIG_SND_SOC_ADAV803) += snd-soc-adav803.o obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o +obj-$(CONFIG_SND_SOC_AK4118) += snd-soc-ak4118.o obj-$(CONFIG_SND_SOC_AK4458) += snd-soc-ak4458.o obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o obj-$(CONFIG_SND_SOC_AK4554) += snd-soc-ak4554.o diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c index 32bc545c19cf..6dec8a65eafc 100644 --- a/sound/soc/codecs/ak4104.c +++ b/sound/soc/codecs/ak4104.c @@ -13,7 +13,7 @@ #include <linux/slab.h> #include <linux/spi/spi.h> #include <linux/of_device.h> -#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> #include <linux/regulator/consumer.h> #include <sound/asoundef.h> #include <sound/core.h> @@ -268,8 +268,8 @@ static const struct regmap_config ak4104_regmap = { static int ak4104_spi_probe(struct spi_device *spi) { - struct device_node *np = spi->dev.of_node; struct ak4104_private *ak4104; + struct gpio_desc *reset_gpiod; unsigned int val; int ret; @@ -297,19 +297,11 @@ static int ak4104_spi_probe(struct spi_device *spi) return ret; } - if (np) { - enum of_gpio_flags flags; - int gpio = of_get_named_gpio_flags(np, "reset-gpio", 0, &flags); - - if (gpio_is_valid(gpio)) { - ret = devm_gpio_request_one(&spi->dev, gpio, - flags & OF_GPIO_ACTIVE_LOW ? - GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH, - "ak4104 reset"); - if (ret < 0) - return ret; - } - } + reset_gpiod = devm_gpiod_get_optional(&spi->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(reset_gpiod) && + PTR_ERR(reset_gpiod) == -EPROBE_DEFER) + return -EPROBE_DEFER; /* read the 'reserved' register - according to the datasheet, it * should contain 0x5b. Not a good way to verify the presence of diff --git a/sound/soc/codecs/ak4118.c b/sound/soc/codecs/ak4118.c new file mode 100644 index 000000000000..238ab29f2bf4 --- /dev/null +++ b/sound/soc/codecs/ak4118.c @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ak4118.c -- Asahi Kasei ALSA Soc Audio driver + * + * Copyright 2018 DEVIALET + */ + +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#include <sound/asoundef.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/soc.h> + +#define AK4118_REG_CLK_PWR_CTL 0x00 +#define AK4118_REG_FORMAT_CTL 0x01 +#define AK4118_REG_IO_CTL0 0x02 +#define AK4118_REG_IO_CTL1 0x03 +#define AK4118_REG_INT0_MASK 0x04 +#define AK4118_REG_INT1_MASK 0x05 +#define AK4118_REG_RCV_STATUS0 0x06 +#define AK4118_REG_RCV_STATUS1 0x07 +#define AK4118_REG_RXCHAN_STATUS0 0x08 +#define AK4118_REG_RXCHAN_STATUS1 0x09 +#define AK4118_REG_RXCHAN_STATUS2 0x0a +#define AK4118_REG_RXCHAN_STATUS3 0x0b +#define AK4118_REG_RXCHAN_STATUS4 0x0c +#define AK4118_REG_TXCHAN_STATUS0 0x0d +#define AK4118_REG_TXCHAN_STATUS1 0x0e +#define AK4118_REG_TXCHAN_STATUS2 0x0f +#define AK4118_REG_TXCHAN_STATUS3 0x10 +#define AK4118_REG_TXCHAN_STATUS4 0x11 +#define AK4118_REG_BURST_PREAMB_PC0 0x12 +#define AK4118_REG_BURST_PREAMB_PC1 0x13 +#define AK4118_REG_BURST_PREAMB_PD0 0x14 +#define AK4118_REG_BURST_PREAMB_PD1 0x15 +#define AK4118_REG_QSUB_CTL 0x16 +#define AK4118_REG_QSUB_TRACK 0x17 +#define AK4118_REG_QSUB_INDEX 0x18 +#define AK4118_REG_QSUB_MIN 0x19 +#define AK4118_REG_QSUB_SEC 0x1a +#define AK4118_REG_QSUB_FRAME 0x1b +#define AK4118_REG_QSUB_ZERO 0x1c +#define AK4118_REG_QSUB_ABS_MIN 0x1d +#define AK4118_REG_QSUB_ABS_SEC 0x1e +#define AK4118_REG_QSUB_ABS_FRAME 0x1f +#define AK4118_REG_GPE 0x20 +#define AK4118_REG_GPDR 0x21 +#define AK4118_REG_GPSCR 0x22 +#define AK4118_REG_GPLR 0x23 +#define AK4118_REG_DAT_MASK_DTS 0x24 +#define AK4118_REG_RX_DETECT 0x25 +#define AK4118_REG_STC_DAT_DETECT 0x26 +#define AK4118_REG_RXCHAN_STATUS5 0x27 +#define AK4118_REG_TXCHAN_STATUS5 0x28 +#define AK4118_REG_MAX 0x29 + +#define AK4118_REG_FORMAT_CTL_DIF0 (1 << 4) +#define AK4118_REG_FORMAT_CTL_DIF1 (1 << 5) +#define AK4118_REG_FORMAT_CTL_DIF2 (1 << 6) + +struct ak4118_priv { + struct regmap *regmap; + struct gpio_desc *reset; + struct gpio_desc *irq; + struct snd_soc_component *component; +}; + +static const struct reg_default ak4118_reg_defaults[] = { + {AK4118_REG_CLK_PWR_CTL, 0x43}, + {AK4118_REG_FORMAT_CTL, 0x6a}, + {AK4118_REG_IO_CTL0, 0x88}, + {AK4118_REG_IO_CTL1, 0x48}, + {AK4118_REG_INT0_MASK, 0xee}, + {AK4118_REG_INT1_MASK, 0xb5}, + {AK4118_REG_RCV_STATUS0, 0x00}, + {AK4118_REG_RCV_STATUS1, 0x10}, + {AK4118_REG_TXCHAN_STATUS0, 0x00}, + {AK4118_REG_TXCHAN_STATUS1, 0x00}, + {AK4118_REG_TXCHAN_STATUS2, 0x00}, + {AK4118_REG_TXCHAN_STATUS3, 0x00}, + {AK4118_REG_TXCHAN_STATUS4, 0x00}, + {AK4118_REG_GPE, 0x77}, + {AK4118_REG_GPDR, 0x00}, + {AK4118_REG_GPSCR, 0x00}, + {AK4118_REG_GPLR, 0x00}, + {AK4118_REG_DAT_MASK_DTS, 0x3f}, + {AK4118_REG_RX_DETECT, 0x00}, + {AK4118_REG_STC_DAT_DETECT, 0x00}, + {AK4118_REG_TXCHAN_STATUS5, 0x00}, +}; + +static const char * const ak4118_input_select_txt[] = { + "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7", +}; +static SOC_ENUM_SINGLE_DECL(ak4118_insel_enum, AK4118_REG_IO_CTL1, 0x0, + ak4118_input_select_txt); + +static const struct snd_kcontrol_new ak4118_input_mux_controls = + SOC_DAPM_ENUM("Input Select", ak4118_insel_enum); + +static const char * const ak4118_iec958_fs_txt[] = { + "44100", "48000", "32000", "22050", "11025", "24000", "16000", "88200", + "8000", "96000", "64000", "176400", "192000", +}; + +static const int ak4118_iec958_fs_val[] = { + 0x0, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xE, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(ak4118_iec958_fs_enum, AK4118_REG_RCV_STATUS1, + 0x4, 0x4, ak4118_iec958_fs_txt, + ak4118_iec958_fs_val); + +static struct snd_kcontrol_new ak4118_iec958_controls[] = { + SOC_SINGLE("IEC958 Parity Errors", AK4118_REG_RCV_STATUS0, 0, 1, 0), + SOC_SINGLE("IEC958 No Audio", AK4118_REG_RCV_STATUS0, 1, 1, 0), + SOC_SINGLE("IEC958 PLL Lock", AK4118_REG_RCV_STATUS0, 4, 1, 1), + SOC_SINGLE("IEC958 Non PCM", AK4118_REG_RCV_STATUS0, 6, 1, 0), + SOC_ENUM("IEC958 Sampling Freq", ak4118_iec958_fs_enum), +}; + +static const struct snd_soc_dapm_widget ak4118_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("INRX0"), + SND_SOC_DAPM_INPUT("INRX1"), + SND_SOC_DAPM_INPUT("INRX2"), + SND_SOC_DAPM_INPUT("INRX3"), + SND_SOC_DAPM_INPUT("INRX4"), + SND_SOC_DAPM_INPUT("INRX5"), + SND_SOC_DAPM_INPUT("INRX6"), + SND_SOC_DAPM_INPUT("INRX7"), + SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, + &ak4118_input_mux_controls), +}; + +static const struct snd_soc_dapm_route ak4118_dapm_routes[] = { + {"Input Mux", "RX0", "INRX0"}, + {"Input Mux", "RX1", "INRX1"}, + {"Input Mux", "RX2", "INRX2"}, + {"Input Mux", "RX3", "INRX3"}, + {"Input Mux", "RX4", "INRX4"}, + {"Input Mux", "RX5", "INRX5"}, + {"Input Mux", "RX6", "INRX6"}, + {"Input Mux", "RX7", "INRX7"}, +}; + + +static int ak4118_set_dai_fmt_master(struct ak4118_priv *ak4118, + unsigned int format) +{ + int dif; + + switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + dif = AK4118_REG_FORMAT_CTL_DIF0 | AK4118_REG_FORMAT_CTL_DIF2; + break; + case SND_SOC_DAIFMT_RIGHT_J: + dif = AK4118_REG_FORMAT_CTL_DIF0 | AK4118_REG_FORMAT_CTL_DIF1; + break; + case SND_SOC_DAIFMT_LEFT_J: + dif = AK4118_REG_FORMAT_CTL_DIF2; + break; + default: + return -ENOTSUPP; + } + + return dif; +} + +static int ak4118_set_dai_fmt_slave(struct ak4118_priv *ak4118, + unsigned int format) +{ + int dif; + + switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + dif = AK4118_REG_FORMAT_CTL_DIF0 | AK4118_REG_FORMAT_CTL_DIF1 | + AK4118_REG_FORMAT_CTL_DIF2; + break; + case SND_SOC_DAIFMT_LEFT_J: + dif = AK4118_REG_FORMAT_CTL_DIF1 | AK4118_REG_FORMAT_CTL_DIF2; + break; + default: + return -ENOTSUPP; + } + + return dif; +} + +static int ak4118_set_dai_fmt(struct snd_soc_dai *dai, + unsigned int format) +{ + struct snd_soc_component *component = dai->component; + struct ak4118_priv *ak4118 = snd_soc_component_get_drvdata(component); + int dif; + int ret = 0; + + switch (format & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + /* component is master */ + dif = ak4118_set_dai_fmt_master(ak4118, format); + break; + case SND_SOC_DAIFMT_CBS_CFS: + /*component is slave */ + dif = ak4118_set_dai_fmt_slave(ak4118, format); + break; + default: + ret = -ENOTSUPP; + goto exit; + } + + /* format not supported */ + if (dif < 0) { + ret = dif; + goto exit; + } + + ret = regmap_update_bits(ak4118->regmap, AK4118_REG_FORMAT_CTL, + AK4118_REG_FORMAT_CTL_DIF0 | + AK4118_REG_FORMAT_CTL_DIF1 | + AK4118_REG_FORMAT_CTL_DIF2, dif); + if (ret < 0) + goto exit; + +exit: + return ret; +} + +static int ak4118_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + return 0; +} + +static const struct snd_soc_dai_ops ak4118_dai_ops = { + .hw_params = ak4118_hw_params, + .set_fmt = ak4118_set_dai_fmt, +}; + +static struct snd_soc_dai_driver ak4118_dai = { + .name = "ak4118-hifi", + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .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 + }, + .ops = &ak4118_dai_ops, +}; + +static irqreturn_t ak4118_irq_handler(int irq, void *data) +{ + struct ak4118_priv *ak4118 = data; + struct snd_soc_component *component = ak4118->component; + struct snd_kcontrol_new *kctl_new; + struct snd_kcontrol *kctl; + struct snd_ctl_elem_id *id; + unsigned int i; + + if (!component) + return IRQ_NONE; + + for (i = 0; i < ARRAY_SIZE(ak4118_iec958_controls); i++) { + kctl_new = &ak4118_iec958_controls[i]; + kctl = snd_soc_card_get_kcontrol(component->card, + kctl_new->name); + if (!kctl) + continue; + id = &kctl->id; + snd_ctl_notify(component->card->snd_card, + SNDRV_CTL_EVENT_MASK_VALUE, id); + } + + return IRQ_HANDLED; +} + +static int ak4118_probe(struct snd_soc_component *component) +{ + struct ak4118_priv *ak4118 = snd_soc_component_get_drvdata(component); + int ret = 0; + + ak4118->component = component; + + /* release reset */ + gpiod_set_value(ak4118->reset, 0); + + /* unmask all int1 sources */ + ret = regmap_write(ak4118->regmap, AK4118_REG_INT1_MASK, 0x00); + if (ret < 0) { + dev_err(component->dev, + "failed to write regmap 0x%x 0x%x: %d\n", + AK4118_REG_INT1_MASK, 0x00, ret); + return ret; + } + + /* rx detect enable on all channels */ + ret = regmap_write(ak4118->regmap, AK4118_REG_RX_DETECT, 0xff); + if (ret < 0) { + dev_err(component->dev, + "failed to write regmap 0x%x 0x%x: %d\n", + AK4118_REG_RX_DETECT, 0xff, ret); + return ret; + } + + ret = snd_soc_add_component_controls(component, ak4118_iec958_controls, + ARRAY_SIZE(ak4118_iec958_controls)); + if (ret) { + dev_err(component->dev, + "failed to add component kcontrols: %d\n", ret); + return ret; + } + + return 0; +} + +static void ak4118_remove(struct snd_soc_component *component) +{ + struct ak4118_priv *ak4118 = snd_soc_component_get_drvdata(component); + + /* hold reset */ + gpiod_set_value(ak4118->reset, 1); +} + +static const struct snd_soc_component_driver soc_component_drv_ak4118 = { + .probe = ak4118_probe, + .remove = ak4118_remove, + .dapm_widgets = ak4118_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4118_dapm_widgets), + .dapm_routes = ak4118_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ak4118_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static const struct regmap_config ak4118_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .reg_defaults = ak4118_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ak4118_reg_defaults), + + .cache_type = REGCACHE_NONE, + .max_register = AK4118_REG_MAX - 1, +}; + +static int ak4118_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct ak4118_priv *ak4118; + int ret; + + ak4118 = devm_kzalloc(&i2c->dev, sizeof(struct ak4118_priv), + GFP_KERNEL); + if (ak4118 == NULL) + return -ENOMEM; + + ak4118->regmap = devm_regmap_init_i2c(i2c, &ak4118_regmap); + if (IS_ERR(ak4118->regmap)) + return PTR_ERR(ak4118->regmap); + + i2c_set_clientdata(i2c, ak4118); + + ak4118->reset = devm_gpiod_get(&i2c->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(ak4118->reset)) { + ret = PTR_ERR(ak4118->reset); + if (ret != -EPROBE_DEFER) + dev_err(&i2c->dev, "Failed to get reset: %d\n", ret); + return ret; + } + + ak4118->irq = devm_gpiod_get(&i2c->dev, "irq", GPIOD_IN); + if (IS_ERR(ak4118->irq)) { + ret = PTR_ERR(ak4118->irq); + if (ret != -EPROBE_DEFER) + dev_err(&i2c->dev, "Failed to get IRQ: %d\n", ret); + return ret; + } + + ret = devm_request_threaded_irq(&i2c->dev, gpiod_to_irq(ak4118->irq), + NULL, ak4118_irq_handler, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "ak4118-irq", ak4118); + if (ret < 0) { + dev_err(&i2c->dev, "Fail to request_irq: %d\n", ret); + return ret; + } + + return snd_soc_register_component(&i2c->dev, &soc_component_drv_ak4118, + &ak4118_dai, 1); +} + +static int ak4118_i2c_remove(struct i2c_client *i2c) +{ + snd_soc_unregister_component(&i2c->dev); + return 0; +} + +static const struct of_device_id ak4118_of_match[] = { + { .compatible = "asahi-kasei,ak4118", }, + {} +}; +MODULE_DEVICE_TABLE(of, ak4118_of_match); + +static const struct i2c_device_id ak4118_id_table[] = { + { "ak4118", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, ak4118_id_table); + +static struct i2c_driver ak4118_i2c_driver = { + .driver = { + .name = "ak4118", + .of_match_table = of_match_ptr(ak4118_of_match), + }, + .id_table = ak4118_id_table, + .probe = ak4118_i2c_probe, + .remove = ak4118_i2c_remove, +}; + +module_i2c_driver(ak4118_i2c_driver); + +MODULE_DESCRIPTION("Asahi Kasei AK4118 ALSA SoC driver"); +MODULE_AUTHOR("Adrien Charruel <adrien.charruel@devialet.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c index 299ada4dfaa0..70d4c89bd6fc 100644 --- a/sound/soc/codecs/ak4458.c +++ b/sound/soc/codecs/ak4458.c @@ -456,7 +456,7 @@ static int ak4458_startup(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_dai_ops ak4458_dai_ops = { +static const struct snd_soc_dai_ops ak4458_dai_ops = { .startup = ak4458_startup, .hw_params = ak4458_hw_params, .set_fmt = ak4458_set_dai_fmt, diff --git a/sound/soc/codecs/ak5558.c b/sound/soc/codecs/ak5558.c index 448bb90c9c8e..8179512129d3 100644 --- a/sound/soc/codecs/ak5558.c +++ b/sound/soc/codecs/ak5558.c @@ -130,16 +130,12 @@ static int ak5558_hw_params(struct snd_pcm_substream *substream, u8 bits; int pcm_width = max(params_physical_width(params), ak5558->slot_width); - /* set master/slave audio interface */ - bits = snd_soc_component_read32(component, AK5558_02_CONTROL1); - bits &= ~AK5558_BITS; - switch (pcm_width) { case 16: - bits |= AK5558_DIF_24BIT_MODE; + bits = AK5558_DIF_24BIT_MODE; break; case 32: - bits |= AK5558_DIF_32BIT_MODE; + bits = AK5558_DIF_32BIT_MODE; break; default: return -EINVAL; @@ -168,18 +164,15 @@ static int ak5558_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) } /* set master/slave audio interface */ - format = snd_soc_component_read32(component, AK5558_02_CONTROL1); - format &= ~AK5558_DIF; - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: - format |= AK5558_DIF_I2S_MODE; + format = AK5558_DIF_I2S_MODE; break; case SND_SOC_DAIFMT_LEFT_J: - format |= AK5558_DIF_MSB_MODE; + format = AK5558_DIF_MSB_MODE; break; case SND_SOC_DAIFMT_DSP_B: - format |= AK5558_DIF_MSB_MODE; + format = AK5558_DIF_MSB_MODE; break; default: return -EINVAL; @@ -246,7 +239,7 @@ static int ak5558_startup(struct snd_pcm_substream *substream, &ak5558_rate_constraints); } -static struct snd_soc_dai_ops ak5558_dai_ops = { +static const struct snd_soc_dai_ops ak5558_dai_ops = { .startup = ak5558_startup, .hw_params = ak5558_hw_params, diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 3c266eeb89bf..33d74f163bd7 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -29,8 +29,8 @@ #include <linux/i2c.h> #include <linux/delay.h> #include <linux/regulator/consumer.h> +#include <linux/gpio/consumer.h> #include <linux/of_device.h> -#include <linux/of_gpio.h> /* * The codec isn't really big-endian or little-endian, since the I2S @@ -658,8 +658,8 @@ static const struct regmap_config cs4270_regmap = { static int cs4270_i2c_probe(struct i2c_client *i2c_client, const struct i2c_device_id *id) { - struct device_node *np = i2c_client->dev.of_node; struct cs4270_private *cs4270; + struct gpio_desc *reset_gpiod; unsigned int val; int ret, i; @@ -678,20 +678,11 @@ static int cs4270_i2c_probe(struct i2c_client *i2c_client, if (ret < 0) return ret; - /* See if we have a way to bring the codec out of reset */ - if (np) { - enum of_gpio_flags flags; - int gpio = of_get_named_gpio_flags(np, "reset-gpio", 0, &flags); - - if (gpio_is_valid(gpio)) { - ret = devm_gpio_request_one(&i2c_client->dev, gpio, - flags & OF_GPIO_ACTIVE_LOW ? - GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH, - "cs4270 reset"); - if (ret < 0) - return ret; - } - } + reset_gpiod = devm_gpiod_get_optional(&i2c_client->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(reset_gpiod) && + PTR_ERR(reset_gpiod) == -EPROBE_DEFER) + return -EPROBE_DEFER; cs4270->regmap = devm_regmap_init_i2c(i2c_client, &cs4270_regmap); if (IS_ERR(cs4270->regmap)) diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c index 71322e0410ee..da921da50ef0 100644 --- a/sound/soc/codecs/dmic.c +++ b/sound/soc/codecs/dmic.c @@ -30,9 +30,39 @@ #include <sound/soc.h> #include <sound/soc-dapm.h> +#define MAX_MODESWITCH_DELAY 70 +static int modeswitch_delay; +module_param(modeswitch_delay, uint, 0644); + +static int wakeup_delay; +module_param(wakeup_delay, uint, 0644); + struct dmic { struct gpio_desc *gpio_en; int wakeup_delay; + /* Delay after DMIC mode switch */ + int modeswitch_delay; +}; + +int dmic_daiops_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct dmic *dmic = snd_soc_component_get_drvdata(component); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_STOP: + if (dmic->modeswitch_delay) + mdelay(dmic->modeswitch_delay); + + break; + } + + return 0; +} + +static const struct snd_soc_dai_ops dmic_dai_ops = { + .trigger = dmic_daiops_trigger, }; static int dmic_aif_event(struct snd_soc_dapm_widget *w, @@ -68,6 +98,7 @@ static struct snd_soc_dai_driver dmic_dai = { | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE, }, + .ops = &dmic_dai_ops, }; static int dmic_component_probe(struct snd_soc_component *component) @@ -85,6 +116,15 @@ static int dmic_component_probe(struct snd_soc_component *component) device_property_read_u32(component->dev, "wakeup-delay-ms", &dmic->wakeup_delay); + device_property_read_u32(component->dev, "modeswitch-delay-ms", + &dmic->modeswitch_delay); + if (wakeup_delay) + dmic->wakeup_delay = wakeup_delay; + if (modeswitch_delay) + dmic->modeswitch_delay = modeswitch_delay; + + if (dmic->modeswitch_delay > MAX_MODESWITCH_DELAY) + dmic->modeswitch_delay = MAX_MODESWITCH_DELAY; snd_soc_component_set_drvdata(component, dmic); diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index 2aaa83028e55..ffecdaaa8cf2 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -46,7 +46,7 @@ static int hdac_hda_dai_set_tdm_slot(struct snd_soc_dai *dai, static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt, struct snd_soc_dai *dai); -static struct snd_soc_dai_ops hdac_hda_dai_ops = { +static const struct snd_soc_dai_ops hdac_hda_dai_ops = { .startup = hdac_hda_dai_open, .shutdown = hdac_hda_dai_close, .prepare = hdac_hda_dai_prepare, diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index e63d6e33df48..3ab2949c1dfa 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -121,8 +121,16 @@ struct hdac_hdmi_dai_port_map { struct hdac_hdmi_cvt *cvt; }; +/* + * pin to port mapping table where the value indicate the pin number and + * the index indicate the port number with 1 base. + */ +static const int icl_pin2port_map[] = {0x4, 0x6, 0x8, 0xa, 0xb}; + struct hdac_hdmi_drv_data { unsigned int vendor_nid; + const int *port_map; /* pin to port mapping table */ + int port_num; }; struct hdac_hdmi_priv { @@ -1329,11 +1337,12 @@ static int hdac_hdmi_add_pin(struct hdac_device *hdev, hda_nid_t nid) return 0; } -#define INTEL_VENDOR_NID 0x08 -#define INTEL_GLK_VENDOR_NID 0x0b +#define INTEL_VENDOR_NID_0x2 0x02 +#define INTEL_VENDOR_NID_0x8 0x08 +#define INTEL_VENDOR_NID_0xb 0x0b #define INTEL_GET_VENDOR_VERB 0xf81 #define INTEL_SET_VENDOR_VERB 0x781 -#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */ +#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */ #define INTEL_EN_ALL_PIN_CVTS 0x01 /* enable 2nd & 3rd pins and convertors */ static void hdac_hdmi_skl_enable_all_pins(struct hdac_device *hdev) @@ -1538,7 +1547,26 @@ free_widgets: static int hdac_hdmi_pin2port(void *aptr, int pin) { - return pin - 4; /* map NID 0x05 -> port #1 */ + struct hdac_device *hdev = aptr; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + const int *map = hdmi->drv_data->port_map; + int i; + + if (!hdmi->drv_data->port_num) + return pin - 4; /* map NID 0x05 -> port #1 */ + + /* + * looking for the pin number in the mapping table and return + * the index which indicate the port number + */ + for (i = 0; i < hdmi->drv_data->port_num; i++) { + if (pin == map[i]) + return i + 1; + } + + /* return -1 if pin number exceeds our expectation */ + dev_err(&hdev->dev, "Can't find the port for pin %d\n", pin); + return -1; } static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe) @@ -1549,9 +1577,18 @@ static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe) struct hdac_hdmi_port *hport = NULL; struct snd_soc_component *component = hdmi->component; int i; - - /* Don't know how this mapping is derived */ - hda_nid_t pin_nid = port + 0x04; + hda_nid_t pin_nid; + + if (!hdmi->drv_data->port_num) { + /* for legacy platforms */ + pin_nid = port + 0x04; + } else if (port < hdmi->drv_data->port_num) { + /* get pin number from the pin2port mapping table */ + pin_nid = hdmi->drv_data->port_map[port - 1]; + } else { + dev_err(&hdev->dev, "Can't find the pin for port %d\n", port); + return; + } dev_dbg(&hdev->dev, "%s: for pin:%d port=%d\n", __func__, pin_nid, pipe); @@ -1973,12 +2010,18 @@ static int hdac_hdmi_get_spk_alloc(struct hdac_device *hdev, int pcm_idx) return port->eld.info.spk_alloc; } +static struct hdac_hdmi_drv_data intel_icl_drv_data = { + .vendor_nid = INTEL_VENDOR_NID_0x2, + .port_map = icl_pin2port_map, + .port_num = ARRAY_SIZE(icl_pin2port_map), +}; + static struct hdac_hdmi_drv_data intel_glk_drv_data = { - .vendor_nid = INTEL_GLK_VENDOR_NID, + .vendor_nid = INTEL_VENDOR_NID_0xb, }; static struct hdac_hdmi_drv_data intel_drv_data = { - .vendor_nid = INTEL_VENDOR_NID, + .vendor_nid = INTEL_VENDOR_NID_0x8, }; static int hdac_hdmi_dev_probe(struct hdac_device *hdev) @@ -2031,13 +2074,7 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) * Turned off in the runtime_suspend during the first explicit * pm_runtime_suspend call. */ - ret = snd_hdac_display_power(hdev->bus, true); - if (ret < 0) { - dev_err(&hdev->dev, - "Cannot turn on display power on i915 err: %d\n", - ret); - return ret; - } + snd_hdac_display_power(hdev->bus, hdev->addr, true); ret = hdac_hdmi_parse_and_map_nid(hdev, &hdmi_dais, &num_dais); if (ret < 0) { @@ -2065,6 +2102,8 @@ static int hdac_hdmi_dev_remove(struct hdac_device *hdev) struct hdac_hdmi_port *port, *port_next; int i; + snd_hdac_display_power(hdev->bus, hdev->addr, false); + list_for_each_entry_safe(pcm, pcm_next, &hdmi->pcm_list, head) { pcm->cvt = NULL; if (list_empty(&pcm->port_list)) @@ -2170,7 +2209,6 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) struct hdac_device *hdev = dev_to_hdac_dev(dev); struct hdac_bus *bus = hdev->bus; struct hdac_ext_link *hlink = NULL; - int err; dev_dbg(dev, "Enter: %s\n", __func__); @@ -2196,11 +2234,9 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) snd_hdac_ext_bus_link_put(bus, hlink); - err = snd_hdac_display_power(bus, false); - if (err < 0) - dev_err(dev, "Cannot turn off display power on i915\n"); + snd_hdac_display_power(bus, hdev->addr, false); - return err; + return 0; } static int hdac_hdmi_runtime_resume(struct device *dev) @@ -2208,7 +2244,6 @@ static int hdac_hdmi_runtime_resume(struct device *dev) struct hdac_device *hdev = dev_to_hdac_dev(dev); struct hdac_bus *bus = hdev->bus; struct hdac_ext_link *hlink = NULL; - int err; dev_dbg(dev, "Enter: %s\n", __func__); @@ -2224,11 +2259,7 @@ static int hdac_hdmi_runtime_resume(struct device *dev) snd_hdac_ext_bus_link_get(bus, hlink); - err = snd_hdac_display_power(bus, true); - if (err < 0) { - dev_err(dev, "Cannot turn on display power on i915\n"); - return err; - } + snd_hdac_display_power(bus, hdev->addr, true); hdac_hdmi_skl_enable_all_pins(hdev); hdac_hdmi_skl_enable_dp12(hdev); @@ -2258,6 +2289,8 @@ static const struct hda_device_id hdmi_list[] = { &intel_glk_drv_data), HDA_CODEC_EXT_ENTRY(0x8086280d, 0x100000, "Geminilake HDMI", &intel_glk_drv_data), + HDA_CODEC_EXT_ENTRY(0x8086280f, 0x100000, "Icelake HDMI", + &intel_icl_drv_data), {} }; diff --git a/sound/soc/codecs/max98373.c b/sound/soc/codecs/max98373.c index a09d01318f79..9c8616a7b61c 100644 --- a/sound/soc/codecs/max98373.c +++ b/sound/soc/codecs/max98373.c @@ -724,14 +724,39 @@ static struct snd_soc_dai_driver max98373_dai[] = { } }; +static void max98373_reset(struct max98373_priv *max98373, struct device *dev) +{ + int ret, reg, count; + + /* Software Reset */ + ret = regmap_update_bits(max98373->regmap, + MAX98373_R2000_SW_RESET, + MAX98373_SOFT_RESET, + MAX98373_SOFT_RESET); + if (ret) + dev_err(dev, "Reset command failed. (ret:%d)\n", ret); + + count = 0; + while (count < 3) { + usleep_range(10000, 11000); + /* Software Reset Verification */ + ret = regmap_read(max98373->regmap, + MAX98373_R21FF_REV_ID, ®); + if (!ret) { + dev_info(dev, "Reset completed (retry:%d)\n", count); + return; + } + count++; + } + dev_err(dev, "Reset failed. (ret:%d)\n", ret); +} + static int max98373_probe(struct snd_soc_component *component) { struct max98373_priv *max98373 = snd_soc_component_get_drvdata(component); /* Software Reset */ - regmap_write(max98373->regmap, - MAX98373_R2000_SW_RESET, MAX98373_SOFT_RESET); - usleep_range(10000, 11000); + max98373_reset(max98373, component->dev); /* IV default slot configuration */ regmap_write(max98373->regmap, @@ -818,9 +843,7 @@ static int max98373_resume(struct device *dev) { struct max98373_priv *max98373 = dev_get_drvdata(dev); - regmap_write(max98373->regmap, - MAX98373_R2000_SW_RESET, MAX98373_SOFT_RESET); - usleep_range(10000, 11000); + max98373_reset(max98373, dev); regcache_cache_only(max98373->regmap, false); regcache_sync(max98373->regmap); return 0; diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c index 4ea3287162ad..8600c5439e1e 100644 --- a/sound/soc/codecs/max9867.c +++ b/sound/soc/codecs/max9867.c @@ -1,12 +1,10 @@ -/* - * max9867.c -- max9867 ALSA SoC Audio driver - * - * Copyright 2013-15 Maxim Integrated Products - * - * 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. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// MAX9867 ALSA SoC codec driver +// +// Copyright 2013-2015 Maxim Integrated Products +// Copyright 2018 Ladislav Michl <ladis@linux-mips.org> +// #include <linux/delay.h> #include <linux/i2c.h> @@ -23,254 +21,237 @@ static const char *const max9867_spmode[] = { "Stereo Single", "Mono Single", "Stereo Single Fast", "Mono Single Fast" }; -static const char *const max9867_sidetone_text[] = { - "None", "Left", "Right", "LeftRight", "LeftRightDiv2", -}; static const char *const max9867_filter_text[] = {"IIR", "FIR"}; static SOC_ENUM_SINGLE_DECL(max9867_filter, MAX9867_CODECFLTR, 7, max9867_filter_text); static SOC_ENUM_SINGLE_DECL(max9867_spkmode, MAX9867_MODECONFIG, 0, max9867_spmode); -static SOC_ENUM_SINGLE_DECL(max9867_sidetone, MAX9867_DACGAIN, 6, - max9867_sidetone_text); -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 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 SNDRV_CTL_TLVD_DECLARE_DB_RANGE(max9867_master_tlv, + 0, 2, TLV_DB_SCALE_ITEM(-8600, 200, 1), + 3, 17, TLV_DB_SCALE_ITEM(-7800, 400, 0), + 18, 25, TLV_DB_SCALE_ITEM(-2000, 200, 0), + 26, 34, TLV_DB_SCALE_ITEM( -500, 100, 0), + 35, 40, TLV_DB_SCALE_ITEM( 350, 50, 0), +); +static DECLARE_TLV_DB_SCALE(max9867_mic_tlv, 0, 100, 0); +static DECLARE_TLV_DB_SCALE(max9867_line_tlv, -600, 200, 0); +static DECLARE_TLV_DB_SCALE(max9867_adc_tlv, -1200, 100, 0); +static DECLARE_TLV_DB_SCALE(max9867_dac_tlv, -1500, 100, 0); +static DECLARE_TLV_DB_SCALE(max9867_dacboost_tlv, 0, 600, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(max9867_micboost_tlv, + 0, 2, TLV_DB_SCALE_ITEM(-2000, 2000, 1), + 3, 3, TLV_DB_SCALE_ITEM(3000, 0, 0), ); static const struct snd_kcontrol_new max9867_snd_controls[] = { - SOC_DOUBLE_R("Master Playback Volume", MAX9867_LEFTVOL, - MAX9867_RIGHTVOL, 0, 63, 1), - SOC_DOUBLE_R_TLV("Capture Volume", MAX9867_LEFTMICGAIN, - MAX9867_RIGHTMICGAIN, - 0, 15, 1, max9860_capture_tlv), - SOC_DOUBLE_R_TLV("Mic Volume", MAX9867_LEFTMICGAIN, - MAX9867_RIGHTMICGAIN, 0, 31, 1, max9860_mic_tlv), - SOC_DOUBLE_R_TLV("Mic Boost Volume", MAX9867_LEFTMICGAIN, - MAX9867_RIGHTMICGAIN, 5, 3, 0, max98088_micboost_tlv), - SOC_ENUM("Digital Sidetone Src", max9867_sidetone), - SOC_SINGLE("Sidetone Volume", MAX9867_DACGAIN, 0, 31, 1), - SOC_SINGLE("DAC Volume", MAX9867_DACLEVEL, 4, 3, 0), - SOC_SINGLE("DAC Attenuation", MAX9867_DACLEVEL, 0, 15, 1), - SOC_SINGLE_TLV("ADC Left Volume", MAX9867_ADCLEVEL, - 4, 15, 1, max9860_adc_left_tlv), - SOC_SINGLE_TLV("ADC Right Volume", MAX9867_ADCLEVEL, - 0, 15, 1, max9860_adc_right_tlv), + SOC_DOUBLE_R_TLV("Master Playback Volume", MAX9867_LEFTVOL, + MAX9867_RIGHTVOL, 0, 41, 1, max9867_master_tlv), + SOC_DOUBLE_R_TLV("Line Capture Volume", MAX9867_LEFTLINELVL, + MAX9867_RIGHTLINELVL, 0, 15, 1, max9867_line_tlv), + SOC_DOUBLE_R_TLV("Mic Capture Volume", MAX9867_LEFTMICGAIN, + MAX9867_RIGHTMICGAIN, 0, 20, 1, max9867_mic_tlv), + SOC_DOUBLE_R_TLV("Mic Boost Capture Volume", MAX9867_LEFTMICGAIN, + MAX9867_RIGHTMICGAIN, 5, 4, 0, max9867_micboost_tlv), + SOC_SINGLE("Digital Sidetone Volume", MAX9867_SIDETONE, 0, 31, 1), + SOC_SINGLE_TLV("Digital Playback Volume", MAX9867_DACLEVEL, 0, 15, 1, + max9867_dac_tlv), + SOC_SINGLE_TLV("Digital Boost Playback Volume", MAX9867_DACLEVEL, 4, 3, 0, + max9867_dacboost_tlv), + SOC_DOUBLE_TLV("Digital Capture Volume", MAX9867_ADCLEVEL, 0, 4, 15, 1, + max9867_adc_tlv), SOC_ENUM("Speaker Mode", max9867_spkmode), SOC_SINGLE("Volume Smoothing Switch", MAX9867_MODECONFIG, 6, 1, 0), - SOC_SINGLE("ZCD Switch", MAX9867_MODECONFIG, 5, 1, 0), + SOC_SINGLE("Line ZC Switch", MAX9867_MODECONFIG, 5, 1, 0), SOC_ENUM("DSP Filter", max9867_filter), }; -static const char *const max9867_mux[] = {"None", "Mic", "Line", "Mic_Line"}; +/* Input mixer */ +static const struct snd_kcontrol_new max9867_input_mixer_controls[] = { + SOC_DAPM_DOUBLE("Line Capture Switch", MAX9867_INPUTCONFIG, 7, 5, 1, 0), + SOC_DAPM_DOUBLE("Mic Capture Switch", MAX9867_INPUTCONFIG, 6, 4, 1, 0), +}; + +/* Output mixer */ +static const struct snd_kcontrol_new max9867_output_mixer_controls[] = { + SOC_DAPM_DOUBLE_R("Line Bypass Switch", + MAX9867_LEFTLINELVL, MAX9867_RIGHTLINELVL, 6, 1, 1), +}; -static SOC_ENUM_SINGLE_DECL(max9867_mux_enum, - MAX9867_INPUTCONFIG, MAX9867_INPUT_SHIFT, - max9867_mux); +/* Sidetone mixer */ +static const struct snd_kcontrol_new max9867_sidetone_mixer_controls[] = { + SOC_DAPM_DOUBLE("Sidetone Switch", MAX9867_SIDETONE, 6, 7, 1, 0), +}; -static const struct snd_kcontrol_new max9867_dapm_mux_controls = - SOC_DAPM_ENUM("Route", max9867_mux_enum); +/* Line out switch */ +static const struct snd_kcontrol_new max9867_line_out_control = + SOC_DAPM_DOUBLE_R("Switch", + MAX9867_LEFTVOL, MAX9867_RIGHTVOL, 6, 1, 1); -static const struct snd_kcontrol_new max9867_left_dapm_control = - SOC_DAPM_SINGLE("Switch", MAX9867_PWRMAN, 6, 1, 0); -static const struct snd_kcontrol_new max9867_right_dapm_control = - SOC_DAPM_SINGLE("Switch", MAX9867_PWRMAN, 5, 1, 0); -static const struct snd_kcontrol_new max9867_line_dapm_control = - SOC_DAPM_SINGLE("Switch", MAX9867_LEFTLINELVL, 6, 1, 1); static const struct snd_soc_dapm_widget max9867_dapm_widgets[] = { - SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0), - SND_SOC_DAPM_DAC("Left DAC", NULL, MAX9867_PWRMAN, 3, 0), - SND_SOC_DAPM_DAC("Right DAC", NULL, MAX9867_PWRMAN, 2, 0), - SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_OUTPUT("HPOUT"), - - SND_SOC_DAPM_AIF_IN("DAI_IN", "HiFi Capture", 0, SND_SOC_NOPM, 0, 0), - SND_SOC_DAPM_ADC("Left ADC", "HiFi Capture", MAX9867_PWRMAN, 1, 0), - SND_SOC_DAPM_ADC("Right ADC", "HiFi Capture", MAX9867_PWRMAN, 0, 0), - SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, - &max9867_dapm_mux_controls), - - SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_SWITCH("Left Line", MAX9867_LEFTLINELVL, 6, 1, - &max9867_left_dapm_control), - SND_SOC_DAPM_SWITCH("Right Line", MAX9867_RIGTHLINELVL, 6, 1, - &max9867_right_dapm_control), - SND_SOC_DAPM_SWITCH("Line Mixer", SND_SOC_NOPM, 0, 0, - &max9867_line_dapm_control), - SND_SOC_DAPM_INPUT("LINE_IN"), + SND_SOC_DAPM_INPUT("MICL"), + SND_SOC_DAPM_INPUT("MICR"), + SND_SOC_DAPM_INPUT("LINL"), + SND_SOC_DAPM_INPUT("LINR"), + + SND_SOC_DAPM_PGA("Left Line Input", MAX9867_PWRMAN, 6, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Line Input", MAX9867_PWRMAN, 5, 0, NULL, 0), + SND_SOC_DAPM_MIXER_NAMED_CTL("Input Mixer", SND_SOC_NOPM, 0, 0, + max9867_input_mixer_controls, + ARRAY_SIZE(max9867_input_mixer_controls)), + SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", MAX9867_PWRMAN, 1, 0), + SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", MAX9867_PWRMAN, 0, 0), + + SND_SOC_DAPM_MIXER("Digital", SND_SOC_NOPM, 0, 0, + max9867_sidetone_mixer_controls, + ARRAY_SIZE(max9867_sidetone_mixer_controls)), + SND_SOC_DAPM_MIXER_NAMED_CTL("Output Mixer", SND_SOC_NOPM, 0, 0, + max9867_output_mixer_controls, + ARRAY_SIZE(max9867_output_mixer_controls)), + SND_SOC_DAPM_DAC("DACL", "HiFi Playback", MAX9867_PWRMAN, 3, 0), + SND_SOC_DAPM_DAC("DACR", "HiFi Playback", MAX9867_PWRMAN, 2, 0), + SND_SOC_DAPM_SWITCH("Master Playback", SND_SOC_NOPM, 0, 0, + &max9867_line_out_control), + SND_SOC_DAPM_OUTPUT("LOUT"), + SND_SOC_DAPM_OUTPUT("ROUT"), }; static const struct snd_soc_dapm_route max9867_audio_map[] = { - {"Left DAC", NULL, "DAI_OUT"}, - {"Right DAC", NULL, "DAI_OUT"}, - {"Output Mixer", NULL, "Left DAC"}, - {"Output Mixer", NULL, "Right DAC"}, - {"HPOUT", NULL, "Output Mixer"}, - - {"Left ADC", NULL, "DAI_IN"}, - {"Right ADC", NULL, "DAI_IN"}, - {"Input Mixer", NULL, "Left ADC"}, - {"Input Mixer", NULL, "Right ADC"}, - {"Input Mux", "Line", "Input Mixer"}, - {"Input Mux", "Mic", "Input Mixer"}, - {"Input Mux", "Mic_Line", "Input Mixer"}, - {"Right Line", "Switch", "Input Mux"}, - {"Left Line", "Switch", "Input Mux"}, - {"LINE_IN", NULL, "Left Line"}, - {"LINE_IN", NULL, "Right Line"}, + {"Left Line Input", NULL, "LINL"}, + {"Right Line Input", NULL, "LINR"}, + {"Input Mixer", "Mic Capture Switch", "MICL"}, + {"Input Mixer", "Mic Capture Switch", "MICR"}, + {"Input Mixer", "Line Capture Switch", "Left Line Input"}, + {"Input Mixer", "Line Capture Switch", "Right Line Input"}, + {"ADCL", NULL, "Input Mixer"}, + {"ADCR", NULL, "Input Mixer"}, + + {"Digital", "Sidetone Switch", "ADCL"}, + {"Digital", "Sidetone Switch", "ADCR"}, + {"DACL", NULL, "Digital"}, + {"DACR", NULL, "Digital"}, + + {"Output Mixer", "Line Bypass Switch", "Left Line Input"}, + {"Output Mixer", "Line Bypass Switch", "Right Line Input"}, + {"Output Mixer", NULL, "DACL"}, + {"Output Mixer", NULL, "DACR"}, + {"Master Playback", "Switch", "Output Mixer"}, + {"LOUT", NULL, "Master Playback"}, + {"ROUT", NULL, "Master Playback"}, +}; + +static const unsigned int max9867_rates_44k1[] = { + 11025, 22050, 44100, +}; + +static const struct snd_pcm_hw_constraint_list max9867_constraints_44k1 = { + .list = max9867_rates_44k1, + .count = ARRAY_SIZE(max9867_rates_44k1), }; -enum rates { - pcm_rate_8, pcm_rate_16, pcm_rate_24, - pcm_rate_32, pcm_rate_44, - pcm_rate_48, max_pcm_rate, +static const unsigned int max9867_rates_48k[] = { + 8000, 16000, 32000, 48000, }; -static const struct ni_div_rates { - u32 mclk; - u16 ni[max_pcm_rate]; -} ni_div[] = { - {11289600, {0x116A, 0x22D4, 0x343F, 0x45A9, 0x6000, 0x687D} }, - {12000000, {0x1062, 0x20C5, 0x3127, 0x4189, 0x5A51, 0x624E} }, - {12288000, {0x1000, 0x2000, 0x3000, 0x4000, 0x5833, 0x6000} }, - {13000000, {0x0F20, 0x1E3F, 0x2D5F, 0x3C7F, 0x535F, 0x5ABE} }, - {19200000, {0x0A3D, 0x147B, 0x1EB8, 0x28F6, 0x3873, 0x3D71} }, - {24000000, {0x1062, 0x20C5, 0x1893, 0x4189, 0x5A51, 0x624E} }, - {26000000, {0x0F20, 0x1E3F, 0x16AF, 0x3C7F, 0x535F, 0x5ABE} }, - {27000000, {0x0E90, 0x1D21, 0x15D8, 0x3A41, 0x5048, 0x5762} }, +static const struct snd_pcm_hw_constraint_list max9867_constraints_48k = { + .list = max9867_rates_48k, + .count = ARRAY_SIZE(max9867_rates_48k), }; -static inline int get_ni_value(int mclk, int rate) +struct max9867_priv { + struct regmap *regmap; + const struct snd_pcm_hw_constraint_list *constraints; + unsigned int sysclk, pclk; + bool master, dsp_a; +}; + +static int max9867_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) { - int i, ret = 0; + struct max9867_priv *max9867 = + snd_soc_component_get_drvdata(dai->component); - /* find the closest rate index*/ - for (i = 0; i < ARRAY_SIZE(ni_div); i++) { - if (ni_div[i].mclk >= mclk) - break; - } - if (i == ARRAY_SIZE(ni_div)) - return -EINVAL; + if (max9867->constraints) + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, max9867->constraints); - switch (rate) { - case 8000: - return ni_div[i].ni[pcm_rate_8]; - case 16000: - return ni_div[i].ni[pcm_rate_16]; - case 32000: - return ni_div[i].ni[pcm_rate_32]; - case 44100: - return ni_div[i].ni[pcm_rate_44]; - case 48000: - return ni_div[i].ni[pcm_rate_48]; - default: - pr_err("%s wrong rate %d\n", __func__, rate); - ret = -EINVAL; - } - return ret; + return 0; } static int max9867_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { + int value; + unsigned long int rate, ratio; struct snd_soc_component *component = dai->component; struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); - unsigned int ni_h, ni_l; - int value; + unsigned int ni = DIV_ROUND_CLOSEST_ULL(96ULL * 0x10000 * params_rate(params), + max9867->pclk); - value = get_ni_value(max9867->sysclk, params_rate(params)); - if (value < 0) - return value; - - ni_h = (0xFF00 & value) >> 8; - ni_l = 0x00FF & value; /* set up the ni value */ regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH, - MAX9867_NI_HIGH_MASK, ni_h); + MAX9867_NI_HIGH_MASK, (0xFF00 & ni) >> 8); regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW, - MAX9867_NI_LOW_MASK, ni_l); - if (!max9867->master) { - /* - * digital pll locks on to any externally supplied LRCLK signal - * and also enable rapid lock mode. - */ - regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW, - MAX9867_RAPID_LOCK, MAX9867_RAPID_LOCK); - regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH, - MAX9867_PLL, MAX9867_PLL); - } else { - unsigned long int bclk_rate, pclk_bclk_ratio; - int bclk_value; - - bclk_rate = params_rate(params) * 2 * params_width(params); - pclk_bclk_ratio = max9867->pclk/bclk_rate; - switch (params_width(params)) { - case 8: - case 16: - switch (pclk_bclk_ratio) { - case 2: - bclk_value = MAX9867_IFC1B_PCLK_2; - break; - case 4: - bclk_value = MAX9867_IFC1B_PCLK_4; - break; + MAX9867_NI_LOW_MASK, 0x00FF & ni); + if (max9867->master) { + if (max9867->dsp_a) { + value = MAX9867_IFC1B_48X; + } else { + rate = params_rate(params) * 2 * params_width(params); + ratio = max9867->pclk / rate; + switch (params_width(params)) { case 8: - bclk_value = MAX9867_IFC1B_PCLK_8; - break; case 16: - bclk_value = MAX9867_IFC1B_PCLK_16; + switch (ratio) { + case 2: + value = MAX9867_IFC1B_PCLK_2; + break; + case 4: + value = MAX9867_IFC1B_PCLK_4; + break; + case 8: + value = MAX9867_IFC1B_PCLK_8; + break; + case 16: + value = MAX9867_IFC1B_PCLK_16; + break; + default: + return -EINVAL; + } + break; + case 24: + value = MAX9867_IFC1B_48X; + break; + case 32: + value = MAX9867_IFC1B_64X; break; default: - dev_err(component->dev, - "unsupported sampling rate\n"); return -EINVAL; } - break; - case 24: - bclk_value = MAX9867_IFC1B_24BIT; - break; - case 32: - bclk_value = MAX9867_IFC1B_32BIT; - break; - default: - dev_err(component->dev, "unsupported sampling rate\n"); - return -EINVAL; } regmap_update_bits(max9867->regmap, MAX9867_IFC1B, - MAX9867_IFC1B_BCLK_MASK, bclk_value); + MAX9867_IFC1B_BCLK_MASK, value); + } else { + /* + * digital pll locks on to any externally supplied LRCLK signal + * and also enable rapid lock mode. + */ + regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW, + MAX9867_RAPID_LOCK, MAX9867_RAPID_LOCK); + regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH, + MAX9867_PLL, MAX9867_PLL); } return 0; } -static int max9867_prepare(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_component *component = dai->component; - struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); - - regmap_update_bits(max9867->regmap, MAX9867_PWRMAN, - MAX9867_SHTDOWN_MASK, MAX9867_SHTDOWN_MASK); - return 0; -} - static int max9867_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_component *component = dai->component; struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); - if (mute) - regmap_update_bits(max9867->regmap, MAX9867_DACLEVEL, - MAX9867_DAC_MUTE_MASK, MAX9867_DAC_MUTE_MASK); - else - regmap_update_bits(max9867->regmap, MAX9867_DACLEVEL, - MAX9867_DAC_MUTE_MASK, 0); - return 0; + return regmap_update_bits(max9867->regmap, MAX9867_DACLEVEL, + 1 << 6, !!mute << 6); } static int max9867_set_dai_sysclk(struct snd_soc_dai *codec_dai, @@ -283,21 +264,29 @@ static int max9867_set_dai_sysclk(struct snd_soc_dai *codec_dai, /* Set the prescaler based on the master clock frequency*/ if (freq >= 10000000 && freq <= 20000000) { value |= MAX9867_PSCLK_10_20; - max9867->pclk = freq; + max9867->pclk = freq; } else if (freq >= 20000000 && freq <= 40000000) { value |= MAX9867_PSCLK_20_40; - max9867->pclk = freq/2; + max9867->pclk = freq / 2; } else if (freq >= 40000000 && freq <= 60000000) { value |= MAX9867_PSCLK_40_60; - max9867->pclk = freq/4; + max9867->pclk = freq / 4; } else { dev_err(component->dev, "Invalid clock frequency %uHz (required 10-60MHz)\n", freq); return -EINVAL; } - value = value << MAX9867_PSCLK_SHIFT; + if (freq % 48000 == 0) + max9867->constraints = &max9867_constraints_48k; + else if (freq % 44100 == 0) + max9867->constraints = &max9867_constraints_44k1; + else + dev_warn(component->dev, + "Unable to set exact rate with %uHz clock frequency\n", + freq); max9867->sysclk = freq; + value = value << MAX9867_PSCLK_SHIFT; /* exact integer mode is not supported */ value &= ~MAX9867_FREQ_MASK; regmap_update_bits(max9867->regmap, MAX9867_SYSCLK, @@ -310,16 +299,17 @@ static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai, { struct snd_soc_component *component = codec_dai->component; struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); - u8 iface1A = 0, iface1B = 0; + u8 iface1A, iface1B; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: - max9867->master = 1; - iface1A |= MAX9867_MASTER; + max9867->master = true; + iface1A = MAX9867_MASTER; + iface1B = MAX9867_IFC1B_48X; break; case SND_SOC_DAIFMT_CBS_CFS: - max9867->master = 0; - iface1A &= ~MAX9867_MASTER; + max9867->master = false; + iface1A = iface1B = 0; break; default: return -EINVAL; @@ -327,9 +317,11 @@ static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai, switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: + max9867->dsp_a = false; iface1A |= MAX9867_I2S_DLY; break; case SND_SOC_DAIFMT_DSP_A: + max9867->dsp_a = true; iface1A |= MAX9867_TDM_MODE | MAX9867_SDOUT_HIZ; break; default: @@ -355,21 +347,18 @@ static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai, regmap_write(max9867->regmap, MAX9867_IFC1A, iface1A); regmap_write(max9867->regmap, MAX9867_IFC1B, iface1B); + return 0; } static const struct snd_soc_dai_ops max9867_dai_ops = { - .set_fmt = max9867_dai_set_fmt, .set_sysclk = max9867_set_dai_sysclk, - .prepare = max9867_prepare, + .set_fmt = max9867_dai_set_fmt, .digital_mute = max9867_mute, - .hw_params = max9867_dai_hw_params, + .startup = max9867_startup, + .hw_params = max9867_dai_hw_params, }; -#define MAX9867_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ - SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) -#define MAX9867_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) - static struct snd_soc_dai_driver max9867_dai[] = { { .name = "max9867-aif1", @@ -377,42 +366,74 @@ static struct snd_soc_dai_driver max9867_dai[] = { .stream_name = "HiFi Playback", .channels_min = 2, .channels_max = 2, - .rates = MAX9867_RATES, - .formats = MAX9867_FORMATS, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .capture = { .stream_name = "HiFi Capture", .channels_min = 2, .channels_max = 2, - .rates = MAX9867_RATES, - .formats = MAX9867_FORMATS, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .ops = &max9867_dai_ops, .symmetric_rates = 1, } }; -#ifdef CONFIG_PM_SLEEP -static int max9867_suspend(struct device *dev) +#ifdef CONFIG_PM +static int max9867_suspend(struct snd_soc_component *component) { - struct max9867_priv *max9867 = dev_get_drvdata(dev); + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); - /* Drop down to power saving mode when system is suspended */ - regmap_update_bits(max9867->regmap, MAX9867_PWRMAN, - MAX9867_SHTDOWN_MASK, ~MAX9867_SHTDOWN_MASK); return 0; } -static int max9867_resume(struct device *dev) +static int max9867_resume(struct snd_soc_component *component) { - struct max9867_priv *max9867 = dev_get_drvdata(dev); + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY); - regmap_update_bits(max9867->regmap, MAX9867_PWRMAN, - MAX9867_SHTDOWN_MASK, MAX9867_SHTDOWN_MASK); return 0; } +#else +#define max9867_suspend NULL +#define max9867_resume NULL #endif +static int max9867_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + int err; + struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + err = regcache_sync(max9867->regmap); + if (err) + return err; + + err = regmap_update_bits(max9867->regmap, MAX9867_PWRMAN, + MAX9867_SHTDOWN, MAX9867_SHTDOWN); + if (err) + return err; + } + break; + case SND_SOC_BIAS_OFF: + err = regmap_update_bits(max9867->regmap, MAX9867_PWRMAN, + MAX9867_SHTDOWN, 0); + if (err) + return err; + + regcache_mark_dirty(max9867->regmap); + break; + default: + break; + } + + return 0; +} + static const struct snd_soc_component_driver max9867_component = { .controls = max9867_snd_controls, .num_controls = ARRAY_SIZE(max9867_snd_controls), @@ -420,6 +441,9 @@ static const struct snd_soc_component_driver max9867_component = { .num_dapm_routes = ARRAY_SIZE(max9867_audio_map), .dapm_widgets = max9867_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(max9867_dapm_widgets), + .suspend = max9867_suspend, + .resume = max9867_resume, + .set_bias_level = max9867_set_bias_level, .idle_bias_on = 1, .use_pmdown_time = 1, .endianness = 1, @@ -450,8 +474,8 @@ static const struct reg_default max9867_reg[] = { { 0x0B, 0x00 }, { 0x0C, 0x00 }, { 0x0D, 0x00 }, - { 0x0E, 0x00 }, - { 0x0F, 0x00 }, + { 0x0E, 0x40 }, + { 0x0F, 0x40 }, { 0x10, 0x00 }, { 0x11, 0x00 }, { 0x12, 0x00 }, @@ -476,10 +500,9 @@ static int max9867_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct max9867_priv *max9867; - int ret = 0, reg; + int ret, reg; - max9867 = devm_kzalloc(&i2c->dev, - sizeof(*max9867), GFP_KERNEL); + max9867 = devm_kzalloc(&i2c->dev, sizeof(*max9867), GFP_KERNEL); if (!max9867) return -ENOMEM; @@ -490,8 +513,7 @@ static int max9867_i2c_probe(struct i2c_client *i2c, dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret); return ret; } - ret = regmap_read(max9867->regmap, - MAX9867_REVISION, ®); + ret = regmap_read(max9867->regmap, MAX9867_REVISION, ®); if (ret < 0) { dev_err(&i2c->dev, "Failed to read: %d\n", ret); return ret; @@ -499,10 +521,8 @@ static int max9867_i2c_probe(struct i2c_client *i2c, dev_info(&i2c->dev, "device revision: %x\n", reg); ret = devm_snd_soc_register_component(&i2c->dev, &max9867_component, max9867_dai, ARRAY_SIZE(max9867_dai)); - if (ret < 0) { + if (ret < 0) dev_err(&i2c->dev, "Failed to register component: %d\n", ret); - return ret; - } return ret; } @@ -518,15 +538,10 @@ static const struct of_device_id max9867_of_match[] = { }; MODULE_DEVICE_TABLE(of, max9867_of_match); -static const struct dev_pm_ops max9867_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(max9867_suspend, max9867_resume) -}; - static struct i2c_driver max9867_i2c_driver = { .driver = { .name = "max9867", .of_match_table = of_match_ptr(max9867_of_match), - .pm = &max9867_pm_ops, }, .probe = max9867_i2c_probe, .id_table = max9867_i2c_id, @@ -534,6 +549,6 @@ static struct i2c_driver max9867_i2c_driver = { module_i2c_driver(max9867_i2c_driver); -MODULE_AUTHOR("anish kumar <yesanishhere@gmail.com>"); -MODULE_DESCRIPTION("ALSA SoC MAX9867 driver"); +MODULE_AUTHOR("Ladislav Michl <ladis@linux-mips.org>"); +MODULE_DESCRIPTION("ASoC MAX9867 driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max9867.h b/sound/soc/codecs/max9867.h index 55cd9976ff47..2277798291a1 100644 --- a/sound/soc/codecs/max9867.h +++ b/sound/soc/codecs/max9867.h @@ -26,13 +26,11 @@ #define MAX9867_PSCLK_10_20 0x1 #define MAX9867_PSCLK_20_40 0x2 #define MAX9867_PSCLK_40_60 0x3 -#define MAX9867_AUDIOCLKHIGH 0x06 -#define MAX9867_NI_HIGH_WIDTH 0x7 -#define MAX9867_NI_HIGH_MASK 0x7F -#define MAX9867_NI_LOW_MASK 0x7F -#define MAX9867_NI_LOW_SHIFT 0x1 -#define MAX9867_PLL (1<<7) -#define MAX9867_AUDIOCLKLOW 0x07 +#define MAX9867_AUDIOCLKHIGH 0x06 +#define MAX9867_NI_HIGH_MASK 0x7F +#define MAX9867_NI_LOW_MASK 0xFE +#define MAX9867_PLL (1<<7) +#define MAX9867_AUDIOCLKLOW 0x07 #define MAX9867_RAPID_LOCK 0x01 #define MAX9867_IFC1A 0x08 #define MAX9867_MASTER (1<<7) @@ -43,40 +41,29 @@ #define MAX9867_BCI_MODE (1<<5) #define MAX9867_IFC1B 0x09 #define MAX9867_IFC1B_BCLK_MASK 7 -#define MAX9867_IFC1B_32BIT 0x01 -#define MAX9867_IFC1B_24BIT 0x02 -#define MAX9867_IFC1B_PCLK_2 4 -#define MAX9867_IFC1B_PCLK_4 5 -#define MAX9867_IFC1B_PCLK_8 6 -#define MAX9867_IFC1B_PCLK_16 7 +#define MAX9867_IFC1B_64X 0x01 +#define MAX9867_IFC1B_48X 0x02 +#define MAX9867_IFC1B_PCLK_2 0x04 +#define MAX9867_IFC1B_PCLK_4 0x05 +#define MAX9867_IFC1B_PCLK_8 0x06 +#define MAX9867_IFC1B_PCLK_16 0x07 #define MAX9867_CODECFLTR 0x0a -#define MAX9867_DACGAIN 0x0b +#define MAX9867_SIDETONE 0x0b #define MAX9867_DACLEVEL 0x0c -#define MAX9867_DAC_MUTE_SHIFT 0x6 -#define MAX9867_DAC_MUTE_WIDTH 0x1 -#define MAX9867_DAC_MUTE_MASK (0x1<<MAX9867_DAC_MUTE_SHIFT) #define MAX9867_ADCLEVEL 0x0d #define MAX9867_LEFTLINELVL 0x0e -#define MAX9867_RIGTHLINELVL 0x0f +#define MAX9867_RIGHTLINELVL 0x0f #define MAX9867_LEFTVOL 0x10 #define MAX9867_RIGHTVOL 0x11 #define MAX9867_LEFTMICGAIN 0x12 #define MAX9867_RIGHTMICGAIN 0x13 #define MAX9867_INPUTCONFIG 0x14 -#define MAX9867_INPUT_SHIFT 0x6 #define MAX9867_MICCONFIG 0x15 #define MAX9867_MODECONFIG 0x16 #define MAX9867_PWRMAN 0x17 -#define MAX9867_SHTDOWN_MASK (1<<7) +#define MAX9867_SHTDOWN 0x80 #define MAX9867_REVISION 0xff #define MAX9867_CACHEREGNUM 10 -/* codec private data */ -struct max9867_priv { - struct regmap *regmap; - unsigned int sysclk; - unsigned int pclk; - unsigned int master; -}; #endif diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c index e3c8cd17daf2..4dd1a609756b 100644 --- a/sound/soc/codecs/nau8540.c +++ b/sound/soc/codecs/nau8540.c @@ -585,7 +585,7 @@ static int nau8540_calc_fll_param(unsigned int fll_in, fvco_max = 0; fvco_sel = ARRAY_SIZE(mclk_src_scaling); for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) { - fvco = 256 * fs * 2 * mclk_src_scaling[i].param; + fvco = 256ULL * fs * 2 * mclk_src_scaling[i].param; if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX && fvco_max < fvco) { fvco_max = fvco; diff --git a/sound/soc/codecs/nau8822.c b/sound/soc/codecs/nau8822.c index 622ce947f134..c6152a044416 100644 --- a/sound/soc/codecs/nau8822.c +++ b/sound/soc/codecs/nau8822.c @@ -1,18 +1,14 @@ -/* - * nau8822.c -- NAU8822 ALSA Soc Audio Codec driver - * - * Copyright 2017 Nuvoton Technology Corp. - * - * Author: David Lin <ctlin0@nuvoton.com> - * Co-author: John Hsu <kchsu0@nuvoton.com> - * Co-author: Seven Li <wtli@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. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// nau8822.c -- NAU8822 ALSA Soc Audio driver +// +// Copyright 2017 Nuvoton Technology Crop. +// +// Author: David Lin <ctlin0@nuvoton.com> +// Co-author: John Hsu <kchsu0@nuvoton.com> +// Co-author: Seven Li <wtli@nuvoton.com> +// +// Based on WM8974.c #include <linux/module.h> #include <linux/moduleparam.h> diff --git a/sound/soc/codecs/nau8822.h b/sound/soc/codecs/nau8822.h index aa79c969cd44..9c552983a293 100644 --- a/sound/soc/codecs/nau8822.h +++ b/sound/soc/codecs/nau8822.h @@ -1,13 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * nau8822.h -- NAU8822 Soc Audio Codec driver + * nau8822.h -- NAU8822 ALSA SoC Audio driver + * + * Copyright 2017 Nuvoton Technology Crop. * * Author: David Lin <ctlin0@nuvoton.com> * Co-author: John Hsu <kchsu0@nuvoton.com> * Co-author: Seven Li <wtli@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 __NAU8822_H__ diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index b9fed99d8b5e..7bbcbf5f05c8 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -424,10 +424,8 @@ static u32 nau8825_xtalk_sidetone(u32 sig_org, u32 sig_cros) { u32 gain, sidetone; - if (unlikely(sig_org == 0) || unlikely(sig_cros == 0)) { - WARN_ON(1); + if (WARN_ON(sig_org == 0 || sig_cros == 0)) return 0; - } sig_org = nau8825_intlog10_dec3(sig_org); sig_cros = nau8825_intlog10_dec3(sig_cros); diff --git a/sound/soc/codecs/pcm3060.c b/sound/soc/codecs/pcm3060.c index 771b46e1974b..6714aa8d9026 100644 --- a/sound/soc/codecs/pcm3060.c +++ b/sound/soc/codecs/pcm3060.c @@ -198,19 +198,25 @@ static const struct snd_kcontrol_new pcm3060_dapm_controls[] = { }; static const struct snd_soc_dapm_widget pcm3060_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC", "Playback", PCM3060_REG64, + PCM3060_REG_SHIFT_DAPSV, 1), + SND_SOC_DAPM_OUTPUT("OUTL"), SND_SOC_DAPM_OUTPUT("OUTR"), SND_SOC_DAPM_INPUT("INL"), SND_SOC_DAPM_INPUT("INR"), + + SND_SOC_DAPM_ADC("ADC", "Capture", PCM3060_REG64, + PCM3060_REG_SHIFT_ADPSV, 1), }; static const struct snd_soc_dapm_route pcm3060_dapm_map[] = { - { "OUTL", NULL, "Playback" }, - { "OUTR", NULL, "Playback" }, + { "OUTL", NULL, "DAC" }, + { "OUTR", NULL, "DAC" }, - { "Capture", NULL, "INL" }, - { "Capture", NULL, "INR" }, + { "ADC", NULL, "INL" }, + { "ADC", NULL, "INR" }, }; /* soc component */ @@ -270,9 +276,23 @@ EXPORT_SYMBOL(pcm3060_regmap); /* device */ +static void pcm3060_parse_dt(const struct device_node *np, + struct pcm3060_priv *priv) +{ + priv->out_se = of_property_read_bool(np, "ti,out-single-ended"); +} + int pcm3060_probe(struct device *dev) { int rc; + struct pcm3060_priv *priv = dev_get_drvdata(dev); + + if (dev->of_node) + pcm3060_parse_dt(dev->of_node, priv); + + if (priv->out_se) + regmap_update_bits(priv->regmap, PCM3060_REG64, + PCM3060_REG_SE, PCM3060_REG_SE); rc = devm_snd_soc_register_component(dev, &pcm3060_soc_comp_driver, pcm3060_dai, diff --git a/sound/soc/codecs/pcm3060.h b/sound/soc/codecs/pcm3060.h index fd89a68aa8a7..6a027b4a845d 100644 --- a/sound/soc/codecs/pcm3060.h +++ b/sound/soc/codecs/pcm3060.h @@ -25,6 +25,7 @@ struct pcm3060_priv_dai { struct pcm3060_priv { struct regmap *regmap; struct pcm3060_priv_dai dai[PCM3060_DAI_IDS_NUM]; + u8 out_se: 1; }; int pcm3060_probe(struct device *dev); @@ -36,7 +37,9 @@ int pcm3060_remove(struct device *dev); #define PCM3060_REG_MRST 0x80 #define PCM3060_REG_SRST 0x40 #define PCM3060_REG_ADPSV 0x20 +#define PCM3060_REG_SHIFT_ADPSV 0x05 #define PCM3060_REG_DAPSV 0x10 +#define PCM3060_REG_SHIFT_DAPSV 0x04 #define PCM3060_REG_SE 0x01 #define PCM3060_REG65 0x41 diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c index 52cc950c9fd1..08d3fe192e65 100644 --- a/sound/soc/codecs/pcm3168a.c +++ b/sound/soc/codecs/pcm3168a.c @@ -133,10 +133,6 @@ static const struct snd_kcontrol_new pcm3168a_snd_controls[] = { SOC_DOUBLE("DAC2 Invert Switch", PCM3168A_DAC_INV, 2, 3, 1, 0), SOC_DOUBLE("DAC3 Invert Switch", PCM3168A_DAC_INV, 4, 5, 1, 0), SOC_DOUBLE("DAC4 Invert Switch", PCM3168A_DAC_INV, 6, 7, 1, 0), - SOC_DOUBLE_STS("DAC1 Zero Flag", PCM3168A_DAC_ZERO, 0, 1, 1, 0), - SOC_DOUBLE_STS("DAC2 Zero Flag", PCM3168A_DAC_ZERO, 2, 3, 1, 0), - SOC_DOUBLE_STS("DAC3 Zero Flag", PCM3168A_DAC_ZERO, 4, 5, 1, 0), - SOC_DOUBLE_STS("DAC4 Zero Flag", PCM3168A_DAC_ZERO, 6, 7, 1, 0), SOC_ENUM("DAC Volume Control Type", pcm3168a_dac_volume_type), SOC_ENUM("DAC Volume Rate Multiplier", pcm3168a_dac_att_mult), SOC_ENUM("DAC De-Emphasis", pcm3168a_dac_demp), @@ -176,9 +172,6 @@ static const struct snd_kcontrol_new pcm3168a_snd_controls[] = { SOC_DOUBLE("ADC1 Mute Switch", PCM3168A_ADC_MUTE, 0, 1, 1, 0), SOC_DOUBLE("ADC2 Mute Switch", PCM3168A_ADC_MUTE, 2, 3, 1, 0), SOC_DOUBLE("ADC3 Mute Switch", PCM3168A_ADC_MUTE, 4, 5, 1, 0), - SOC_DOUBLE_STS("ADC1 Overflow Flag", PCM3168A_ADC_OV, 0, 1, 1, 0), - SOC_DOUBLE_STS("ADC2 Overflow Flag", PCM3168A_ADC_OV, 2, 3, 1, 0), - SOC_DOUBLE_STS("ADC3 Overflow Flag", PCM3168A_ADC_OV, 4, 5, 1, 0), SOC_ENUM("ADC Volume Control Type", pcm3168a_adc_volume_type), SOC_ENUM("ADC Volume Rate Multiplier", pcm3168a_adc_att_mult), SOC_ENUM("ADC Overflow Flag Polarity", pcm3168a_adc_ov_pol), @@ -504,6 +497,10 @@ static int pcm3168a_startup(struct snd_pcm_substream *substream, unsigned int fmt; unsigned int sample_min; unsigned int channel_max; + unsigned int channel_maxs[] = { + 6, /* rx */ + 8 /* tx */ + }; if (tx) fmt = pcm3168a->dac_fmt; @@ -528,18 +525,9 @@ static int pcm3168a_startup(struct snd_pcm_substream *substream, channel_max = 2; break; case PCM3168A_FMT_LEFT_J: - sample_min = 24; - if (tx) - channel_max = 8; - else - channel_max = 6; - break; case PCM3168A_FMT_I2S: sample_min = 24; - if (tx) - channel_max = 8; - else - channel_max = 6; + channel_max = channel_maxs[tx]; break; default: sample_min = 24; @@ -770,15 +758,22 @@ err_clk: } EXPORT_SYMBOL_GPL(pcm3168a_probe); -void pcm3168a_remove(struct device *dev) +static void pcm3168a_disable(struct device *dev) { struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev); - pm_runtime_disable(dev); regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies), - pcm3168a->supplies); + pcm3168a->supplies); clk_disable_unprepare(pcm3168a->scki); } + +void pcm3168a_remove(struct device *dev) +{ + pm_runtime_disable(dev); +#ifndef CONFIG_PM + pcm3168a_disable(dev); +#endif +} EXPORT_SYMBOL_GPL(pcm3168a_remove); #ifdef CONFIG_PM @@ -833,10 +828,7 @@ static int pcm3168a_rt_suspend(struct device *dev) regcache_cache_only(pcm3168a->regmap, true); - regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies), - pcm3168a->supplies); - - clk_disable_unprepare(pcm3168a->scki); + pcm3168a_disable(dev); return 0; } diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index f0f2d4fd3769..6cb1653be804 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -53,6 +53,8 @@ struct pcm512x_priv { unsigned long overclock_pll; unsigned long overclock_dac; unsigned long overclock_dsp; + int mute; + struct mutex mutex; }; /* @@ -384,6 +386,61 @@ static const struct soc_enum pcm512x_veds = SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDS_SHIFT, 4, pcm512x_ramp_step_text); +static int pcm512x_update_mute(struct pcm512x_priv *pcm512x) +{ + return regmap_update_bits( + pcm512x->regmap, PCM512x_MUTE, PCM512x_RQML | PCM512x_RQMR, + (!!(pcm512x->mute & 0x5) << PCM512x_RQML_SHIFT) + | (!!(pcm512x->mute & 0x3) << PCM512x_RQMR_SHIFT)); +} + +static int pcm512x_digital_playback_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + + mutex_lock(&pcm512x->mutex); + ucontrol->value.integer.value[0] = !(pcm512x->mute & 0x4); + ucontrol->value.integer.value[1] = !(pcm512x->mute & 0x2); + mutex_unlock(&pcm512x->mutex); + + return 0; +} + +static int pcm512x_digital_playback_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + int ret, changed = 0; + + mutex_lock(&pcm512x->mutex); + + if ((pcm512x->mute & 0x4) == (ucontrol->value.integer.value[0] << 2)) { + pcm512x->mute ^= 0x4; + changed = 1; + } + if ((pcm512x->mute & 0x2) == (ucontrol->value.integer.value[1] << 1)) { + pcm512x->mute ^= 0x2; + changed = 1; + } + + if (changed) { + ret = pcm512x_update_mute(pcm512x); + if (ret != 0) { + dev_err(component->dev, + "Failed to update digital mute: %d\n", ret); + mutex_unlock(&pcm512x->mutex); + return ret; + } + } + + mutex_unlock(&pcm512x->mutex); + + return changed; +} + static const struct snd_kcontrol_new pcm512x_controls[] = { SOC_DOUBLE_R_TLV("Digital Playback Volume", PCM512x_DIGITAL_VOLUME_2, PCM512x_DIGITAL_VOLUME_3, 0, 255, 1, digital_tlv), @@ -391,8 +448,15 @@ SOC_DOUBLE_TLV("Analogue Playback Volume", PCM512x_ANALOG_GAIN_CTRL, PCM512x_LAGN_SHIFT, PCM512x_RAGN_SHIFT, 1, 1, analog_tlv), SOC_DOUBLE_TLV("Analogue Playback Boost Volume", PCM512x_ANALOG_GAIN_BOOST, PCM512x_AGBL_SHIFT, PCM512x_AGBR_SHIFT, 1, 0, boost_tlv), -SOC_DOUBLE("Digital Playback Switch", PCM512x_MUTE, PCM512x_RQML_SHIFT, - PCM512x_RQMR_SHIFT, 1, 1), +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Digital Playback Switch", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_ctl_boolean_stereo_info, + .get = pcm512x_digital_playback_switch_get, + .put = pcm512x_digital_playback_switch_put +}, SOC_SINGLE("Deemphasis Switch", PCM512x_DSP, PCM512x_DEMP_SHIFT, 1, 1), SOC_ENUM("DSP Program", pcm512x_dsp_program), @@ -1319,10 +1383,61 @@ static int pcm512x_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } +static int pcm512x_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_component *component = dai->component; + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + int ret; + unsigned int mute_det; + + mutex_lock(&pcm512x->mutex); + + if (mute) { + pcm512x->mute |= 0x1; + ret = regmap_update_bits(pcm512x->regmap, PCM512x_MUTE, + PCM512x_RQML | PCM512x_RQMR, + PCM512x_RQML | PCM512x_RQMR); + if (ret != 0) { + dev_err(component->dev, + "Failed to set digital mute: %d\n", ret); + mutex_unlock(&pcm512x->mutex); + return ret; + } + + regmap_read_poll_timeout(pcm512x->regmap, + PCM512x_ANALOG_MUTE_DET, + mute_det, (mute_det & 0x3) == 0, + 200, 10000); + + mutex_unlock(&pcm512x->mutex); + } else { + pcm512x->mute &= ~0x1; + ret = pcm512x_update_mute(pcm512x); + if (ret != 0) { + dev_err(component->dev, + "Failed to update digital mute: %d\n", ret); + mutex_unlock(&pcm512x->mutex); + return ret; + } + + regmap_read_poll_timeout(pcm512x->regmap, + PCM512x_ANALOG_MUTE_DET, + mute_det, + (mute_det & 0x3) + == ((~pcm512x->mute >> 1) & 0x3), + 200, 10000); + } + + mutex_unlock(&pcm512x->mutex); + + return 0; +} + static const struct snd_soc_dai_ops pcm512x_dai_ops = { .startup = pcm512x_dai_startup, .hw_params = pcm512x_hw_params, .set_fmt = pcm512x_set_fmt, + .digital_mute = pcm512x_digital_mute, }; static struct snd_soc_dai_driver pcm512x_dai = { @@ -1388,6 +1503,8 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap) if (!pcm512x) return -ENOMEM; + mutex_init(&pcm512x->mutex); + dev_set_drvdata(dev, pcm512x); pcm512x->regmap = regmap; diff --git a/sound/soc/codecs/pcm512x.h b/sound/soc/codecs/pcm512x.h index d70d9c0c2088..9dda8693498e 100644 --- a/sound/soc/codecs/pcm512x.h +++ b/sound/soc/codecs/pcm512x.h @@ -112,7 +112,9 @@ #define PCM512x_RQST_SHIFT 4 /* Page 0, Register 3 - mute */ +#define PCM512x_RQMR (1 << 0) #define PCM512x_RQMR_SHIFT 0 +#define PCM512x_RQML (1 << 4) #define PCM512x_RQML_SHIFT 4 /* Page 0, Register 4 - PLL */ diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c index 27f7445b2432..e74b2e8cd423 100644 --- a/sound/soc/codecs/rt5660.c +++ b/sound/soc/codecs/rt5660.c @@ -1246,6 +1246,7 @@ MODULE_DEVICE_TABLE(of, rt5660_of_match); static const struct acpi_device_id rt5660_acpi_match[] = { { "10EC5660", 0 }, + { "10EC3277", 0 }, { }, }; MODULE_DEVICE_TABLE(acpi, rt5660_acpi_match); diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c index 7eb2cbd39d6e..da6647015708 100644 --- a/sound/soc/codecs/rt5663.c +++ b/sound/soc/codecs/rt5663.c @@ -17,6 +17,7 @@ #include <linux/platform_device.h> #include <linux/spi/spi.h> #include <linux/acpi.h> +#include <linux/regulator/consumer.h> #include <linux/workqueue.h> #include <sound/core.h> #include <sound/pcm.h> @@ -33,6 +34,9 @@ #define RT5663_DEVICE_ID_2 0x6451 #define RT5663_DEVICE_ID_1 0x6406 +#define RT5663_POWER_ON_DELAY_MS 300 +#define RT5663_SUPPLY_CURRENT_UA 500000 + enum { CODEC_VER_1, CODEC_VER_0, @@ -48,6 +52,11 @@ struct impedance_mapping_table { unsigned int dc_offset_r_manual_mic; }; +static const char *const rt5663_supply_names[] = { + "avdd", + "cpvdd", +}; + struct rt5663_priv { struct snd_soc_component *component; struct rt5663_platform_data pdata; @@ -56,6 +65,7 @@ struct rt5663_priv { struct snd_soc_jack *hs_jack; struct timer_list btn_check_timer; struct impedance_mapping_table *imp_table; + struct regulator_bulk_data supplies[ARRAY_SIZE(rt5663_supply_names)]; int codec_ver; int sysclk; @@ -3483,7 +3493,7 @@ static int rt5663_i2c_probe(struct i2c_client *i2c, { struct rt5663_platform_data *pdata = dev_get_platdata(&i2c->dev); struct rt5663_priv *rt5663; - int ret; + int ret, i; unsigned int val; struct regmap *regmap; @@ -3500,12 +3510,44 @@ static int rt5663_i2c_probe(struct i2c_client *i2c, else rt5663_parse_dp(rt5663, &i2c->dev); + for (i = 0; i < ARRAY_SIZE(rt5663->supplies); i++) + rt5663->supplies[i].supply = rt5663_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, + ARRAY_SIZE(rt5663->supplies), + rt5663->supplies); + if (ret) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + /* Set load for regulator. */ + for (i = 0; i < ARRAY_SIZE(rt5663->supplies); i++) { + ret = regulator_set_load(rt5663->supplies[i].consumer, + RT5663_SUPPLY_CURRENT_UA); + if (ret < 0) { + dev_err(&i2c->dev, + "Failed to set regulator load on %s, ret: %d\n", + rt5663->supplies[i].supply, ret); + return ret; + } + } + + ret = regulator_bulk_enable(ARRAY_SIZE(rt5663->supplies), + rt5663->supplies); + + if (ret) { + dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + msleep(RT5663_POWER_ON_DELAY_MS); + 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; + goto err_enable; } ret = regmap_read(regmap, RT5663_VENDOR_ID_2, &val); @@ -3530,14 +3572,15 @@ static int rt5663_i2c_probe(struct i2c_client *i2c, dev_err(&i2c->dev, "Device with ID register %#x is not rt5663\n", val); - return -ENODEV; + ret = -ENODEV; + goto err_enable; } if (IS_ERR(rt5663->regmap)) { ret = PTR_ERR(rt5663->regmap); dev_err(&i2c->dev, "Failed to allocate register map: %d\n", ret); - return ret; + goto err_enable; } /* reset and calibrate */ @@ -3635,20 +3678,32 @@ static int rt5663_i2c_probe(struct i2c_client *i2c, ret = request_irq(i2c->irq, rt5663_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5663", rt5663); - if (ret) + if (ret) { dev_err(&i2c->dev, "%s Failed to reguest IRQ: %d\n", __func__, ret); + goto err_enable; + } } ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_rt5663, rt5663_dai, ARRAY_SIZE(rt5663_dai)); - if (ret) { - if (i2c->irq) - free_irq(i2c->irq, rt5663); - } + if (ret) + goto err_enable; + return 0; + + + /* + * Error after enabling regulators should goto err_enable + * to disable regulators. + */ +err_enable: + if (i2c->irq) + free_irq(i2c->irq, rt5663); + + regulator_bulk_disable(ARRAY_SIZE(rt5663->supplies), rt5663->supplies); return ret; } @@ -3659,6 +3714,8 @@ static int rt5663_i2c_remove(struct i2c_client *i2c) if (i2c->irq) free_irq(i2c->irq, rt5663); + regulator_bulk_disable(ARRAY_SIZE(rt5663->supplies), rt5663->supplies); + return 0; } diff --git a/sound/soc/codecs/simple-amplifier.c b/sound/soc/codecs/simple-amplifier.c index 85524acf3e9c..c07e8a80b4b7 100644 --- a/sound/soc/codecs/simple-amplifier.c +++ b/sound/soc/codecs/simple-amplifier.c @@ -19,6 +19,7 @@ #include <linux/gpio/consumer.h> #include <linux/module.h> +#include <linux/regulator/consumer.h> #include <sound/soc.h> #define DRV_NAME "simple-amplifier" @@ -58,11 +59,14 @@ static const struct snd_soc_dapm_widget simple_amp_dapm_widgets[] = { (SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)), SND_SOC_DAPM_OUTPUT("OUTL"), SND_SOC_DAPM_OUTPUT("OUTR"), + SND_SOC_DAPM_REGULATOR_SUPPLY("VCC", 20, 0), }; static const struct snd_soc_dapm_route simple_amp_dapm_routes[] = { { "DRV", NULL, "INL" }, { "DRV", NULL, "INR" }, + { "OUTL", NULL, "VCC" }, + { "OUTR", NULL, "VCC" }, { "OUTL", NULL, "DRV" }, { "OUTR", NULL, "DRV" }, }; diff --git a/sound/soc/codecs/tas6424.c b/sound/soc/codecs/tas6424.c index 36aebdb8f55c..aaba39295079 100644 --- a/sound/soc/codecs/tas6424.c +++ b/sound/soc/codecs/tas6424.c @@ -378,7 +378,7 @@ static struct snd_soc_component_driver soc_codec_dev_tas6424 = { .non_legacy_dai_naming = 1, }; -static struct snd_soc_dai_ops tas6424_speaker_dai_ops = { +static const struct snd_soc_dai_ops tas6424_speaker_dai_ops = { .hw_params = tas6424_hw_params, .set_fmt = tas6424_set_dai_fmt, .set_tdm_slot = tas6424_set_dai_tdm_slot, diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index 608ad49ad978..c6048d95c6d3 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -1095,7 +1095,7 @@ static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai, if (freq/i > 20000000) { dev_err(aic31xx->dev, "%s: Too high mclk frequency %u\n", __func__, freq); - return -EINVAL; + return -EINVAL; } aic31xx->p_div = i; diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 6a271e6e6b8f..6aa0edf8c5ef 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1260,6 +1260,16 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai, aic3x->master = 0; iface_areg &= ~(BIT_CLK_MASTER | WORD_CLK_MASTER); break; + case SND_SOC_DAIFMT_CBM_CFS: + aic3x->master = 1; + iface_areg |= BIT_CLK_MASTER; + iface_areg &= ~WORD_CLK_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFM: + aic3x->master = 1; + iface_areg |= WORD_CLK_MASTER; + iface_areg &= ~BIT_CLK_MASTER; + break; default: return -EINVAL; } diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index a957eaeb7bc1..32907b1e20cf 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -394,7 +394,7 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) if (ret != 0) { dev_err(component->dev, "Failed to enable supplies: %d\n", ret); - goto exit; + goto exit; } if (dac33->power_gpio >= 0) diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c index 61294c787f27..409bed30a4e4 100644 --- a/sound/soc/codecs/wm8998.c +++ b/sound/soc/codecs/wm8998.c @@ -60,7 +60,7 @@ static int wm8998_asrc_ev(struct snd_soc_dapm_widget *w, dev_warn(component->dev, "Unsupported ASRC rate1 (%s)\n", arizona_sample_rate_val_to_name(val)); - return -EINVAL; + return -EINVAL; } break; default: diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c index ccdf088461b7..54c306707c02 100644 --- a/sound/soc/codecs/wm9705.c +++ b/sound/soc/codecs/wm9705.c @@ -325,8 +325,7 @@ static int wm9705_soc_probe(struct snd_soc_component *component) if (wm9705->mfd_pdata) { wm9705->ac97 = wm9705->mfd_pdata->ac97; regmap = wm9705->mfd_pdata->regmap; - } else { -#ifdef CONFIG_SND_SOC_AC97_BUS + } else if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS)) { wm9705->ac97 = snd_soc_new_ac97_component(component, WM9705_VENDOR_ID, WM9705_VENDOR_ID_MASK); if (IS_ERR(wm9705->ac97)) { @@ -339,7 +338,8 @@ static int wm9705_soc_probe(struct snd_soc_component *component) snd_soc_free_ac97_component(wm9705->ac97); return PTR_ERR(regmap); } -#endif + } else { + return -ENXIO; } snd_soc_component_set_drvdata(component, wm9705->ac97); @@ -350,14 +350,12 @@ static int wm9705_soc_probe(struct snd_soc_component *component) static void wm9705_soc_remove(struct snd_soc_component *component) { -#ifdef CONFIG_SND_SOC_AC97_BUS struct wm9705_priv *wm9705 = snd_soc_component_get_drvdata(component); - if (!wm9705->mfd_pdata) { + if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS) && !wm9705->mfd_pdata) { snd_soc_component_exit_regmap(component); snd_soc_free_ac97_component(wm9705->ac97); } -#endif } static const struct snd_soc_component_driver soc_component_dev_wm9705 = { diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index e873baa9e778..01949eaba4fd 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -642,8 +642,7 @@ static int wm9712_soc_probe(struct snd_soc_component *component) if (wm9712->mfd_pdata) { wm9712->ac97 = wm9712->mfd_pdata->ac97; regmap = wm9712->mfd_pdata->regmap; - } else { -#ifdef CONFIG_SND_SOC_AC97_BUS + } else if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS)) { int ret; wm9712->ac97 = snd_soc_new_ac97_component(component, WM9712_VENDOR_ID, @@ -660,7 +659,8 @@ static int wm9712_soc_probe(struct snd_soc_component *component) snd_soc_free_ac97_component(wm9712->ac97); return PTR_ERR(regmap); } -#endif + } else { + return -ENXIO; } snd_soc_component_init_regmap(component, regmap); @@ -673,14 +673,12 @@ static int wm9712_soc_probe(struct snd_soc_component *component) static void wm9712_soc_remove(struct snd_soc_component *component) { -#ifdef CONFIG_SND_SOC_AC97_BUS struct wm9712_priv *wm9712 = snd_soc_component_get_drvdata(component); - if (!wm9712->mfd_pdata) { + if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS) && !wm9712->mfd_pdata) { snd_soc_component_exit_regmap(component); snd_soc_free_ac97_component(wm9712->ac97); } -#endif } static const struct snd_soc_component_driver soc_component_dev_wm9712 = { diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 643863bb32e0..5a2fdf4f69bf 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -1214,8 +1214,7 @@ static int wm9713_soc_probe(struct snd_soc_component *component) if (wm9713->mfd_pdata) { wm9713->ac97 = wm9713->mfd_pdata->ac97; regmap = wm9713->mfd_pdata->regmap; - } else { -#ifdef CONFIG_SND_SOC_AC97_BUS + } else if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS)) { wm9713->ac97 = snd_soc_new_ac97_component(component, WM9713_VENDOR_ID, WM9713_VENDOR_ID_MASK); if (IS_ERR(wm9713->ac97)) @@ -1225,7 +1224,8 @@ static int wm9713_soc_probe(struct snd_soc_component *component) snd_soc_free_ac97_component(wm9713->ac97); return PTR_ERR(regmap); } -#endif + } else { + return -ENXIO; } snd_soc_component_init_regmap(component, regmap); @@ -1238,14 +1238,12 @@ static int wm9713_soc_probe(struct snd_soc_component *component) static void wm9713_soc_remove(struct snd_soc_component *component) { -#ifdef CONFIG_SND_SOC_AC97_BUS struct wm9713_priv *wm9713 = snd_soc_component_get_drvdata(component); - if (!wm9713->mfd_pdata) { + if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS) && !wm9713->mfd_pdata) { snd_soc_component_exit_regmap(component); snd_soc_free_ac97_component(wm9713->ac97); } -#endif } static const struct snd_soc_component_driver soc_component_dev_wm9713 = { diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 66501b8dc46f..1dd291cebe67 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -2419,7 +2419,7 @@ static int wm_adsp_create_name(struct wm_adsp *dsp) return 0; } -int wm_adsp1_init(struct wm_adsp *dsp) +static int wm_adsp_common_init(struct wm_adsp *dsp) { int ret; @@ -2428,11 +2428,17 @@ int wm_adsp1_init(struct wm_adsp *dsp) return ret; INIT_LIST_HEAD(&dsp->alg_regions); + INIT_LIST_HEAD(&dsp->ctl_list); mutex_init(&dsp->pwr_lock); return 0; } + +int wm_adsp1_init(struct wm_adsp *dsp) +{ + return wm_adsp_common_init(dsp); +} EXPORT_SYMBOL_GPL(wm_adsp1_init); int wm_adsp1_event(struct snd_soc_dapm_widget *w, @@ -2917,7 +2923,7 @@ int wm_adsp2_init(struct wm_adsp *dsp) { int ret; - ret = wm_adsp_create_name(dsp); + ret = wm_adsp_common_init(dsp); if (ret) return ret; @@ -2939,12 +2945,8 @@ int wm_adsp2_init(struct wm_adsp *dsp) break; } - INIT_LIST_HEAD(&dsp->alg_regions); - INIT_LIST_HEAD(&dsp->ctl_list); INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work); - mutex_init(&dsp->pwr_lock); - return 0; } EXPORT_SYMBOL_GPL(wm_adsp2_init); diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig deleted file mode 100644 index 778faff28e0e..000000000000 --- a/sound/soc/davinci/Kconfig +++ /dev/null @@ -1,106 +0,0 @@ -config SND_DAVINCI_SOC - tristate - depends on ARCH_DAVINCI - select SND_EDMA_SOC - -config SND_EDMA_SOC - tristate "SoC Audio for Texas Instruments chips using eDMA" - depends on TI_EDMA - select SND_SOC_GENERIC_DMAENGINE_PCM - help - Say Y or M here if you want audio support for TI SoC which uses eDMA. - The following line of SoCs are supported by this platform driver: - - daVinci devices - - AM335x - - AM437x/AM438x - - DRA7xx family - -config SND_DAVINCI_SOC_I2S - tristate "DaVinci Multichannel Buffered Serial Port (McBSP) support" - depends on SND_EDMA_SOC - help - Say Y or M here if you want to have support for McBSP IP found in - Texas Instruments DaVinci DA850 SoCs. - -config SND_DAVINCI_SOC_MCASP - tristate "Multichannel Audio Serial Port (McASP) support" - depends on SND_SDMA_SOC || SND_EDMA_SOC - help - Say Y or M here if you want to have support for McASP IP found in - various Texas Instruments SoCs like: - - daVinci devices - - Sitara line of SoCs (AM335x, AM438x, etc) - - DRA7x devices - -config SND_DAVINCI_SOC_VCIF - tristate - -config SND_DAVINCI_SOC_GENERIC_EVM - tristate - select SND_SOC_TLV320AIC3X - select SND_DAVINCI_SOC_MCASP - -config SND_AM33XX_SOC_EVM - tristate "SoC Audio for the AM33XX chip based boards" - depends on SND_EDMA_SOC && SOC_AM33XX && I2C - select SND_DAVINCI_SOC_GENERIC_EVM - help - Say Y or M if you want to add support for SoC audio on AM33XX - boards using McASP and TLV320AIC3X codec. For example AM335X-EVM, - AM335X-EVMSK, and BeagelBone with AudioCape boards have this - setup. - -config SND_DAVINCI_SOC_EVM - tristate "SoC Audio support for DaVinci DM6446, DM355 or DM365 EVM" - depends on SND_EDMA_SOC && I2C - depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM || MACH_DAVINCI_DM365_EVM - select SND_DAVINCI_SOC_GENERIC_EVM - help - Say Y if you want to add support for SoC audio on TI - DaVinci DM6446, DM355 or DM365 EVM platforms. - -choice - prompt "DM365 codec select" - depends on SND_DAVINCI_SOC_EVM - depends on MACH_DAVINCI_DM365_EVM - -config SND_DM365_AIC3X_CODEC - tristate "Audio Codec - AIC3101" - help - Say Y if you want to add support for AIC3101 audio codec - -config SND_DM365_VOICE_CODEC - tristate "Voice Codec - CQ93VC" - select MFD_DAVINCI_VOICECODEC - select SND_DAVINCI_SOC_VCIF - select SND_SOC_CQ0093VC - help - Say Y if you want to add support for SoC On-chip voice codec -endchoice - -config SND_DM6467_SOC_EVM - tristate "SoC Audio support for DaVinci DM6467 EVM" - depends on SND_EDMA_SOC && MACH_DAVINCI_DM6467_EVM && I2C - select SND_DAVINCI_SOC_GENERIC_EVM - select SND_SOC_SPDIF - - help - Say Y if you want to add support for SoC audio on TI - -config SND_DA830_SOC_EVM - tristate "SoC Audio support for DA830/OMAP-L137 EVM" - depends on SND_EDMA_SOC && MACH_DAVINCI_DA830_EVM && I2C - select SND_DAVINCI_SOC_GENERIC_EVM - - help - Say Y if you want to add support for SoC audio on TI - DA830/OMAP-L137 EVM - -config SND_DA850_SOC_EVM - tristate "SoC Audio support for DA850/OMAP-L138 EVM" - depends on SND_EDMA_SOC && MACH_DAVINCI_DA850_EVM && I2C - select SND_DAVINCI_SOC_GENERIC_EVM - help - Say Y if you want to add support for SoC audio on TI - DA850/OMAP-L138 EVM - diff --git a/sound/soc/davinci/Makefile b/sound/soc/davinci/Makefile deleted file mode 100644 index 23c6592eb31a..000000000000 --- a/sound/soc/davinci/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# DAVINCI Platform Support -snd-soc-edma-objs := edma-pcm.o -snd-soc-davinci-i2s-objs := davinci-i2s.o -snd-soc-davinci-mcasp-objs:= davinci-mcasp.o -snd-soc-davinci-vcif-objs:= davinci-vcif.o - -obj-$(CONFIG_SND_EDMA_SOC) += snd-soc-edma.o -obj-$(CONFIG_SND_DAVINCI_SOC_I2S) += snd-soc-davinci-i2s.o -obj-$(CONFIG_SND_DAVINCI_SOC_MCASP) += snd-soc-davinci-mcasp.o -obj-$(CONFIG_SND_DAVINCI_SOC_VCIF) += snd-soc-davinci-vcif.o - -# Generic DAVINCI/AM33xx Machine Support -snd-soc-evm-objs := davinci-evm.o - -obj-$(CONFIG_SND_DAVINCI_SOC_GENERIC_EVM) += snd-soc-evm.o diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 6ec19fb4a934..2e75b5bc5f1d 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -221,7 +221,7 @@ config SND_SOC_PHYCORE_AC97 config SND_SOC_EUKREA_TLV320 tristate "Eukrea TLV320" - depends on ARCH_MXC && I2C + depends on ARCH_MXC && !ARM64 && I2C select SND_SOC_TLV320AIC23_I2C select SND_SOC_IMX_AUDMUX select SND_SOC_IMX_SSI diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 44433b20435c..81f2fe2c6d23 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -571,17 +571,17 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) } /* Common settings for corresponding Freescale CPU DAI driver */ - if (strstr(cpu_np->name, "ssi")) { + if (of_node_name_eq(cpu_np, "ssi")) { /* Only SSI needs to configure AUDMUX */ ret = fsl_asoc_card_audmux_init(np, priv); if (ret) { dev_err(&pdev->dev, "failed to init audmux\n"); goto asrc_fail; } - } else if (strstr(cpu_np->name, "esai")) { + } else if (of_node_name_eq(cpu_np, "esai")) { priv->cpu_priv.sysclk_id[1] = ESAI_HCKT_EXTAL; priv->cpu_priv.sysclk_id[0] = ESAI_HCKR_EXTAL; - } else if (strstr(cpu_np->name, "sai")) { + } else if (of_node_name_eq(cpu_np, "sai")) { priv->cpu_priv.sysclk_id[1] = FSL_SAI_CLK_MAST1; priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1; } diff --git a/sound/soc/fsl/fsl_ssi_dbg.c b/sound/soc/fsl/fsl_ssi_dbg.c index 1255dfe19eef..6f6294149476 100644 --- a/sound/soc/fsl/fsl_ssi_dbg.c +++ b/sound/soc/fsl/fsl_ssi_dbg.c @@ -124,17 +124,7 @@ static int fsl_ssi_stats_show(struct seq_file *s, void *unused) return 0; } -static int fsl_ssi_stats_open(struct inode *inode, struct file *file) -{ - return single_open(file, fsl_ssi_stats_show, inode->i_private); -} - -static const struct file_operations fsl_ssi_stats_ops = { - .open = fsl_ssi_stats_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(fsl_ssi_stats); int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg, struct device *dev) { @@ -144,7 +134,7 @@ int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg, struct device *dev) ssi_dbg->dbg_stats = debugfs_create_file("stats", 0444, ssi_dbg->dbg_dir, ssi_dbg, - &fsl_ssi_stats_ops); + &fsl_ssi_stats_fops); if (!ssi_dbg->dbg_stats) { debugfs_remove(ssi_dbg->dbg_dir); return -ENOMEM; diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig index c954be0a0f96..92c2cf06f40a 100644 --- a/sound/soc/generic/Kconfig +++ b/sound/soc/generic/Kconfig @@ -6,6 +6,7 @@ config SND_SIMPLE_CARD select SND_SIMPLE_CARD_UTILS help This option enables generic simple sound card support + It also support DPCM of multi CPU single Codec ststem. config SND_SIMPLE_SCU_CARD tristate "ASoC Simple SCU sound card support" @@ -20,8 +21,9 @@ config SND_AUDIO_GRAPH_CARD depends on OF select SND_SIMPLE_CARD_UTILS help - This option enables generic simple simple sound card support + This option enables generic simple sound card support with OF-graph DT bindings. + It also support DPCM of multi CPU single Codec ststem. config SND_AUDIO_GRAPH_SCU_CARD tristate "ASoC Audio Graph SCU sound card support" diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 25c819e402e1..0d6144560a1e 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -23,19 +23,29 @@ struct graph_card_data { struct snd_soc_card snd_card; struct graph_dai_props { - struct asoc_simple_dai cpu_dai; - struct asoc_simple_dai codec_dai; + struct asoc_simple_dai *cpu_dai; + struct asoc_simple_dai *codec_dai; struct snd_soc_dai_link_component codecs; /* single codec */ struct snd_soc_dai_link_component platform; + struct asoc_simple_card_data adata; + struct snd_soc_codec_conf *codec_conf; unsigned int mclk_fs; } *dai_props; - unsigned int mclk_fs; struct asoc_simple_jack hp_jack; struct asoc_simple_jack mic_jack; struct snd_soc_dai_link *dai_link; + struct asoc_simple_dai *dais; + struct snd_soc_codec_conf *codec_conf; struct gpio_desc *pa_gpio; }; +#define graph_priv_to_card(priv) (&(priv)->snd_card) +#define graph_priv_to_props(priv, i) ((priv)->dai_props + (i)) +#define graph_priv_to_dev(priv) (graph_priv_to_card(priv)->dev) +#define graph_priv_to_link(priv, i) (graph_priv_to_card(priv)->dai_link + (i)) + +#define PREFIX "audio-graph-card," + static int asoc_graph_card_outdrv_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) @@ -63,11 +73,6 @@ static const struct snd_soc_dapm_widget asoc_graph_card_dapm_widgets[] = { SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), }; -#define graph_priv_to_card(priv) (&(priv)->snd_card) -#define graph_priv_to_props(priv, i) ((priv)->dai_props + (i)) -#define graph_priv_to_dev(priv) (graph_priv_to_card(priv)->dev) -#define graph_priv_to_link(priv, i) (graph_priv_to_card(priv)->dai_link + (i)) - static int asoc_graph_card_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -75,13 +80,13 @@ static int asoc_graph_card_startup(struct snd_pcm_substream *substream) struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); int ret; - ret = asoc_simple_card_clk_enable(&dai_props->cpu_dai); + ret = asoc_simple_card_clk_enable(dai_props->cpu_dai); if (ret) return ret; - ret = asoc_simple_card_clk_enable(&dai_props->codec_dai); + ret = asoc_simple_card_clk_enable(dai_props->codec_dai); if (ret) - asoc_simple_card_clk_disable(&dai_props->cpu_dai); + asoc_simple_card_clk_disable(dai_props->cpu_dai); return ret; } @@ -92,9 +97,9 @@ static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream) struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); - asoc_simple_card_clk_disable(&dai_props->cpu_dai); + asoc_simple_card_clk_disable(dai_props->cpu_dai); - asoc_simple_card_clk_disable(&dai_props->codec_dai); + asoc_simple_card_clk_disable(dai_props->codec_dai); } static int asoc_graph_card_hw_params(struct snd_pcm_substream *substream, @@ -108,9 +113,7 @@ static int asoc_graph_card_hw_params(struct snd_pcm_substream *substream, unsigned int mclk, mclk_fs = 0; int ret = 0; - if (priv->mclk_fs) - mclk_fs = priv->mclk_fs; - else if (dai_props->mclk_fs) + if (dai_props->mclk_fs) mclk_fs = dai_props->mclk_fs; if (mclk_fs) { @@ -139,86 +142,238 @@ static const struct snd_soc_ops asoc_graph_card_ops = { static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd) { struct graph_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 graph_dai_props *dai_props = - graph_priv_to_props(priv, rtd->num); - int ret; + struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); + int ret = 0; - ret = asoc_simple_card_init_dai(codec, &dai_props->codec_dai); + ret = asoc_simple_card_init_dai(rtd->codec_dai, + dai_props->codec_dai); if (ret < 0) return ret; - ret = asoc_simple_card_init_dai(cpu, &dai_props->cpu_dai); + ret = asoc_simple_card_init_dai(rtd->cpu_dai, + dai_props->cpu_dai); if (ret < 0) return ret; return 0; } -static int asoc_graph_card_dai_link_of(struct device_node *cpu_port, - struct graph_card_data *priv, - int idx) +static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); + + asoc_simple_card_convert_fixup(&dai_props->adata, params); + + return 0; +} + +static int asoc_graph_card_dai_link_of_dpcm(struct device_node *top, + struct device_node *cpu_ep, + struct device_node *codec_ep, + struct graph_card_data *priv, + int *dai_idx, int link_idx, + int *conf_idx, int is_cpu) { struct device *dev = graph_priv_to_dev(priv); - struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, idx); - struct graph_dai_props *dai_props = graph_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_ep = of_get_next_child(cpu_port, NULL); - struct device_node *codec_ep = of_graph_get_remote_endpoint(cpu_ep); - struct device_node *rcpu_ep = of_graph_get_remote_endpoint(codec_ep); + struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, link_idx); + struct graph_dai_props *dai_props = graph_priv_to_props(priv, link_idx); + struct device_node *ep = is_cpu ? cpu_ep : codec_ep; + struct device_node *port = of_get_parent(ep); + struct device_node *ports = of_get_parent(port); + struct device_node *node = of_graph_get_port_parent(ep); + struct asoc_simple_dai *dai; + struct snd_soc_dai_link_component *codecs = dai_link->codecs; int ret; - if (rcpu_ep != cpu_ep) { - dev_err(dev, "remote-endpoint mismatch (%s/%s/%s)\n", - cpu_ep->name, codec_ep->name, rcpu_ep->name); - ret = -EINVAL; - goto dai_link_of_err; + dev_dbg(dev, "link_of DPCM (for %s)\n", is_cpu ? "CPU" : "Codec"); + + of_property_read_u32(top, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(ports, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(port, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(ep, "mclk-fs", &dai_props->mclk_fs); + + asoc_simple_card_parse_convert(dev, top, NULL, &dai_props->adata); + asoc_simple_card_parse_convert(dev, node, PREFIX, &dai_props->adata); + asoc_simple_card_parse_convert(dev, ports, NULL, &dai_props->adata); + asoc_simple_card_parse_convert(dev, port, NULL, &dai_props->adata); + asoc_simple_card_parse_convert(dev, ep, NULL, &dai_props->adata); + + of_node_put(ports); + of_node_put(port); + + if (is_cpu) { + + /* BE is dummy */ + codecs->of_node = NULL; + codecs->dai_name = "snd-soc-dummy-dai"; + codecs->name = "snd-soc-dummy"; + + /* FE settings */ + dai_link->dynamic = 1; + dai_link->dpcm_merged_format = 1; + + dai = + dai_props->cpu_dai = &priv->dais[(*dai_idx)++]; + + ret = asoc_simple_card_parse_graph_cpu(ep, dai_link); + if (ret) + return ret; + + ret = asoc_simple_card_parse_clk_cpu(dev, ep, dai_link, dai); + 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; + + /* card->num_links includes Codec */ + asoc_simple_card_canonicalize_cpu(dai_link, + of_graph_get_endpoint_count(dai_link->cpu_of_node) == 1); + } else { + struct snd_soc_codec_conf *cconf; + + /* 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_graph_card_be_hw_params_fixup; + + dai = + dai_props->codec_dai = &priv->dais[(*dai_idx)++]; + + cconf = + dai_props->codec_conf = &priv->codec_conf[(*conf_idx)++]; + + ret = asoc_simple_card_parse_graph_codec(ep, dai_link); + if (ret < 0) + return ret; + + ret = asoc_simple_card_parse_clk_codec(dev, ep, dai_link, dai); + if (ret < 0) + return ret; + + ret = asoc_simple_card_set_dailink_name(dev, dai_link, + "be.%s", + codecs->dai_name); + if (ret < 0) + return ret; + + /* check "prefix" from top node */ + snd_soc_of_parse_node_prefix(top, cconf, codecs->of_node, + "prefix"); + snd_soc_of_parse_node_prefix(node, cconf, codecs->of_node, + PREFIX "prefix"); + snd_soc_of_parse_node_prefix(ports, cconf, codecs->of_node, + "prefix"); + snd_soc_of_parse_node_prefix(port, cconf, codecs->of_node, + "prefix"); } + ret = asoc_simple_card_of_parse_tdm(ep, dai); + if (ret) + return ret; + + ret = asoc_simple_card_canonicalize_dailink(dai_link); + if (ret < 0) + return ret; + ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep, NULL, &dai_link->dai_fmt); if (ret < 0) - goto dai_link_of_err; + return ret; + + dai_link->dpcm_playback = 1; + dai_link->dpcm_capture = 1; + dai_link->ops = &asoc_graph_card_ops; + dai_link->init = asoc_graph_card_dai_init; + + return 0; +} + +static int asoc_graph_card_dai_link_of(struct device_node *top, + struct device_node *cpu_ep, + struct device_node *codec_ep, + struct graph_card_data *priv, + int *dai_idx, int link_idx) +{ + struct device *dev = graph_priv_to_dev(priv); + struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, link_idx); + struct graph_dai_props *dai_props = graph_priv_to_props(priv, link_idx); + struct device_node *cpu_port = of_get_parent(cpu_ep); + struct device_node *codec_port = of_get_parent(codec_ep); + struct device_node *cpu_ports = of_get_parent(cpu_port); + struct device_node *codec_ports = of_get_parent(codec_port); + struct asoc_simple_dai *cpu_dai; + struct asoc_simple_dai *codec_dai; + int ret; + + dev_dbg(dev, "link_of\n"); + + cpu_dai = + dai_props->cpu_dai = &priv->dais[(*dai_idx)++]; + codec_dai = + dai_props->codec_dai = &priv->dais[(*dai_idx)++]; - of_property_read_u32(cpu_ep, "mclk-fs", &dai_props->mclk_fs); - of_property_read_u32(codec_ep, "mclk-fs", &dai_props->mclk_fs); + /* Factor to mclk, used in hw_params() */ + of_property_read_u32(top, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(cpu_ports, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(codec_ports, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(cpu_port, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(codec_port, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(cpu_ep, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(codec_ep, "mclk-fs", &dai_props->mclk_fs); + of_node_put(cpu_port); + of_node_put(cpu_ports); + of_node_put(codec_port); + of_node_put(codec_ports); + + ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep, + NULL, &dai_link->dai_fmt); + if (ret < 0) + return ret; ret = asoc_simple_card_parse_graph_cpu(cpu_ep, dai_link); if (ret < 0) - goto dai_link_of_err; + return ret; ret = asoc_simple_card_parse_graph_codec(codec_ep, dai_link); if (ret < 0) - goto dai_link_of_err; + return ret; ret = asoc_simple_card_of_parse_tdm(cpu_ep, cpu_dai); if (ret < 0) - goto dai_link_of_err; + return ret; ret = asoc_simple_card_of_parse_tdm(codec_ep, codec_dai); if (ret < 0) - goto dai_link_of_err; + return ret; ret = asoc_simple_card_parse_clk_cpu(dev, cpu_ep, dai_link, cpu_dai); if (ret < 0) - goto dai_link_of_err; + return ret; ret = asoc_simple_card_parse_clk_codec(dev, codec_ep, dai_link, codec_dai); if (ret < 0) - goto dai_link_of_err; + return ret; ret = asoc_simple_card_canonicalize_dailink(dai_link); if (ret < 0) - goto dai_link_of_err; + return ret; ret = asoc_simple_card_set_dailink_name(dev, dai_link, "%s-%s", dai_link->cpu_dai_name, dai_link->codecs->dai_name); if (ret < 0) - goto dai_link_of_err; + return ret; dai_link->ops = &asoc_graph_card_ops; dai_link->init = asoc_graph_card_dai_init; @@ -226,12 +381,7 @@ static int asoc_graph_card_dai_link_of(struct device_node *cpu_port, asoc_simple_card_canonicalize_cpu(dai_link, of_graph_get_endpoint_count(dai_link->cpu_of_node) == 1); -dai_link_of_err: - of_node_put(cpu_ep); - of_node_put(rcpu_ep); - of_node_put(codec_ep); - - return ret; + return 0; } static int asoc_graph_card_parse_of(struct graph_card_data *priv) @@ -239,44 +389,173 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) struct of_phandle_iterator it; struct device *dev = graph_priv_to_dev(priv); struct snd_soc_card *card = graph_priv_to_card(priv); - struct device_node *node = dev->of_node; - int rc, idx = 0; - int ret; + struct device_node *top = dev->of_node; + struct device_node *node = top; + struct device_node *cpu_port; + struct device_node *cpu_ep = NULL; + struct device_node *codec_ep = NULL; + struct device_node *codec_port = NULL; + struct device_node *codec_port_old = NULL; + int rc, ret; + int link_idx, dai_idx, conf_idx; + int cpu; ret = asoc_simple_card_of_parse_widgets(card, NULL); if (ret < 0) return ret; - ret = asoc_simple_card_of_parse_routing(card, NULL, 1); + ret = asoc_simple_card_of_parse_routing(card, NULL); if (ret < 0) return ret; - /* Factor to mclk, used in hw_params() */ - of_property_read_u32(node, "mclk-fs", &priv->mclk_fs); - - of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { - ret = asoc_graph_card_dai_link_of(it.node, priv, idx++); - if (ret < 0) { - of_node_put(it.node); - - return ret; + link_idx = 0; + dai_idx = 0; + conf_idx = 0; + codec_port_old = NULL; + for (cpu = 1; cpu >= 0; cpu--) { + /* + * Detect all CPU first, and Detect all Codec 2nd. + * + * In Normal sound case, all DAIs are detected + * as "CPU-Codec". + * + * In DPCM sound case, + * all CPUs are detected as "CPU-dummy", and + * all Codecs are detected as "dummy-Codec". + * To avoid random sub-device numbering, + * detect "dummy-Codec" in last; + */ + of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { + cpu_port = it.node; + cpu_ep = NULL; + while (1) { + cpu_ep = of_get_next_child(cpu_port, cpu_ep); + if (!cpu_ep) + break; + + codec_ep = of_graph_get_remote_endpoint(cpu_ep); + codec_port = of_get_parent(codec_ep); + + of_node_put(codec_ep); + of_node_put(codec_port); + + dev_dbg(dev, "%pOFf <-> %pOFf\n", cpu_ep, codec_ep); + + if (of_get_child_count(codec_port) > 1) { + /* + * for DPCM sound + */ + if (!cpu) { + if (codec_port_old == codec_port) + continue; + codec_port_old = codec_port; + } + ret = asoc_graph_card_dai_link_of_dpcm( + top, cpu_ep, codec_ep, priv, + &dai_idx, link_idx++, + &conf_idx, cpu); + } else if (cpu) { + /* + * for Normal sound + */ + ret = asoc_graph_card_dai_link_of( + top, cpu_ep, codec_ep, priv, + &dai_idx, link_idx++); + } + if (ret < 0) + return ret; + } } } return asoc_simple_card_parse_card_name(card, NULL); } -static int asoc_graph_get_dais_count(struct device *dev) +static void asoc_graph_get_dais_count(struct device *dev, + int *link_num, + int *dais_num, + int *ccnf_num) { struct of_phandle_iterator it; struct device_node *node = dev->of_node; - int count = 0; + struct device_node *cpu_port; + struct device_node *cpu_ep; + struct device_node *codec_ep; + struct device_node *codec_port; + struct device_node *codec_port_old; + struct device_node *codec_port_old2; int rc; - of_for_each_phandle(&it, rc, node, "dais", NULL, 0) - count++; - - return count; + /* + * link_num : number of links. + * CPU-Codec / CPU-dummy / dummy-Codec + * dais_num : number of DAIs + * ccnf_num : number of codec_conf + * same number for "dummy-Codec" + * + * ex1) + * CPU0 --- Codec0 link : 5 + * CPU1 --- Codec1 dais : 7 + * CPU2 -/ ccnf : 1 + * CPU3 --- Codec2 + * + * => 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec + * => 7 DAIs = 4xCPU + 3xCodec + * => 1 ccnf = 1xdummy-Codec + * + * ex2) + * CPU0 --- Codec0 link : 5 + * CPU1 --- Codec1 dais : 6 + * CPU2 -/ ccnf : 1 + * CPU3 -/ + * + * => 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec + * => 6 DAIs = 4xCPU + 2xCodec + * => 1 ccnf = 1xdummy-Codec + * + * ex3) + * CPU0 --- Codec0 link : 6 + * CPU1 -/ dais : 6 + * CPU2 --- Codec1 ccnf : 2 + * CPU3 -/ + * + * => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec + * => 6 DAIs = 4xCPU + 2xCodec + * => 2 ccnf = 2xdummy-Codec + */ + codec_port_old = NULL; + codec_port_old2 = NULL; + of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { + cpu_port = it.node; + cpu_ep = NULL; + while (1) { + cpu_ep = of_get_next_child(cpu_port, cpu_ep); + if (!cpu_ep) + break; + + codec_ep = of_graph_get_remote_endpoint(cpu_ep); + codec_port = of_get_parent(codec_ep); + + of_node_put(codec_ep); + of_node_put(codec_port); + + (*link_num)++; + (*dais_num)++; + + if (codec_port_old == codec_port) { + if (codec_port_old2 != codec_port_old) { + (*link_num)++; + (*ccnf_num)++; + } + + codec_port_old2 = codec_port_old; + continue; + } + + (*dais_num)++; + codec_port_old = codec_port; + } + } } static int asoc_graph_soc_card_probe(struct snd_soc_card *card) @@ -300,22 +579,27 @@ static int asoc_graph_card_probe(struct platform_device *pdev) struct graph_card_data *priv; struct snd_soc_dai_link *dai_link; struct graph_dai_props *dai_props; + struct asoc_simple_dai *dais; struct device *dev = &pdev->dev; struct snd_soc_card *card; - int num, ret, i; + struct snd_soc_codec_conf *cconf; + int lnum = 0, dnum = 0, cnum = 0; + int ret, i; /* Allocate the private data and the DAI link array */ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - num = asoc_graph_get_dais_count(dev); - if (num == 0) + asoc_graph_get_dais_count(dev, &lnum, &dnum, &cnum); + if (!lnum || !dnum) return -EINVAL; - dai_props = devm_kcalloc(dev, num, sizeof(*dai_props), GFP_KERNEL); - dai_link = devm_kcalloc(dev, num, sizeof(*dai_link), GFP_KERNEL); - if (!dai_props || !dai_link) + dai_props = devm_kcalloc(dev, lnum, sizeof(*dai_props), GFP_KERNEL); + dai_link = devm_kcalloc(dev, lnum, sizeof(*dai_link), GFP_KERNEL); + dais = devm_kcalloc(dev, dnum, sizeof(*dais), GFP_KERNEL); + cconf = devm_kcalloc(dev, cnum, sizeof(*cconf), GFP_KERNEL); + if (!dai_props || !dai_link || !dais) return -ENOMEM; /* @@ -324,7 +608,7 @@ static int asoc_graph_card_probe(struct platform_device *pdev) * see * soc-core.c :: snd_soc_init_multicodec() */ - for (i = 0; i < num; i++) { + for (i = 0; i < lnum; i++) { dai_link[i].codecs = &dai_props[i].codecs; dai_link[i].num_codecs = 1; dai_link[i].platform = &dai_props[i].platform; @@ -339,16 +623,20 @@ static int asoc_graph_card_probe(struct platform_device *pdev) priv->dai_props = dai_props; priv->dai_link = dai_link; + priv->dais = dais; + priv->codec_conf = cconf; /* Init snd_soc_card */ card = graph_priv_to_card(priv); - card->owner = THIS_MODULE; - card->dev = dev; - card->dai_link = dai_link; - card->num_links = num; - card->dapm_widgets = asoc_graph_card_dapm_widgets; - card->num_dapm_widgets = ARRAY_SIZE(asoc_graph_card_dapm_widgets); - card->probe = asoc_graph_soc_card_probe; + card->owner = THIS_MODULE; + card->dev = dev; + card->dai_link = dai_link; + card->num_links = lnum; + card->dapm_widgets = asoc_graph_card_dapm_widgets; + card->num_dapm_widgets = ARRAY_SIZE(asoc_graph_card_dapm_widgets); + card->probe = asoc_graph_soc_card_probe; + card->codec_conf = cconf; + card->num_configs = cnum; ret = asoc_graph_card_parse_of(priv); if (ret < 0) { @@ -379,6 +667,7 @@ static int asoc_graph_card_remove(struct platform_device *pdev) static const struct of_device_id asoc_graph_of_match[] = { { .compatible = "audio-graph-card", }, + { .compatible = "audio-graph-scu-card", }, {}, }; MODULE_DEVICE_TABLE(of, asoc_graph_of_match); diff --git a/sound/soc/generic/audio-graph-scu-card.c b/sound/soc/generic/audio-graph-scu-card.c index b83bb31021a9..e1b192ea147b 100644 --- a/sound/soc/generic/audio-graph-scu-card.c +++ b/sound/soc/generic/audio-graph-scu-card.c @@ -24,14 +24,18 @@ struct graph_card_data { struct snd_soc_card snd_card; - struct snd_soc_codec_conf codec_conf; struct graph_dai_props { - struct asoc_simple_dai dai; + struct asoc_simple_dai *cpu_dai; + struct asoc_simple_dai *codec_dai; struct snd_soc_dai_link_component codecs; struct snd_soc_dai_link_component platform; + struct asoc_simple_card_data adata; + struct snd_soc_codec_conf *codec_conf; } *dai_props; struct snd_soc_dai_link *dai_link; + struct asoc_simple_dai *dais; struct asoc_simple_card_data adata; + struct snd_soc_codec_conf *codec_conf; }; #define graph_priv_to_card(priv) (&(priv)->snd_card) @@ -39,13 +43,24 @@ struct graph_card_data { #define graph_priv_to_dev(priv) (graph_priv_to_card(priv)->dev) #define graph_priv_to_link(priv, i) (graph_priv_to_card(priv)->dai_link + (i)) +#define PREFIX "audio-graph-card," + static int asoc_graph_card_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); + int ret = 0; + + ret = asoc_simple_card_clk_enable(dai_props->cpu_dai); + if (ret) + return ret; - return asoc_simple_card_clk_enable(&dai_props->dai); + ret = asoc_simple_card_clk_enable(dai_props->codec_dai); + if (ret) + asoc_simple_card_clk_disable(dai_props->cpu_dai); + + return ret; } static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream) @@ -54,7 +69,9 @@ static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream) struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); - asoc_simple_card_clk_disable(&dai_props->dai); + asoc_simple_card_clk_disable(dai_props->cpu_dai); + + asoc_simple_card_clk_disable(dai_props->codec_dai); } static const struct snd_soc_ops asoc_graph_card_ops = { @@ -65,39 +82,49 @@ static const struct snd_soc_ops asoc_graph_card_ops = { static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd) { struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *dai; - struct snd_soc_dai_link *dai_link; - struct graph_dai_props *dai_props; - int num = rtd->num; + struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); + int ret = 0; - dai_link = graph_priv_to_link(priv, num); - dai_props = graph_priv_to_props(priv, num); - dai = dai_link->dynamic ? - rtd->cpu_dai : - rtd->codec_dai; + ret = asoc_simple_card_init_dai(rtd->codec_dai, + dai_props->codec_dai); + if (ret < 0) + return ret; - return asoc_simple_card_init_dai(dai, &dai_props->dai); + ret = asoc_simple_card_init_dai(rtd->cpu_dai, + dai_props->cpu_dai); + if (ret < 0) + return ret; + + return 0; } static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); + + asoc_simple_card_convert_fixup(&dai_props->adata, params); + /* overwrite by top level adata if exist */ asoc_simple_card_convert_fixup(&priv->adata, params); return 0; } -static int asoc_graph_card_dai_link_of(struct device_node *ep, +static int asoc_graph_card_dai_link_of(struct device_node *cpu_ep, + struct device_node *codec_ep, struct graph_card_data *priv, - unsigned int daifmt, - int idx, int is_fe) + int *dai_idx, int link_idx, + int *conf_idx, int is_fe) { struct device *dev = graph_priv_to_dev(priv); - struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, idx); - struct graph_dai_props *dai_props = graph_priv_to_props(priv, idx); + struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, link_idx); + struct graph_dai_props *dai_props = graph_priv_to_props(priv, link_idx); struct snd_soc_card *card = graph_priv_to_card(priv); + struct device_node *ep = is_fe ? cpu_ep : codec_ep; + struct device_node *node = of_graph_get_port_parent(ep); + struct asoc_simple_dai *dai; int ret; if (is_fe) { @@ -113,11 +140,14 @@ static int asoc_graph_card_dai_link_of(struct device_node *ep, dai_link->dynamic = 1; dai_link->dpcm_merged_format = 1; + dai = + dai_props->cpu_dai = &priv->dais[(*dai_idx)++]; + ret = asoc_simple_card_parse_graph_cpu(ep, dai_link); if (ret) return ret; - ret = asoc_simple_card_parse_clk_cpu(dev, ep, dai_link, &dai_props->dai); + ret = asoc_simple_card_parse_clk_cpu(dev, ep, dai_link, dai); if (ret < 0) return ret; @@ -131,6 +161,8 @@ static int asoc_graph_card_dai_link_of(struct device_node *ep, asoc_simple_card_canonicalize_cpu(dai_link, of_graph_get_endpoint_count(dai_link->cpu_of_node) == 1); } else { + struct snd_soc_codec_conf *cconf; + /* FE is dummy */ dai_link->cpu_of_node = NULL; dai_link->cpu_dai_name = "snd-soc-dummy-dai"; @@ -140,11 +172,17 @@ static int asoc_graph_card_dai_link_of(struct device_node *ep, dai_link->no_pcm = 1; dai_link->be_hw_params_fixup = asoc_graph_card_be_hw_params_fixup; + dai = + dai_props->codec_dai = &priv->dais[(*dai_idx)++]; + + cconf = + dai_props->codec_conf = &priv->codec_conf[(*conf_idx)++]; + ret = asoc_simple_card_parse_graph_codec(ep, dai_link); if (ret < 0) return ret; - ret = asoc_simple_card_parse_clk_codec(dev, ep, dai_link, &dai_props->dai); + ret = asoc_simple_card_parse_clk_codec(dev, ep, dai_link, dai); if (ret < 0) return ret; @@ -154,13 +192,20 @@ static int asoc_graph_card_dai_link_of(struct device_node *ep, if (ret < 0) return ret; - snd_soc_of_parse_audio_prefix(card, - &priv->codec_conf, + /* check "prefix" from top node */ + snd_soc_of_parse_audio_prefix(card, cconf, dai_link->codecs->of_node, "prefix"); + /* check "prefix" from each node if top doesn't have */ + if (!cconf->of_node) + snd_soc_of_parse_node_prefix(node, cconf, + dai_link->codecs->of_node, + PREFIX "prefix"); } - ret = asoc_simple_card_of_parse_tdm(ep, &dai_props->dai); + asoc_simple_card_parse_convert(dev, node, PREFIX, &dai_props->adata); + + ret = asoc_simple_card_of_parse_tdm(ep, dai); if (ret) return ret; @@ -168,7 +213,11 @@ static int asoc_graph_card_dai_link_of(struct device_node *ep, if (ret < 0) return ret; - dai_link->dai_fmt = daifmt; + ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep, + NULL, &dai_link->dai_fmt); + if (ret < 0) + return ret; + dai_link->dpcm_playback = 1; dai_link->dpcm_capture = 1; dai_link->ops = &asoc_graph_card_ops; @@ -186,11 +235,9 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) struct device_node *cpu_port; struct device_node *cpu_ep; struct device_node *codec_ep; - struct device_node *rcpu_ep; struct device_node *codec_port; struct device_node *codec_port_old; - unsigned int daifmt = 0; - int dai_idx, ret; + int dai_idx, link_idx, conf_idx, ret; int rc, codec; if (!node) @@ -201,47 +248,20 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) * see simple-card */ - ret = asoc_simple_card_of_parse_routing(card, NULL, 0); + ret = asoc_simple_card_of_parse_routing(card, NULL); if (ret < 0) return ret; - asoc_simple_card_parse_convert(dev, NULL, &priv->adata); + asoc_simple_card_parse_convert(dev, node, NULL, &priv->adata); /* * it supports multi CPU, single CODEC only here * see asoc_graph_get_dais_count */ - /* find 1st codec */ - of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { - cpu_port = it.node; - cpu_ep = of_get_next_child(cpu_port, NULL); - codec_ep = of_graph_get_remote_endpoint(cpu_ep); - rcpu_ep = of_graph_get_remote_endpoint(codec_ep); - - of_node_put(cpu_ep); - of_node_put(codec_ep); - of_node_put(rcpu_ep); - - if (!codec_ep) - continue; - - if (rcpu_ep != cpu_ep) { - dev_err(dev, "remote-endpoint missmatch (%s/%s/%s)\n", - cpu_ep->name, codec_ep->name, rcpu_ep->name); - ret = -EINVAL; - goto parse_of_err; - } - - ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep, - NULL, &daifmt); - if (ret < 0) { - of_node_put(cpu_port); - goto parse_of_err; - } - } - + link_idx = 0; dai_idx = 0; + conf_idx = 0; codec_port_old = NULL; for (codec = 0; codec < 2; codec++) { /* @@ -257,31 +277,23 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) of_node_put(cpu_ep); of_node_put(codec_ep); + of_node_put(cpu_port); of_node_put(codec_port); + it.node = NULL; if (codec) { - if (!codec_port) - continue; - if (codec_port_old == codec_port) continue; codec_port_old = codec_port; - - /* Back-End (= Codec) */ - ret = asoc_graph_card_dai_link_of(codec_ep, priv, daifmt, dai_idx++, 0); - if (ret < 0) { - of_node_put(cpu_port); - goto parse_of_err; - } - } else { - /* Front-End (= CPU) */ - ret = asoc_graph_card_dai_link_of(cpu_ep, priv, daifmt, dai_idx++, 1); - if (ret < 0) { - of_node_put(cpu_port); - goto parse_of_err; - } } + + ret = asoc_graph_card_dai_link_of(cpu_ep, codec_ep, + priv, &dai_idx, + link_idx++, &conf_idx, + !codec); + if (ret < 0) + goto parse_of_err; } } @@ -289,13 +301,24 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) if (ret) goto parse_of_err; + if ((card->num_links != link_idx) || + (card->num_configs != conf_idx)) { + dev_err(dev, "dai_link or codec_config wrong (%d/%d, %d/%d)\n", + card->num_links, link_idx, card->num_configs, conf_idx); + ret = -EINVAL; + goto parse_of_err; + } + ret = 0; parse_of_err: return ret; } -static int asoc_graph_get_dais_count(struct device *dev) +static void asoc_graph_get_dais_count(struct device *dev, + int *link_num, + int *dais_num, + int *ccnf_num) { struct of_phandle_iterator it; struct device_node *node = dev->of_node; @@ -304,10 +327,48 @@ static int asoc_graph_get_dais_count(struct device *dev) struct device_node *codec_ep; struct device_node *codec_port; struct device_node *codec_port_old; - int count = 0; + struct device_node *codec_port_old2; int rc; + /* + * link_num : number of links. + * CPU-Codec / CPU-dummy / dummy-Codec + * dais_num : number of DAIs + * ccnf_num : number of codec_conf + * same number for dummy-Codec + * + * ex1) + * CPU0 --- Codec0 link : 5 + * CPU1 --- Codec1 dais : 7 + * CPU2 -/ ccnf : 1 + * CPU3 --- Codec2 + * + * => 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec + * => 7 DAIs = 4xCPU + 3xCodec + * => 1 ccnf = 1xdummy-Codec + * + * ex2) + * CPU0 --- Codec0 link : 5 + * CPU1 --- Codec1 dais : 6 + * CPU2 -/ ccnf : 1 + * CPU3 -/ + * + * => 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec + * => 6 DAIs = 4xCPU + 2xCodec + * => 1 ccnf = 1xdummy-Codec + * + * ex3) + * CPU0 --- Codec0 link : 6 + * CPU1 -/ dais : 6 + * CPU2 --- Codec1 ccnf : 2 + * CPU3 -/ + * + * => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec + * => 6 DAIs = 4xCPU + 2xCodec + * => 2 ccnf = 2xdummy-Codec + */ codec_port_old = NULL; + codec_port_old2 = NULL; of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { cpu_port = it.node; cpu_ep = of_get_next_child(cpu_port, NULL); @@ -318,20 +379,22 @@ static int asoc_graph_get_dais_count(struct device *dev) of_node_put(codec_ep); of_node_put(codec_port); - if (cpu_ep) - count++; + (*link_num)++; + (*dais_num)++; - if (!codec_port) - continue; + if (codec_port_old == codec_port) { + if (codec_port_old2 != codec_port_old) { + (*link_num)++; + (*ccnf_num)++; + } - if (codec_port_old == codec_port) + codec_port_old2 = codec_port_old; continue; + } - count++; + (*dais_num)++; codec_port_old = codec_port; } - - return count; } static int asoc_graph_card_probe(struct platform_device *pdev) @@ -339,22 +402,27 @@ static int asoc_graph_card_probe(struct platform_device *pdev) struct graph_card_data *priv; struct snd_soc_dai_link *dai_link; struct graph_dai_props *dai_props; + struct asoc_simple_dai *dais; struct device *dev = &pdev->dev; struct snd_soc_card *card; - int num, ret, i; + struct snd_soc_codec_conf *cconf; + int lnum = 0, dnum = 0, cnum = 0; + int ret, i; /* Allocate the private data and the DAI link array */ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - num = asoc_graph_get_dais_count(dev); - if (num == 0) + asoc_graph_get_dais_count(dev, &lnum, &dnum, &cnum); + if (!lnum || !dnum) return -EINVAL; - dai_props = devm_kcalloc(dev, num, sizeof(*dai_props), GFP_KERNEL); - dai_link = devm_kcalloc(dev, num, sizeof(*dai_link), GFP_KERNEL); - if (!dai_props || !dai_link) + dai_props = devm_kcalloc(dev, lnum, sizeof(*dai_props), GFP_KERNEL); + dai_link = devm_kcalloc(dev, lnum, sizeof(*dai_link), GFP_KERNEL); + dais = devm_kcalloc(dev, dnum, sizeof(*dais), GFP_KERNEL); + cconf = devm_kcalloc(dev, cnum, sizeof(*cconf), GFP_KERNEL); + if (!dai_props || !dai_link || !dais) return -ENOMEM; /* @@ -363,7 +431,7 @@ static int asoc_graph_card_probe(struct platform_device *pdev) * see * soc-core.c :: snd_soc_init_multicodec() */ - for (i = 0; i < num; i++) { + for (i = 0; i < lnum; i++) { dai_link[i].codecs = &dai_props[i].codecs; dai_link[i].num_codecs = 1; dai_link[i].platform = &dai_props[i].platform; @@ -371,15 +439,17 @@ static int asoc_graph_card_probe(struct platform_device *pdev) priv->dai_props = dai_props; priv->dai_link = dai_link; + priv->dais = dais; + priv->codec_conf = cconf; /* Init snd_soc_card */ card = graph_priv_to_card(priv); card->owner = THIS_MODULE; card->dev = dev; card->dai_link = priv->dai_link; - card->num_links = num; - card->codec_conf = &priv->codec_conf; - card->num_configs = 1; + card->num_links = lnum; + card->codec_conf = cconf; + card->num_configs = cnum; ret = asoc_graph_card_parse_of(priv); if (ret < 0) { diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index f34cc6cddfa2..b807a47515eb 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -32,10 +32,11 @@ void asoc_simple_card_convert_fixup(struct asoc_simple_card_data *data, } EXPORT_SYMBOL_GPL(asoc_simple_card_convert_fixup); -void asoc_simple_card_parse_convert(struct device *dev, char *prefix, +void asoc_simple_card_parse_convert(struct device *dev, + struct device_node *np, + char *prefix, struct asoc_simple_card_data *data) { - struct device_node *np = dev->of_node; char prop[128]; if (!prefix) @@ -151,21 +152,19 @@ int asoc_simple_card_parse_card_name(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name); -static void asoc_simple_card_clk_register(struct asoc_simple_dai *dai, - struct clk *clk) -{ - dai->clk = clk; -} - int asoc_simple_card_clk_enable(struct asoc_simple_dai *dai) { - return clk_prepare_enable(dai->clk); + if (dai) + return clk_prepare_enable(dai->clk); + + return 0; } EXPORT_SYMBOL_GPL(asoc_simple_card_clk_enable); void asoc_simple_card_clk_disable(struct asoc_simple_dai *dai) { - clk_disable_unprepare(dai->clk); + if (dai) + clk_disable_unprepare(dai->clk); } EXPORT_SYMBOL_GPL(asoc_simple_card_clk_disable); @@ -200,7 +199,7 @@ int asoc_simple_card_parse_clk(struct device *dev, if (!IS_ERR(clk)) { simple_dai->sysclk = clk_get_rate(clk); - asoc_simple_card_clk_register(simple_dai, clk); + simple_dai->clk = clk; } else if (!of_property_read_u32(node, "system-clock-frequency", &val)) { simple_dai->sysclk = val; } else { @@ -272,13 +271,24 @@ static int asoc_simple_card_get_dai_id(struct device_node *ep) { struct device_node *node; struct device_node *endpoint; + struct of_endpoint info; int i, id; int ret; + /* use driver specified DAI ID if exist */ ret = snd_soc_get_dai_id(ep); if (ret != -ENOTSUPP) return ret; + /* use endpoint/port reg if exist */ + ret = of_graph_parse_endpoint(ep, &info); + if (ret == 0) { + if (info.id) + return info.id; + if (info.port) + return info.port; + } + node = of_graph_get_port_parent(ep); /* @@ -348,6 +358,9 @@ int asoc_simple_card_init_dai(struct snd_soc_dai *dai, { int ret; + if (!simple_dai) + return 0; + if (simple_dai->sysclk) { ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk, simple_dai->clk_direction); @@ -415,8 +428,7 @@ int asoc_simple_card_clean_reference(struct snd_soc_card *card) EXPORT_SYMBOL_GPL(asoc_simple_card_clean_reference); int asoc_simple_card_of_parse_routing(struct snd_soc_card *card, - char *prefix, - int optional) + char *prefix) { struct device_node *node = card->dev->of_node; char prop[128]; @@ -426,11 +438,8 @@ int asoc_simple_card_of_parse_routing(struct snd_soc_card *card, snprintf(prop, sizeof(prop), "%s%s", prefix, "routing"); - if (!of_property_read_bool(node, prop)) { - if (optional) - return 0; - return -EINVAL; - } + if (!of_property_read_bool(node, prop)) + return 0; return snd_soc_of_parse_audio_routing(card, prop); } diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 5a3f59aa4ba5..37e001cf9cd1 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -18,16 +18,19 @@ struct simple_card_data { struct snd_soc_card snd_card; struct simple_dai_props { - struct asoc_simple_dai cpu_dai; - struct asoc_simple_dai codec_dai; + struct asoc_simple_dai *cpu_dai; + struct asoc_simple_dai *codec_dai; struct snd_soc_dai_link_component codecs; /* single codec */ struct snd_soc_dai_link_component platform; + struct asoc_simple_card_data adata; + struct snd_soc_codec_conf *codec_conf; unsigned int mclk_fs; } *dai_props; - unsigned int mclk_fs; struct asoc_simple_jack hp_jack; struct asoc_simple_jack mic_jack; struct snd_soc_dai_link *dai_link; + struct asoc_simple_dai *dais; + struct snd_soc_codec_conf *codec_conf; }; #define simple_priv_to_card(priv) (&(priv)->snd_card) @@ -47,13 +50,13 @@ static int asoc_simple_card_startup(struct snd_pcm_substream *substream) simple_priv_to_props(priv, rtd->num); int ret; - ret = asoc_simple_card_clk_enable(&dai_props->cpu_dai); + ret = asoc_simple_card_clk_enable(dai_props->cpu_dai); if (ret) return ret; - ret = asoc_simple_card_clk_enable(&dai_props->codec_dai); + ret = asoc_simple_card_clk_enable(dai_props->codec_dai); if (ret) - asoc_simple_card_clk_disable(&dai_props->cpu_dai); + asoc_simple_card_clk_disable(dai_props->cpu_dai); return ret; } @@ -65,14 +68,17 @@ static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream) struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); - asoc_simple_card_clk_disable(&dai_props->cpu_dai); + asoc_simple_card_clk_disable(dai_props->cpu_dai); - asoc_simple_card_clk_disable(&dai_props->codec_dai); + asoc_simple_card_clk_disable(dai_props->codec_dai); } static int asoc_simple_set_clk_rate(struct asoc_simple_dai *simple_dai, unsigned long rate) { + if (!simple_dai) + return 0; + if (!simple_dai->clk) return 0; @@ -94,19 +100,17 @@ static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream, unsigned int mclk, mclk_fs = 0; int ret = 0; - if (priv->mclk_fs) - mclk_fs = priv->mclk_fs; - else if (dai_props->mclk_fs) + if (dai_props->mclk_fs) mclk_fs = dai_props->mclk_fs; if (mclk_fs) { mclk = params_rate(params) * mclk_fs; - ret = asoc_simple_set_clk_rate(&dai_props->codec_dai, mclk); + ret = asoc_simple_set_clk_rate(dai_props->codec_dai, mclk); if (ret < 0) return ret; - ret = asoc_simple_set_clk_rate(&dai_props->cpu_dai, mclk); + ret = asoc_simple_set_clk_rate(dai_props->cpu_dai, mclk); if (ret < 0) return ret; @@ -134,33 +138,169 @@ static const struct snd_soc_ops asoc_simple_card_ops = { 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 = - simple_priv_to_props(priv, rtd->num); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); int ret; - ret = asoc_simple_card_init_dai(codec, &dai_props->codec_dai); + ret = asoc_simple_card_init_dai(rtd->codec_dai, + dai_props->codec_dai); if (ret < 0) return ret; - ret = asoc_simple_card_init_dai(cpu, &dai_props->cpu_dai); + ret = asoc_simple_card_init_dai(rtd->cpu_dai, + dai_props->cpu_dai); if (ret < 0) return ret; return 0; } -static int asoc_simple_card_dai_link_of(struct device_node *node, +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 simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); + + asoc_simple_card_convert_fixup(&dai_props->adata, params); + + return 0; +} + +static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top, + struct device_node *node, + struct device_node *np, + struct device_node *codec, + struct simple_card_data *priv, + int *dai_idx, int link_idx, + int *conf_idx, int is_fe, + bool is_top_level_node) +{ + struct device *dev = simple_priv_to_dev(priv); + struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, link_idx); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, link_idx); + struct asoc_simple_dai *dai; + struct snd_soc_dai_link_component *codecs = dai_link->codecs; + + char prop[128]; + char *prefix = ""; + int ret; + + /* For single DAI link & old style of DT node */ + if (is_top_level_node) + prefix = PREFIX; + + if (is_fe) { + int is_single_links = 0; + + /* BE is dummy */ + codecs->of_node = NULL; + codecs->dai_name = "snd-soc-dummy-dai"; + codecs->name = "snd-soc-dummy"; + + /* FE settings */ + dai_link->dynamic = 1; + dai_link->dpcm_merged_format = 1; + + dai = + dai_props->cpu_dai = &priv->dais[(*dai_idx)++]; + + 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(dev, np, dai_link, dai); + 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 { + struct snd_soc_codec_conf *cconf; + + /* 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; + + dai = + dai_props->codec_dai = &priv->dais[(*dai_idx)++]; + + cconf = + dai_props->codec_conf = &priv->codec_conf[(*conf_idx)++]; + + ret = asoc_simple_card_parse_codec(np, dai_link, DAI, CELL); + if (ret < 0) + return ret; + + ret = asoc_simple_card_parse_clk_codec(dev, np, dai_link, dai); + if (ret < 0) + return ret; + + ret = asoc_simple_card_set_dailink_name(dev, dai_link, + "be.%s", + codecs->dai_name); + if (ret < 0) + return ret; + + /* check "prefix" from top node */ + snd_soc_of_parse_node_prefix(top, cconf, codecs->of_node, + PREFIX "prefix"); + snd_soc_of_parse_node_prefix(node, cconf, codecs->of_node, + "prefix"); + snd_soc_of_parse_node_prefix(np, cconf, codecs->of_node, + "prefix"); + } + + asoc_simple_card_parse_convert(dev, top, PREFIX, &dai_props->adata); + asoc_simple_card_parse_convert(dev, node, prefix, &dai_props->adata); + asoc_simple_card_parse_convert(dev, np, NULL, &dai_props->adata); + + ret = asoc_simple_card_of_parse_tdm(np, dai); + if (ret) + return ret; + + ret = asoc_simple_card_canonicalize_dailink(dai_link); + if (ret < 0) + return ret; + + snprintf(prop, sizeof(prop), "%smclk-fs", prefix); + of_property_read_u32(top, PREFIX "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(node, prop, &dai_props->mclk_fs); + of_property_read_u32(np, prop, &dai_props->mclk_fs); + + ret = asoc_simple_card_parse_daifmt(dev, node, codec, + prefix, &dai_link->dai_fmt); + if (ret < 0) + return ret; + + 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; + + return 0; +} + +static int asoc_simple_card_dai_link_of(struct device_node *top, + struct device_node *node, struct simple_card_data *priv, - int idx, + int *dai_idx, int link_idx, bool is_top_level_node) { struct device *dev = simple_priv_to_dev(priv); - struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx); - struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx); - struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai; - struct asoc_simple_dai *codec_dai = &dai_props->codec_dai; + struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, link_idx); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, link_idx); + struct asoc_simple_dai *cpu_dai; + struct asoc_simple_dai *codec_dai; struct device_node *cpu = NULL; struct device_node *plat = NULL; struct device_node *codec = NULL; @@ -193,12 +333,21 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, goto dai_link_of_err; } + cpu_dai = + dai_props->cpu_dai = &priv->dais[(*dai_idx)++]; + codec_dai = + dai_props->codec_dai = &priv->dais[(*dai_idx)++]; + ret = asoc_simple_card_parse_daifmt(dev, node, codec, prefix, &dai_link->dai_fmt); if (ret < 0) goto dai_link_of_err; - of_property_read_u32(node, "mclk-fs", &dai_props->mclk_fs); + snprintf(prop, sizeof(prop), "%smclk-fs", prefix); + of_property_read_u32(top, PREFIX "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(node, prop, &dai_props->mclk_fs); + of_property_read_u32(cpu, prop, &dai_props->mclk_fs); + of_property_read_u32(codec, prop, &dai_props->mclk_fs); ret = asoc_simple_card_parse_cpu(cpu, dai_link, DAI, CELL, &single_cpu); @@ -286,61 +435,148 @@ static int asoc_simple_card_parse_aux_devs(struct device_node *node, static int asoc_simple_card_parse_of(struct simple_card_data *priv) { struct device *dev = simple_priv_to_dev(priv); + struct device_node *top = dev->of_node; struct snd_soc_card *card = simple_priv_to_card(priv); - struct device_node *dai_link; - struct device_node *node = dev->of_node; - int ret; - - if (!node) + struct device_node *node; + struct device_node *np; + struct device_node *codec; + bool is_fe; + int ret, loop; + int dai_idx, link_idx, conf_idx; + + if (!top) return -EINVAL; - dai_link = of_get_child_by_name(node, PREFIX "dai-link"); - ret = asoc_simple_card_of_parse_widgets(card, PREFIX); if (ret < 0) - goto card_parse_end; + return ret; - ret = asoc_simple_card_of_parse_routing(card, PREFIX, 1); + ret = asoc_simple_card_of_parse_routing(card, PREFIX); if (ret < 0) - goto card_parse_end; - - /* Factor to mclk, used in hw_params() */ - of_property_read_u32(node, PREFIX "mclk-fs", &priv->mclk_fs); + return ret; /* Single/Muti DAI link(s) & New style of DT node */ - if (dai_link) { - struct device_node *np = NULL; - int i = 0; - - for_each_child_of_node(node, np) { - dev_dbg(dev, "\tlink %d:\n", i); - ret = asoc_simple_card_dai_link_of(np, priv, - i, false); - if (ret < 0) { - of_node_put(np); - goto card_parse_end; + loop = 1; + link_idx = 0; + dai_idx = 0; + conf_idx = 0; + node = of_get_child_by_name(top, PREFIX "dai-link"); + if (!node) { + node = dev->of_node; + loop = 0; + } + + do { + /* DPCM */ + if (of_get_child_count(node) > 2) { + for_each_child_of_node(node, np) { + codec = of_get_child_by_name(node, + loop ? "codec" : + PREFIX "codec"); + if (!codec) + return -ENODEV; + + is_fe = (np != codec); + + ret = asoc_simple_card_dai_link_of_dpcm( + top, node, np, codec, priv, + &dai_idx, link_idx++, &conf_idx, + is_fe, !loop); } - i++; + } else { + ret = asoc_simple_card_dai_link_of( + top, node, priv, + &dai_idx, link_idx++, !loop); } - } else { - /* For single DAI link & old style of DT node */ - ret = asoc_simple_card_dai_link_of(node, priv, 0, true); if (ret < 0) - goto card_parse_end; - } + return ret; + + node = of_get_next_child(top, node); + } while (loop && node); ret = asoc_simple_card_parse_card_name(card, PREFIX); if (ret < 0) - goto card_parse_end; - - ret = asoc_simple_card_parse_aux_devs(node, priv); + return ret; -card_parse_end: - of_node_put(dai_link); + ret = asoc_simple_card_parse_aux_devs(top, priv); return ret; } +static void asoc_simple_card_get_dais_count(struct device *dev, + int *link_num, + int *dais_num, + int *ccnf_num) +{ + struct device_node *top = dev->of_node; + struct device_node *node; + int loop; + int num; + + /* + * link_num : number of links. + * CPU-Codec / CPU-dummy / dummy-Codec + * dais_num : number of DAIs + * ccnf_num : number of codec_conf + * same number for "dummy-Codec" + * + * ex1) + * CPU0 --- Codec0 link : 5 + * CPU1 --- Codec1 dais : 7 + * CPU2 -/ ccnf : 1 + * CPU3 --- Codec2 + * + * => 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec + * => 7 DAIs = 4xCPU + 3xCodec + * => 1 ccnf = 1xdummy-Codec + * + * ex2) + * CPU0 --- Codec0 link : 5 + * CPU1 --- Codec1 dais : 6 + * CPU2 -/ ccnf : 1 + * CPU3 -/ + * + * => 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec + * => 6 DAIs = 4xCPU + 2xCodec + * => 1 ccnf = 1xdummy-Codec + * + * ex3) + * CPU0 --- Codec0 link : 6 + * CPU1 -/ dais : 6 + * CPU2 --- Codec1 ccnf : 2 + * CPU3 -/ + * + * => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec + * => 6 DAIs = 4xCPU + 2xCodec + * => 2 ccnf = 2xdummy-Codec + */ + if (!top) { + (*link_num) = 1; + (*dais_num) = 2; + (*ccnf_num) = 0; + return; + } + + loop = 1; + node = of_get_child_by_name(top, PREFIX "dai-link"); + if (!node) { + node = top; + loop = 0; + } + + do { + num = of_get_child_count(node); + (*dais_num) += num; + if (num > 2) { + (*link_num) += num; + (*ccnf_num)++; + } else { + (*link_num)++; + } + node = of_get_next_child(top, node); + } while (loop && node); +} + static int asoc_simple_soc_card_probe(struct snd_soc_card *card) { struct simple_card_data *priv = snd_soc_card_get_drvdata(card); @@ -362,25 +598,28 @@ 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 asoc_simple_dai *dais; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct snd_soc_card *card; - int num, ret, i; - - /* Get the number of DAI links */ - if (np && of_get_child_by_name(np, PREFIX "dai-link")) - num = of_get_child_count(np); - else - num = 1; + struct snd_soc_codec_conf *cconf; + int lnum = 0, dnum = 0, cnum = 0; + int ret, i; /* Allocate the private data and the DAI link array */ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - dai_props = devm_kcalloc(dev, num, sizeof(*dai_props), GFP_KERNEL); - dai_link = devm_kcalloc(dev, num, sizeof(*dai_link), GFP_KERNEL); - if (!dai_props || !dai_link) + asoc_simple_card_get_dais_count(dev, &lnum, &dnum, &cnum); + if (!lnum || !dnum) + return -EINVAL; + + dai_props = devm_kcalloc(dev, lnum, sizeof(*dai_props), GFP_KERNEL); + dai_link = devm_kcalloc(dev, lnum, sizeof(*dai_link), GFP_KERNEL); + dais = devm_kcalloc(dev, dnum, sizeof(*dais), GFP_KERNEL); + cconf = devm_kcalloc(dev, cnum, sizeof(*cconf), GFP_KERNEL); + if (!dai_props || !dai_link || !dais) return -ENOMEM; /* @@ -389,7 +628,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) * see * soc-core.c :: snd_soc_init_multicodec() */ - for (i = 0; i < num; i++) { + for (i = 0; i < lnum; i++) { dai_link[i].codecs = &dai_props[i].codecs; dai_link[i].num_codecs = 1; dai_link[i].platform = &dai_props[i].platform; @@ -397,13 +636,17 @@ static int asoc_simple_card_probe(struct platform_device *pdev) priv->dai_props = dai_props; priv->dai_link = dai_link; + priv->dais = dais; + priv->codec_conf = cconf; /* Init snd_soc_card */ card = simple_priv_to_card(priv); card->owner = THIS_MODULE; card->dev = dev; card->dai_link = priv->dai_link; - card->num_links = num; + card->num_links = lnum; + card->codec_conf = cconf; + card->num_configs = cnum; card->probe = asoc_simple_soc_card_probe; if (np && of_device_is_available(np)) { @@ -419,6 +662,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) struct asoc_simple_card_info *cinfo; struct snd_soc_dai_link_component *codecs; struct snd_soc_dai_link_component *platform; + int dai_idx = 0; cinfo = dev->platform_data; if (!cinfo) { @@ -435,6 +679,9 @@ static int asoc_simple_card_probe(struct platform_device *pdev) return -EINVAL; } + dai_props->cpu_dai = &priv->dais[dai_idx++]; + dai_props->codec_dai = &priv->dais[dai_idx++]; + codecs = dai_link->codecs; codecs->name = cinfo->codec; codecs->dai_name = cinfo->codec_dai.name; @@ -448,10 +695,10 @@ static int asoc_simple_card_probe(struct platform_device *pdev) dai_link->cpu_dai_name = cinfo->cpu_dai.name; dai_link->dai_fmt = cinfo->daifmt; dai_link->init = asoc_simple_card_dai_init; - memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai, - sizeof(priv->dai_props->cpu_dai)); - memcpy(&priv->dai_props->codec_dai, &cinfo->codec_dai, - sizeof(priv->dai_props->codec_dai)); + memcpy(priv->dai_props->cpu_dai, &cinfo->cpu_dai, + 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(card, priv); @@ -476,6 +723,7 @@ static int asoc_simple_card_remove(struct platform_device *pdev) static const struct of_device_id asoc_simple_of_match[] = { { .compatible = "simple-audio-card", }, + { .compatible = "simple-scu-audio-card", }, {}, }; MODULE_DEVICE_TABLE(of, asoc_simple_of_match); diff --git a/sound/soc/generic/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c index 85b46f0eae0f..9d7299d536a8 100644 --- a/sound/soc/generic/simple-scu-card.c +++ b/sound/soc/generic/simple-scu-card.c @@ -21,14 +21,18 @@ struct simple_card_data { struct snd_soc_card snd_card; - struct snd_soc_codec_conf codec_conf; struct simple_dai_props { - struct asoc_simple_dai dai; + struct asoc_simple_dai *cpu_dai; + struct asoc_simple_dai *codec_dai; struct snd_soc_dai_link_component codecs; struct snd_soc_dai_link_component platform; + struct asoc_simple_card_data adata; + struct snd_soc_codec_conf *codec_conf; } *dai_props; struct snd_soc_dai_link *dai_link; + struct asoc_simple_dai *dais; struct asoc_simple_card_data adata; + struct snd_soc_codec_conf *codec_conf; }; #define simple_priv_to_card(priv) (&(priv)->snd_card) @@ -46,8 +50,17 @@ static int asoc_simple_card_startup(struct snd_pcm_substream *substream) struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); + int ret; + + ret = asoc_simple_card_clk_enable(dai_props->cpu_dai); + if (ret) + return ret; + + ret = asoc_simple_card_clk_enable(dai_props->codec_dai); + if (ret) + asoc_simple_card_clk_disable(dai_props->cpu_dai); - return asoc_simple_card_clk_enable(&dai_props->dai); + return ret; } static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream) @@ -57,7 +70,9 @@ static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream) struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); - asoc_simple_card_clk_disable(&dai_props->dai); + asoc_simple_card_clk_disable(dai_props->cpu_dai); + + asoc_simple_card_clk_disable(dai_props->codec_dai); } static const struct snd_soc_ops asoc_simple_card_ops = { @@ -67,42 +82,57 @@ static const struct snd_soc_ops asoc_simple_card_ops = { 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 simple_dai_props *dai_props; - int num = rtd->num; + struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); + int ret; + + ret = asoc_simple_card_init_dai(rtd->codec_dai, + dai_props->codec_dai); + if (ret < 0) + return ret; - 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; + ret = asoc_simple_card_init_dai(rtd->cpu_dai, + dai_props->cpu_dai); + if (ret < 0) + return ret; - return asoc_simple_card_init_dai(dai, &dai_props->dai); + return 0; } 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 simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); + + asoc_simple_card_convert_fixup(&dai_props->adata, params); + /* overwrite by top level adata if exist */ asoc_simple_card_convert_fixup(&priv->adata, params); return 0; } -static int asoc_simple_card_dai_link_of(struct device_node *np, +static int asoc_simple_card_dai_link_of(struct device_node *link, + struct device_node *np, + struct device_node *codec, struct simple_card_data *priv, - unsigned int daifmt, - int idx, bool is_fe) + int *dai_idx, int link_idx, + int *conf_idx, int is_fe, + bool is_top_level_node) { struct device *dev = simple_priv_to_dev(priv); - struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx); - struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx); + struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, link_idx); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, link_idx); struct snd_soc_card *card = simple_priv_to_card(priv); + struct asoc_simple_dai *dai; + char *prefix = ""; int ret; + /* For single DAI link & old style of DT node */ + if (is_top_level_node) + prefix = PREFIX; + if (is_fe) { int is_single_links = 0; struct snd_soc_dai_link_component *codecs; @@ -117,12 +147,15 @@ static int asoc_simple_card_dai_link_of(struct device_node *np, dai_link->dynamic = 1; dai_link->dpcm_merged_format = 1; + dai = + dai_props->cpu_dai = &priv->dais[(*dai_idx)++]; + 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(dev, np, dai_link, &dai_props->dai); + ret = asoc_simple_card_parse_clk_cpu(dev, np, dai_link, dai); if (ret < 0) return ret; @@ -134,6 +167,8 @@ static int asoc_simple_card_dai_link_of(struct device_node *np, asoc_simple_card_canonicalize_cpu(dai_link, is_single_links); } else { + struct snd_soc_codec_conf *cconf; + /* FE is dummy */ dai_link->cpu_of_node = NULL; dai_link->cpu_dai_name = "snd-soc-dummy-dai"; @@ -143,11 +178,17 @@ static int asoc_simple_card_dai_link_of(struct device_node *np, dai_link->no_pcm = 1; dai_link->be_hw_params_fixup = asoc_simple_card_be_hw_params_fixup; + dai = + dai_props->codec_dai = &priv->dais[(*dai_idx)++]; + + cconf = + dai_props->codec_conf = &priv->codec_conf[(*conf_idx)++]; + ret = asoc_simple_card_parse_codec(np, dai_link, DAI, CELL); if (ret < 0) return ret; - ret = asoc_simple_card_parse_clk_codec(dev, np, dai_link, &dai_props->dai); + ret = asoc_simple_card_parse_clk_codec(dev, np, dai_link, dai); if (ret < 0) return ret; @@ -157,13 +198,20 @@ static int asoc_simple_card_dai_link_of(struct device_node *np, if (ret < 0) return ret; - snd_soc_of_parse_audio_prefix(card, - &priv->codec_conf, + /* check "prefix" from top node */ + snd_soc_of_parse_audio_prefix(card, cconf, dai_link->codecs->of_node, PREFIX "prefix"); + /* check "prefix" from each node if top doesn't have */ + if (!cconf->of_node) + snd_soc_of_parse_node_prefix(np, cconf, + dai_link->codecs->of_node, + "prefix"); } - ret = asoc_simple_card_of_parse_tdm(np, &dai_props->dai); + asoc_simple_card_parse_convert(dev, link, prefix, &dai_props->adata); + + ret = asoc_simple_card_of_parse_tdm(np, dai); if (ret) return ret; @@ -171,7 +219,11 @@ static int asoc_simple_card_dai_link_of(struct device_node *np, if (ret < 0) return ret; - dai_link->dai_fmt = daifmt; + ret = asoc_simple_card_parse_daifmt(dev, link, codec, + prefix, &dai_link->dai_fmt); + if (ret < 0) + return ret; + dai_link->dpcm_playback = 1; dai_link->dpcm_capture = 1; dai_link->ops = &asoc_simple_card_ops; @@ -184,52 +236,136 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv) { struct device *dev = simple_priv_to_dev(priv); + struct device_node *top = dev->of_node; + struct device_node *node; struct device_node *np; + struct device_node *codec; struct snd_soc_card *card = simple_priv_to_card(priv); - struct device_node *node = dev->of_node; - unsigned int daifmt = 0; bool is_fe; - int ret, i; + int ret, loop; + int dai_idx, link_idx, conf_idx; - if (!node) + if (!top) return -EINVAL; ret = asoc_simple_card_of_parse_widgets(card, PREFIX); if (ret < 0) return ret; - ret = asoc_simple_card_of_parse_routing(card, PREFIX, 0); + ret = asoc_simple_card_of_parse_routing(card, PREFIX); if (ret < 0) return ret; - asoc_simple_card_parse_convert(dev, PREFIX, &priv->adata); + asoc_simple_card_parse_convert(dev, top, PREFIX, &priv->adata); - /* find 1st codec */ - np = of_get_child_by_name(node, PREFIX "codec"); - if (!np) - return -ENODEV; + loop = 1; + link_idx = 0; + dai_idx = 0; + conf_idx = 0; + node = of_get_child_by_name(top, PREFIX "dai-link"); + if (!node) { + node = dev->of_node; + loop = 0; + } + + do { + codec = of_get_child_by_name(node, + loop ? "codec" : PREFIX "codec"); + if (!codec) + return -ENODEV; + + for_each_child_of_node(node, np) { + is_fe = (np != codec); + + ret = asoc_simple_card_dai_link_of(node, np, codec, priv, + &dai_idx, link_idx++, + &conf_idx, + is_fe, !loop); + if (ret < 0) + return ret; + } + node = of_get_next_child(top, node); + } while (loop && node); - ret = asoc_simple_card_parse_daifmt(dev, node, np, PREFIX, &daifmt); + ret = asoc_simple_card_parse_card_name(card, PREFIX); 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; + return 0; +} - ret = asoc_simple_card_dai_link_of(np, priv, daifmt, i, is_fe); - if (ret < 0) - return ret; - i++; +static void asoc_simple_card_get_dais_count(struct device *dev, + int *link_num, + int *dais_num, + int *ccnf_num) +{ + struct device_node *top = dev->of_node; + struct device_node *node; + int loop; + int num; + + /* + * link_num : number of links. + * CPU-Codec / CPU-dummy / dummy-Codec + * dais_num : number of DAIs + * ccnf_num : number of codec_conf + * same number for "dummy-Codec" + * + * ex1) + * CPU0 --- Codec0 link : 5 + * CPU1 --- Codec1 dais : 7 + * CPU2 -/ ccnf : 1 + * CPU3 --- Codec2 + * + * => 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec + * => 7 DAIs = 4xCPU + 3xCodec + * => 1 ccnf = 1xdummy-Codec + * + * ex2) + * CPU0 --- Codec0 link : 5 + * CPU1 --- Codec1 dais : 6 + * CPU2 -/ ccnf : 1 + * CPU3 -/ + * + * => 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec + * => 6 DAIs = 4xCPU + 2xCodec + * => 1 ccnf = 1xdummy-Codec + * + * ex3) + * CPU0 --- Codec0 link : 6 + * CPU1 -/ dais : 6 + * CPU2 --- Codec1 ccnf : 2 + * CPU3 -/ + * + * => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec + * => 6 DAIs = 4xCPU + 2xCodec + * => 2 ccnf = 2xdummy-Codec + */ + if (!top) { + (*link_num) = 1; + (*dais_num) = 2; + (*ccnf_num) = 0; + return; } - ret = asoc_simple_card_parse_card_name(card, PREFIX); - if (ret < 0) - return ret; + loop = 1; + node = of_get_child_by_name(top, PREFIX "dai-link"); + if (!node) { + node = top; + loop = 0; + } - return 0; + do { + num = of_get_child_count(node); + (*dais_num) += num; + if (num > 2) { + (*link_num) += num; + (*ccnf_num)++; + } else { + (*link_num)++; + } + node = of_get_next_child(top, node); + } while (loop && node); } static int asoc_simple_card_probe(struct platform_device *pdev) @@ -237,21 +373,27 @@ 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 asoc_simple_dai *dais; struct snd_soc_card *card; + struct snd_soc_codec_conf *cconf; struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - int num, ret, i; + int ret, i; + int lnum = 0, dnum = 0, cnum = 0; /* Allocate the private data */ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - num = of_get_child_count(np); + asoc_simple_card_get_dais_count(dev, &lnum, &dnum, &cnum); + if (!lnum || !dnum) + return -EINVAL; - dai_props = devm_kcalloc(dev, num, sizeof(*dai_props), GFP_KERNEL); - dai_link = devm_kcalloc(dev, num, sizeof(*dai_link), GFP_KERNEL); - if (!dai_props || !dai_link) + dai_props = devm_kcalloc(dev, lnum, sizeof(*dai_props), GFP_KERNEL); + dai_link = devm_kcalloc(dev, lnum, sizeof(*dai_link), GFP_KERNEL); + dais = devm_kcalloc(dev, dnum, sizeof(*dais), GFP_KERNEL); + cconf = devm_kcalloc(dev, cnum, sizeof(*cconf), GFP_KERNEL); + if (!dai_props || !dai_link || !dais) return -ENOMEM; /* @@ -260,7 +402,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) * see * soc-core.c :: snd_soc_init_multicodec() */ - for (i = 0; i < num; i++) { + for (i = 0; i < lnum; i++) { dai_link[i].codecs = &dai_props[i].codecs; dai_link[i].num_codecs = 1; dai_link[i].platform = &dai_props[i].platform; @@ -268,15 +410,17 @@ static int asoc_simple_card_probe(struct platform_device *pdev) priv->dai_props = dai_props; priv->dai_link = dai_link; + priv->dais = dais; + priv->codec_conf = cconf; /* Init snd_soc_card */ card = simple_priv_to_card(priv); card->owner = THIS_MODULE; card->dev = dev; card->dai_link = priv->dai_link; - card->num_links = num; - card->codec_conf = &priv->codec_conf; - card->num_configs = 1; + card->num_links = lnum; + card->codec_conf = cconf; + card->num_configs = cnum; ret = asoc_simple_card_parse_of(priv); if (ret < 0) { diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 18e717703685..2fd1b61e8331 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -102,15 +102,74 @@ config SND_SST_ATOM_HIFI2_PLATFORM_ACPI recommended option config SND_SOC_INTEL_SKYLAKE - tristate "SKL/BXT/KBL/GLK/CNL... Platforms" + tristate "All Skylake/SST Platforms" depends on PCI && ACPI - select SND_SOC_INTEL_SKYLAKE_COMMON + select SND_SOC_INTEL_SKL + select SND_SOC_INTEL_APL + select SND_SOC_INTEL_KBL + select SND_SOC_INTEL_GLK + select SND_SOC_INTEL_CNL + select SND_SOC_INTEL_CFL help - If you have a Intel Skylake/Broxton/ApolloLake/KabyLake/ - GeminiLake or CannonLake platform with the DSP enabled in the BIOS - then enable this option by saying Y or m. + This is a backwards-compatible option to select all devices + supported by the Intel SST/Skylake driver. This option is no + longer recommended and will be deprecated when the SOF + driver is introduced. Distributions should explicitly + select which platform uses this driver. + +config SND_SOC_INTEL_SKL + tristate "Skylake Platforms" + depends on PCI && ACPI + select SND_SOC_INTEL_SKYLAKE_FAMILY + help + If you have a Intel Skylake platform with the DSP enabled + in the BIOS then enable this option by saying Y or m. + +config SND_SOC_INTEL_APL + tristate "Broxton/ApolloLake Platforms" + depends on PCI && ACPI + select SND_SOC_INTEL_SKYLAKE_FAMILY + help + If you have a Intel Broxton/ApolloLake platform with the DSP + enabled in the BIOS then enable this option by saying Y or m. + +config SND_SOC_INTEL_KBL + tristate "Kabylake Platforms" + depends on PCI && ACPI + select SND_SOC_INTEL_SKYLAKE_FAMILY + help + If you have a Intel Kabylake platform with the DSP + enabled in the BIOS then enable this option by saying Y or m. + +config SND_SOC_INTEL_GLK + tristate "GeminiLake Platforms" + depends on PCI && ACPI + select SND_SOC_INTEL_SKYLAKE_FAMILY + help + If you have a Intel GeminiLake platform with the DSP + enabled in the BIOS then enable this option by saying Y or m. + +config SND_SOC_INTEL_CNL + tristate "CannonLake/WhiskyLake Platforms" + depends on PCI && ACPI + select SND_SOC_INTEL_SKYLAKE_FAMILY + help + If you have a Intel CNL/WHL platform with the DSP + enabled in the BIOS then enable this option by saying Y or m. + +config SND_SOC_INTEL_CFL + tristate "CoffeeLake Platforms" + depends on PCI && ACPI + select SND_SOC_INTEL_SKYLAKE_FAMILY + help + If you have a Intel CoffeeLake platform with the DSP + enabled in the BIOS then enable this option by saying Y or m. + +config SND_SOC_INTEL_SKYLAKE_FAMILY + tristate + select SND_SOC_INTEL_SKYLAKE_COMMON -if SND_SOC_INTEL_SKYLAKE +if SND_SOC_INTEL_SKYLAKE_FAMILY config SND_SOC_INTEL_SKYLAKE_SSP_CLK tristate @@ -129,13 +188,19 @@ config SND_SOC_INTEL_SKYLAKE_COMMON select SND_SOC_TOPOLOGY select SND_SOC_INTEL_SST select SND_SOC_HDAC_HDA if SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC + select SND_HDA_INTEL_DSP_DETECTION_SKL if SND_SOC_INTEL_SKL + select SND_HDA_INTEL_DSP_DETECTION_APL if SND_SOC_INTEL_APL + select SND_HDA_INTEL_DSP_DETECTION_KBL if SND_SOC_INTEL_KBL + select SND_HDA_INTEL_DSP_DETECTION_GLK if SND_SOC_INTEL_GLK + select SND_HDA_INTEL_DSP_DETECTION_CNL if SND_SOC_INTEL_CNL + select SND_HDA_INTEL_DSP_DETECTION_CFL if SND_SOC_INTEL_CFL select SND_SOC_ACPI_INTEL_MATCH help If you have a Intel Skylake/Broxton/ApolloLake/KabyLake/ GeminiLake or CannonLake platform with the DSP enabled in the BIOS then enable this option by saying Y or m. -endif ## SND_SOC_INTEL_SKYLAKE +endif ## SND_SOC_INTEL_SKYLAKE_FAMILY config SND_SOC_ACPI_INTEL_MATCH tristate diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c index c90b04cc071d..ac542535b9d5 100644 --- a/sound/soc/intel/atom/sst/sst_acpi.c +++ b/sound/soc/intel/atom/sst/sst_acpi.c @@ -341,6 +341,10 @@ static int sst_acpi_probe(struct platform_device *pdev) byt_rvp_platform_data.res_info = &bytcr_res_info; } + /* update machine parameters */ + mach->mach_params.acpi_ipc_irq_index = + pdata->res_info->acpi_ipc_irq_index; + plat_dev = platform_device_register_data(dev, pdata->platform, -1, NULL, 0); if (IS_ERR(plat_dev)) { diff --git a/sound/soc/intel/atom/sst/sst_loader.c b/sound/soc/intel/atom/sst/sst_loader.c index 27413ebae956..b8c456753f01 100644 --- a/sound/soc/intel/atom/sst/sst_loader.c +++ b/sound/soc/intel/atom/sst/sst_loader.c @@ -354,14 +354,14 @@ static int sst_request_fw(struct intel_sst_drv *sst) const struct firmware *fw; retval = request_firmware(&fw, sst->firmware_name, sst->dev); - if (fw == NULL) { - dev_err(sst->dev, "fw is returning as null\n"); - return -EINVAL; - } if (retval) { dev_err(sst->dev, "request fw failed %d\n", retval); return retval; } + if (fw == NULL) { + dev_err(sst->dev, "fw is returning as null\n"); + return -EINVAL; + } mutex_lock(&sst->sst_lock); retval = sst_cache_and_parse_fw(sst, fw); mutex_unlock(&sst->sst_lock); diff --git a/sound/soc/intel/atom/sst/sst_pvt.c b/sound/soc/intel/atom/sst/sst_pvt.c index af93244b4868..00a37a09dc9b 100644 --- a/sound/soc/intel/atom/sst/sst_pvt.c +++ b/sound/soc/intel/atom/sst/sst_pvt.c @@ -166,11 +166,11 @@ int sst_create_ipc_msg(struct ipc_post **arg, bool large) { struct ipc_post *msg; - msg = kzalloc(sizeof(struct ipc_post), GFP_ATOMIC); + msg = kzalloc(sizeof(*msg), GFP_KERNEL); if (!msg) return -ENOMEM; if (large) { - msg->mailbox_data = kzalloc(SST_MAILBOX_SIZE, GFP_ATOMIC); + msg->mailbox_data = kzalloc(SST_MAILBOX_SIZE, GFP_KERNEL); if (!msg->mailbox_data) { kfree(msg); return -ENOMEM; diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index b177db2a0dbb..0a7e40d06395 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -172,7 +172,7 @@ config SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH endif ## SND_SST_ATOM_HIFI2_PLATFORM -if SND_SOC_INTEL_SKYLAKE +if SND_SOC_INTEL_SKL config SND_SOC_INTEL_SKL_RT286_MACH tristate "SKL with RT286 I2S mode" @@ -212,6 +212,10 @@ config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +endif ## SND_SOC_INTEL_SKL + +if SND_SOC_INTEL_APL + config SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH tristate "Broxton with DA7219 and MAX98357A in I2S Mode" depends on MFD_INTEL_LPSS && I2C && ACPI @@ -239,6 +243,10 @@ config SND_SOC_INTEL_BXT_RT298_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +endif ## SND_SOC_INTEL_APL + +if SND_SOC_INTEL_KBL + config SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH tristate "KBL with RT5663 and MAX98927 in I2S Mode" depends on MFD_INTEL_LPSS && I2C && ACPI @@ -293,6 +301,20 @@ config SND_SOC_INTEL_KBL_DA7219_MAX98927_MACH Say Y if you have such a device. If unsure select "N". +config SND_SOC_INTEL_KBL_RT5660_MACH + tristate "KBL with RT5660 in I2S Mode" + depends on MFD_INTEL_LPSS && I2C && ACPI + select SND_SOC_RT5660 + select SND_SOC_HDAC_HDMI + help + This adds support for ASoC Onboard Codec I2S machine driver. This will + create an alsa sound card for RT5660 I2S audio codec. + Say Y if you have such a device. + +endif ## SND_SOC_INTEL_KBL + +if SND_SOC_INTEL_GLK + config SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH tristate "GLK with RT5682 and MAX98357A in I2S Mode" depends on MFD_INTEL_LPSS && I2C && ACPI @@ -307,7 +329,7 @@ config SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH Say Y if you have such a device. If unsure select "N". -endif ## SND_SOC_INTEL_SKYLAKE +endif ## SND_SOC_INTEL_GLK if SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 5381e27df9cc..bf072ea299b7 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -20,6 +20,7 @@ snd-soc-kbl_da7219_max98357a-objs := kbl_da7219_max98357a.o snd-soc-kbl_da7219_max98927-objs := kbl_da7219_max98927.o snd-soc-kbl_rt5663_max98927-objs := kbl_rt5663_max98927.o snd-soc-kbl_rt5663_rt5514_max98927-objs := kbl_rt5663_rt5514_max98927.o +snd-soc-kbl_rt5660-objs := kbl_rt5660.o snd-soc-skl_rt286-objs := skl_rt286.o snd-soc-skl_hda_dsp-objs := skl_hda_dsp_generic.o skl_hda_dsp_common.o snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o @@ -46,6 +47,7 @@ obj-$(CONFIG_SND_SOC_INTEL_KBL_DA7219_MAX98357A_MACH) += snd-soc-kbl_da7219_max9 obj-$(CONFIG_SND_SOC_INTEL_KBL_DA7219_MAX98927_MACH) += snd-soc-kbl_da7219_max98927.o obj-$(CONFIG_SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH) += snd-soc-kbl_rt5663_max98927.o obj-$(CONFIG_SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH) += snd-soc-kbl_rt5663_rt5514_max98927.o +obj-$(CONFIG_SND_SOC_INTEL_KBL_RT5660_MACH) += snd-soc-kbl_rt5660.o obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 8587bd3d1cc1..a22366ce33c4 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -29,7 +29,6 @@ #include <linux/input.h> #include <linux/slab.h> #include <asm/cpu_device_id.h> -#include <asm/platform_sst_audio.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -674,6 +673,33 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_SSP0_AIF2 | BYT_RT5640_MCLK_EN), }, + { /* Point of View Mobii TAB-P1005W-232 (V2.0) */ + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "POV"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "I102A"), + }, + .driver_data = (void *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_JD_SRC_JD2_IN4N | + BYT_RT5640_OVCD_TH_2000UA | + BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_DIFF_MIC | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN), + }, + { + /* Prowise PT301 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Prowise"), + DMI_MATCH(DMI_PRODUCT_NAME, "PT301"), + }, + .driver_data = (void *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_JD_SRC_JD2_IN4N | + BYT_RT5640_OVCD_TH_2000UA | + BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_DIFF_MIC | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN), + }, { .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "TECLAST"), @@ -1152,10 +1178,7 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) * (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) + if (mach->mach_params.acpi_ipc_irq_index == 0) is_bytcr = true; } diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index c44298130720..e528995668b7 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -32,7 +32,6 @@ #include <linux/slab.h> #include <asm/cpu_device_id.h> #include <asm/intel-family.h> -#include <asm/platform_sst_audio.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -920,10 +919,7 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) * (will be overridden if DMI quirk is detected) */ if (x86_match_cpu(baytrail_cpu_ids)) { - 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) + if (mach->mach_params.acpi_ipc_irq_index == 0) is_bytcr = true; } diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c index 9d9f6e41d81c..08a5152e635a 100644 --- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c +++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c @@ -390,6 +390,20 @@ static struct snd_soc_card snd_soc_card_cht = { static const struct dmi_system_id cht_max98090_quirk_table[] = { { + /* Clapper model Chromebook */ + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "Clapper"), + }, + .driver_data = (void *)QUIRK_PMC_PLT_CLK_0, + }, + { + /* Gnawty model Chromebook (Acer Chromebook CB3-111) */ + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "Gnawty"), + }, + .driver_data = (void *)QUIRK_PMC_PLT_CLK_0, + }, + { /* Swanky model Chromebook (Toshiba Chromebook 2) */ .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "Swanky"), diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index f5a5ea6a093c..250a356a0cbf 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -27,7 +27,6 @@ #include <linux/dmi.h> #include <linux/slab.h> #include <asm/cpu_device_id.h> -#include <asm/platform_sst_audio.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -585,10 +584,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) * (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) + if (mach->mach_params.acpi_ipc_irq_index == 0) is_bytcr = true; } diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index 51f0d45d6f8f..9de64f447e7b 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -403,7 +403,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) const char *i2c_name; int i; - drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC); + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); if (!drv) return -ENOMEM; diff --git a/sound/soc/intel/boards/glk_rt5682_max98357a.c b/sound/soc/intel/boards/glk_rt5682_max98357a.c index c4b94e2617c5..c74c4f17316f 100644 --- a/sound/soc/intel/boards/glk_rt5682_max98357a.c +++ b/sound/soc/intel/boards/glk_rt5682_max98357a.c @@ -603,7 +603,7 @@ static int geminilake_audio_probe(struct platform_device *pdev) { struct glk_card_private *ctx; - ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; diff --git a/sound/soc/intel/boards/kbl_da7219_max98927.c b/sound/soc/intel/boards/kbl_da7219_max98927.c index 3fa1c3ca6d37..723a4935ed76 100644 --- a/sound/soc/intel/boards/kbl_da7219_max98927.c +++ b/sound/soc/intel/boards/kbl_da7219_max98927.c @@ -262,9 +262,9 @@ static int kabylake_da7219_codec_init(struct snd_soc_pcm_runtime *rtd) jack = &ctx->kabylake_headset; snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); - snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); - snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); - snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND); da7219_aad_jack_det(component, &ctx->kabylake_headset); @@ -441,7 +441,7 @@ static int kabylake_refcap_startup(struct snd_pcm_substream *substream) } -static struct snd_soc_ops skylaye_refcap_ops = { +static struct snd_soc_ops skylake_refcap_ops = { .startup = kabylake_refcap_startup, }; @@ -525,7 +525,7 @@ static struct snd_soc_dai_link kabylake_dais[] = { .dpcm_capture = 1, .nonatomic = 1, .dynamic = 1, - .ops = &skylaye_refcap_ops, + .ops = &skylake_refcap_ops, }, [KBL_DPCM_AUDIO_DMIC_CP] = { .name = "Kbl Audio DMIC cap", @@ -736,7 +736,7 @@ static struct snd_soc_dai_link kabylake_max98927_dais[] = { .dpcm_capture = 1, .nonatomic = 1, .dynamic = 1, - .ops = &skylaye_refcap_ops, + .ops = &skylake_refcap_ops, }, [KBL_DPCM_AUDIO_DMIC_CP] = { .name = "Kbl Audio DMIC cap", @@ -935,7 +935,7 @@ static int kabylake_audio_probe(struct platform_device *pdev) { struct kbl_codec_private *ctx; - ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; diff --git a/sound/soc/intel/boards/kbl_rt5660.c b/sound/soc/intel/boards/kbl_rt5660.c new file mode 100644 index 000000000000..3255e0029276 --- /dev/null +++ b/sound/soc/intel/boards/kbl_rt5660.c @@ -0,0 +1,543 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2018-19 Canonical Corporation. + +/* + * Intel Kabylake I2S Machine Driver with RT5660 Codec + * + * Modified from: + * Intel Kabylake I2S Machine driver supporting MAXIM98357a and + * DA7219 codecs + * Also referred to: + * Intel Broadwell I2S Machine driver supporting RT5677 codec + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/gpio/consumer.h> +#include <linux/acpi.h> +#include <sound/core.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "../../codecs/hdac_hdmi.h" +#include "../../codecs/rt5660.h" + +#define KBL_RT5660_CODEC_DAI "rt5660-aif1" +#define DUAL_CHANNEL 2 + +static struct snd_soc_card *kabylake_audio_card; +static struct snd_soc_jack skylake_hdmi[3]; +static struct snd_soc_jack lineout_jack; +static struct snd_soc_jack mic_jack; + +struct kbl_hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +struct kbl_codec_private { + struct gpio_desc *gpio_lo_mute; + struct list_head hdmi_pcm_list; +}; + +enum { + KBL_DPCM_AUDIO_PB = 0, + KBL_DPCM_AUDIO_CP, + KBL_DPCM_AUDIO_HDMI1_PB, + KBL_DPCM_AUDIO_HDMI2_PB, + KBL_DPCM_AUDIO_HDMI3_PB, +}; + +#define GPIO_LINEOUT_MUTE_INDEX 0 +#define GPIO_LINEOUT_DET_INDEX 3 +#define GPIO_LINEIN_DET_INDEX 4 + +static const struct acpi_gpio_params lineout_mute_gpio = { GPIO_LINEOUT_MUTE_INDEX, 0, true }; +static const struct acpi_gpio_params lineout_det_gpio = { GPIO_LINEOUT_DET_INDEX, 0, false }; +static const struct acpi_gpio_params mic_det_gpio = { GPIO_LINEIN_DET_INDEX, 0, false }; + + +static const struct acpi_gpio_mapping acpi_rt5660_gpios[] = { + { "lineout-mute-gpios", &lineout_mute_gpio, 1 }, + { "lineout-det-gpios", &lineout_det_gpio, 1 }, + { "mic-det-gpios", &mic_det_gpio, 1 }, + { NULL }, +}; + +static struct snd_soc_jack_pin lineout_jack_pin = { + .pin = "Line Out", + .mask = SND_JACK_LINEOUT, +}; + +static struct snd_soc_jack_pin mic_jack_pin = { + .pin = "Line In", + .mask = SND_JACK_MICROPHONE, +}; + +static struct snd_soc_jack_gpio lineout_jack_gpio = { + .name = "lineout-det", + .report = SND_JACK_LINEOUT, + .debounce_time = 200, +}; + +static struct snd_soc_jack_gpio mic_jack_gpio = { + .name = "mic-det", + .report = SND_JACK_MICROPHONE, + .debounce_time = 200, +}; + +static int kabylake_5660_event_lineout(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct kbl_codec_private *priv = snd_soc_card_get_drvdata(dapm->card); + + gpiod_set_value_cansleep(priv->gpio_lo_mute, + !(SND_SOC_DAPM_EVENT_ON(event))); + + return 0; +} + +static const struct snd_kcontrol_new kabylake_rt5660_controls[] = { + SOC_DAPM_PIN_SWITCH("Line In"), + SOC_DAPM_PIN_SWITCH("Line Out"), +}; + +static const struct snd_soc_dapm_widget kabylake_rt5660_widgets[] = { + SND_SOC_DAPM_MIC("Line In", NULL), + SND_SOC_DAPM_LINE("Line Out", kabylake_5660_event_lineout), +}; + +static const struct snd_soc_dapm_route kabylake_rt5660_map[] = { + /* other jacks */ + {"IN1P", NULL, "Line In"}, + {"IN2P", NULL, "Line In"}, + {"Line Out", NULL, "LOUTR"}, + {"Line Out", NULL, "LOUTL"}, + + /* CODEC BE connections */ + { "AIF1 Playback", NULL, "ssp0 Tx"}, + { "ssp0 Tx", NULL, "codec0_out"}, + + { "codec0_in", NULL, "ssp0 Rx" }, + { "ssp0 Rx", NULL, "AIF1 Capture" }, + + { "hifi1", NULL, "iDisp1 Tx"}, + { "iDisp1 Tx", NULL, "iDisp1_out"}, + { "hifi2", NULL, "iDisp2 Tx"}, + { "iDisp2 Tx", NULL, "iDisp2_out"}, + { "hifi3", NULL, "iDisp3 Tx"}, + { "iDisp3 Tx", NULL, "iDisp3_out"}, +}; + +static int kabylake_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); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* The ADSP will convert the FE rate to 48k, stereo */ + rate->min = rate->max = 48000; + channels->min = channels->max = DUAL_CHANNEL; + + /* set SSP0 to 24 bit */ + snd_mask_none(fmt); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + +static int kabylake_rt5660_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret; + struct kbl_codec_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + ret = devm_acpi_dev_add_driver_gpios(component->dev, acpi_rt5660_gpios); + if (ret) + dev_warn(component->dev, "Failed to add driver gpios\n"); + + /* Request rt5660 GPIO for lineout mute control, return if fails */ + ctx->gpio_lo_mute = devm_gpiod_get(component->dev, "lineout-mute", + GPIOD_OUT_HIGH); + if (IS_ERR(ctx->gpio_lo_mute)) { + dev_err(component->dev, "Can't find GPIO_MUTE# gpio\n"); + return PTR_ERR(ctx->gpio_lo_mute); + } + + /* Create and initialize headphone jack, this jack is not mandatory, don't return if fails */ + ret = snd_soc_card_jack_new(rtd->card, "Lineout Jack", + SND_JACK_LINEOUT, &lineout_jack, + &lineout_jack_pin, 1); + if (ret) + dev_warn(component->dev, "Can't create Lineout jack\n"); + else { + lineout_jack_gpio.gpiod_dev = component->dev; + ret = snd_soc_jack_add_gpios(&lineout_jack, 1, + &lineout_jack_gpio); + if (ret) + dev_warn(component->dev, "Can't add Lineout jack gpio\n"); + } + + /* Create and initialize mic jack, this jack is not mandatory, don't return if fails */ + ret = snd_soc_card_jack_new(rtd->card, "Mic Jack", + SND_JACK_MICROPHONE, &mic_jack, + &mic_jack_pin, 1); + if (ret) + dev_warn(component->dev, "Can't create mic jack\n"); + else { + mic_jack_gpio.gpiod_dev = component->dev; + ret = snd_soc_jack_add_gpios(&mic_jack, 1, &mic_jack_gpio); + if (ret) + dev_warn(component->dev, "Can't add mic jack gpio\n"); + } + + /* Here we enable some dapms in advance to reduce the pop noise for recording via line-in */ + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1"); + snd_soc_dapm_force_enable_pin(dapm, "BST1"); + snd_soc_dapm_force_enable_pin(dapm, "BST2"); + + return 0; +} + +static int kabylake_hdmi_init(struct snd_soc_pcm_runtime *rtd, int device) +{ + struct kbl_codec_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai = rtd->codec_dai; + struct kbl_hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + pcm->device = device; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + +static int kabylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd) +{ + return kabylake_hdmi_init(rtd, KBL_DPCM_AUDIO_HDMI1_PB); +} + +static int kabylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd) +{ + return kabylake_hdmi_init(rtd, KBL_DPCM_AUDIO_HDMI2_PB); +} + +static int kabylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd) +{ + return kabylake_hdmi_init(rtd, KBL_DPCM_AUDIO_HDMI3_PB); +} + +static int kabylake_rt5660_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, + RT5660_SCLK_S_PLL1, params_rate(params) * 512, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_pll(codec_dai, 0, + RT5660_PLL1_S_BCLK, + params_rate(params) * 50, + params_rate(params) * 512); + if (ret < 0) + dev_err(codec_dai->dev, "can't set codec pll: %d\n", ret); + + return ret; +} + +static struct snd_soc_ops kabylake_rt5660_ops = { + .hw_params = kabylake_rt5660_hw_params, +}; + +static const unsigned int rates[] = { + 48000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + +static const unsigned int channels[] = { + DUAL_CHANNEL, +}; + +static const struct snd_pcm_hw_constraint_list constraints_channels = { + .count = ARRAY_SIZE(channels), + .list = channels, + .mask = 0, +}; + +static int kbl_fe_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + /* + * On this platform for PCM device we support, + * 48Khz + * stereo + * 16 bit audio + */ + + runtime->hw.channels_max = DUAL_CHANNEL; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels); + + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; + snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16); + + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); + + return 0; +} + +static const struct snd_soc_ops kabylake_rt5660_fe_ops = { + .startup = kbl_fe_startup, +}; + +/* kabylake digital audio interface glue - connects rt5660 codec <--> CPU */ +static struct snd_soc_dai_link kabylake_rt5660_dais[] = { + /* Front End DAI links */ + [KBL_DPCM_AUDIO_PB] = { + .name = "Kbl Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .nonatomic = 1, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + .ops = &kabylake_rt5660_fe_ops, + }, + [KBL_DPCM_AUDIO_CP] = { + .name = "Kbl Audio Capture Port", + .stream_name = "Audio Record", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .nonatomic = 1, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_capture = 1, + .ops = &kabylake_rt5660_fe_ops, + }, + [KBL_DPCM_AUDIO_HDMI1_PB] = { + .name = "Kbl HDMI Port1", + .stream_name = "Hdmi1", + .cpu_dai_name = "HDMI1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .nonatomic = 1, + .dynamic = 1, + }, + [KBL_DPCM_AUDIO_HDMI2_PB] = { + .name = "Kbl HDMI Port2", + .stream_name = "Hdmi2", + .cpu_dai_name = "HDMI2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .nonatomic = 1, + .dynamic = 1, + }, + [KBL_DPCM_AUDIO_HDMI3_PB] = { + .name = "Kbl HDMI Port3", + .stream_name = "Hdmi3", + .cpu_dai_name = "HDMI3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + + /* Back End DAI links */ + { + /* SSP0 - Codec */ + .name = "SSP0-Codec", + .id = 0, + .cpu_dai_name = "SSP0 Pin", + .platform_name = "0000:00:1f.3", + .no_pcm = 1, + .codec_name = "i2c-10EC3277:00", + .codec_dai_name = KBL_RT5660_CODEC_DAI, + .init = kabylake_rt5660_codec_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = kabylake_ssp0_fixup, + .ops = &kabylake_rt5660_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "iDisp1", + .id = 1, + .cpu_dai_name = "iDisp1 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi1", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = kabylake_hdmi1_init, + .no_pcm = 1, + }, + { + .name = "iDisp2", + .id = 2, + .cpu_dai_name = "iDisp2 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi2", + .platform_name = "0000:00:1f.3", + .init = kabylake_hdmi2_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp3", + .id = 3, + .cpu_dai_name = "iDisp3 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi3", + .platform_name = "0000:00:1f.3", + .init = kabylake_hdmi3_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, +}; + + +#define NAME_SIZE 32 +static int kabylake_card_late_probe(struct snd_soc_card *card) +{ + struct kbl_codec_private *ctx = snd_soc_card_get_drvdata(card); + struct kbl_hdmi_pcm *pcm; + struct snd_soc_component *component = NULL; + int err, i = 0; + char jack_name[NAME_SIZE]; + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + component = pcm->codec_dai->component; + snprintf(jack_name, sizeof(jack_name), + "HDMI/DP, pcm=%d Jack", pcm->device); + err = snd_soc_card_jack_new(card, jack_name, + SND_JACK_AVOUT, &skylake_hdmi[i], + NULL, 0); + + if (err) + return err; + + err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, + &skylake_hdmi[i]); + if (err < 0) + return err; + + i++; + + } + + if (!component) + return -EINVAL; + + return hdac_hdmi_jack_port_init(component, &card->dapm); +} + +/* kabylake audio machine driver for rt5660 */ +static struct snd_soc_card kabylake_audio_card_rt5660 = { + .name = "kblrt5660", + .owner = THIS_MODULE, + .dai_link = kabylake_rt5660_dais, + .num_links = ARRAY_SIZE(kabylake_rt5660_dais), + .controls = kabylake_rt5660_controls, + .num_controls = ARRAY_SIZE(kabylake_rt5660_controls), + .dapm_widgets = kabylake_rt5660_widgets, + .num_dapm_widgets = ARRAY_SIZE(kabylake_rt5660_widgets), + .dapm_routes = kabylake_rt5660_map, + .num_dapm_routes = ARRAY_SIZE(kabylake_rt5660_map), + .fully_routed = true, + .late_probe = kabylake_card_late_probe, +}; + +static int kabylake_audio_probe(struct platform_device *pdev) +{ + struct kbl_codec_private *ctx; + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + + kabylake_audio_card = + (struct snd_soc_card *)pdev->id_entry->driver_data; + + kabylake_audio_card->dev = &pdev->dev; + snd_soc_card_set_drvdata(kabylake_audio_card, ctx); + return devm_snd_soc_register_card(&pdev->dev, kabylake_audio_card); +} + +static const struct platform_device_id kbl_board_ids[] = { + { + .name = "kbl_rt5660", + .driver_data = + (kernel_ulong_t)&kabylake_audio_card_rt5660, + }, + { } +}; + +static struct platform_driver kabylake_audio = { + .probe = kabylake_audio_probe, + .driver = { + .name = "kbl_rt5660", + .pm = &snd_soc_pm_ops, + }, + .id_table = kbl_board_ids, +}; + +module_platform_driver(kabylake_audio) + +/* Module information */ +MODULE_DESCRIPTION("Audio Machine driver-RT5660 in I2S mode"); +MODULE_AUTHOR("Hui Wang <hui.wang@canonical.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:kbl_rt5660"); diff --git a/sound/soc/intel/boards/kbl_rt5663_max98927.c b/sound/soc/intel/boards/kbl_rt5663_max98927.c index 99e1320c485f..d71475200b08 100644 --- a/sound/soc/intel/boards/kbl_rt5663_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_max98927.c @@ -25,9 +25,9 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/soc-acpi.h> #include "../../codecs/rt5663.h" #include "../../codecs/hdac_hdmi.h" -#include "../skylake/skl.h" #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/clkdev.h> @@ -586,7 +586,7 @@ static int kabylake_refcap_startup(struct snd_pcm_substream *substream) &constraints_16000); } -static struct snd_soc_ops skylaye_refcap_ops = { +static struct snd_soc_ops skylake_refcap_ops = { .startup = kabylake_refcap_startup, }; @@ -655,7 +655,7 @@ static struct snd_soc_dai_link kabylake_dais[] = { .dpcm_capture = 1, .nonatomic = 1, .dynamic = 1, - .ops = &skylaye_refcap_ops, + .ops = &skylake_refcap_ops, }, [KBL_DPCM_AUDIO_DMIC_CP] = { .name = "Kbl Audio DMIC cap", @@ -969,7 +969,7 @@ static struct snd_soc_card kabylake_audio_card_rt5663 = { static int kabylake_audio_probe(struct platform_device *pdev) { struct kbl_rt5663_private *ctx; - struct skl_machine_pdata *pdata; + struct snd_soc_acpi_mach *mach; int ret; ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); @@ -984,9 +984,9 @@ static int kabylake_audio_probe(struct platform_device *pdev) kabylake_audio_card->dev = &pdev->dev; snd_soc_card_set_drvdata(kabylake_audio_card, ctx); - pdata = dev_get_drvdata(&pdev->dev); - if (pdata) - dmic_constraints = pdata->dmic_num == 2 ? + mach = (&pdev->dev)->platform_data; + if (mach) + dmic_constraints = mach->mach_params.dmic_num == 2 ? &constraints_dmic_2ch : &constraints_dmic_channels; ctx->mclk = devm_clk_get(&pdev->dev, "ssp1_mclk"); diff --git a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c index a737c915d46a..7044d8c2b187 100644 --- a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c @@ -26,10 +26,10 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/soc-acpi.h> #include "../../codecs/rt5514.h" #include "../../codecs/rt5663.h" #include "../../codecs/hdac_hdmi.h" -#include "../skylake/skl.h" #define KBL_REALTEK_CODEC_DAI "rt5663-aif" #define KBL_REALTEK_DMIC_CODEC_DAI "rt5514-aif1" @@ -648,7 +648,7 @@ static struct snd_soc_card kabylake_audio_card = { static int kabylake_audio_probe(struct platform_device *pdev) { struct kbl_codec_private *ctx; - struct skl_machine_pdata *pdata; + struct snd_soc_acpi_mach *mach; ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -659,9 +659,9 @@ static int kabylake_audio_probe(struct platform_device *pdev) kabylake_audio_card.dev = &pdev->dev; snd_soc_card_set_drvdata(&kabylake_audio_card, ctx); - pdata = dev_get_drvdata(&pdev->dev); - if (pdata) - dmic_constraints = pdata->dmic_num == 2 ? + mach = (&pdev->dev)->platform_data; + if (mach) + dmic_constraints = mach->mach_params.dmic_num == 2 ? &constraints_dmic_2ch : &constraints_dmic_channels; return devm_snd_soc_register_card(&pdev->dev, &kabylake_audio_card); diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c index b415dd4c85f5..b9a21e64ead2 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_generic.c +++ b/sound/soc/intel/boards/skl_hda_dsp_generic.c @@ -12,8 +12,8 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/soc-acpi.h> #include "../../codecs/hdac_hdmi.h" -#include "../skylake/skl.h" #include "skl_hda_dsp_common.h" static const struct snd_soc_dapm_widget skl_hda_widgets[] = { @@ -101,17 +101,17 @@ static struct snd_soc_card hda_soc_card = { #define IDISP_ROUTE_COUNT (IDISP_DAI_COUNT * 2) #define IDISP_CODEC_MASK 0x4 -static int skl_hda_fill_card_info(struct skl_machine_pdata *pdata) +static int skl_hda_fill_card_info(struct snd_soc_acpi_mach_params *mach_params) { struct snd_soc_card *card = &hda_soc_card; struct snd_soc_dai_link *dai_link; u32 codec_count, codec_mask; int i, num_links, num_route; - codec_mask = pdata->codec_mask; + codec_mask = mach_params->codec_mask; codec_count = hweight_long(codec_mask); - if (codec_count == 1 && pdata->codec_mask & IDISP_CODEC_MASK) { + if (codec_count == 1 && codec_mask & IDISP_CODEC_MASK) { num_links = IDISP_DAI_COUNT; num_route = IDISP_ROUTE_COUNT; } else if (codec_count == 2 && codec_mask & IDISP_CODEC_MASK) { @@ -127,30 +127,30 @@ static int skl_hda_fill_card_info(struct skl_machine_pdata *pdata) card->num_dapm_routes = num_route; for_each_card_prelinks(card, i, dai_link) - dai_link->platform_name = pdata->platform; + dai_link->platform_name = mach_params->platform; return 0; } static int skl_hda_audio_probe(struct platform_device *pdev) { - struct skl_machine_pdata *pdata; + struct snd_soc_acpi_mach *mach; struct skl_hda_private *ctx; int ret; dev_dbg(&pdev->dev, "%s: entry\n", __func__); - ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; INIT_LIST_HEAD(&ctx->hdmi_pcm_list); - pdata = dev_get_drvdata(&pdev->dev); - if (!pdata) + mach = (&pdev->dev)->platform_data; + if (!mach) return -EINVAL; - ret = skl_hda_fill_card_info(pdata); + ret = skl_hda_fill_card_info(&mach->mach_params); if (ret < 0) { dev_err(&pdev->dev, "Unsupported HDAudio/iDisp configuration found\n"); return ret; @@ -158,7 +158,7 @@ static int skl_hda_audio_probe(struct platform_device *pdev) ctx->pcm_count = hda_soc_card.num_links; ctx->dai_index = 1; /* hdmi codec dai name starts from index 1 */ - ctx->platform_name = pdata->platform; + ctx->platform_name = mach->mach_params.platform; hda_soc_card.dev = &pdev->dev; snd_soc_card_set_drvdata(&hda_soc_card, ctx); diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c index d31482b8c9bb..0922106bd323 100644 --- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c +++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c @@ -21,9 +21,9 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/soc-acpi.h> #include "../../codecs/nau8825.h" #include "../../codecs/hdac_hdmi.h" -#include "../skylake/skl.h" #define SKL_NUVOTON_CODEC_DAI "nau8825-hifi" #define SKL_MAXIM_CODEC_DAI "HiFi" @@ -400,7 +400,7 @@ static int skylake_refcap_startup(struct snd_pcm_substream *substream) &constraints_16000); } -static const struct snd_soc_ops skylaye_refcap_ops = { +static const struct snd_soc_ops skylake_refcap_ops = { .startup = skylake_refcap_startup, }; @@ -447,7 +447,7 @@ static struct snd_soc_dai_link skylake_dais[] = { .dpcm_capture = 1, .nonatomic = 1, .dynamic = 1, - .ops = &skylaye_refcap_ops, + .ops = &skylake_refcap_ops, }, [SKL_DPCM_AUDIO_DMIC_CP] = { .name = "Skl Audio DMIC cap", @@ -641,7 +641,7 @@ static struct snd_soc_card skylake_audio_card = { static int skylake_audio_probe(struct platform_device *pdev) { struct skl_nau8825_private *ctx; - struct skl_machine_pdata *pdata; + struct snd_soc_acpi_mach *mach; ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -652,9 +652,9 @@ static int skylake_audio_probe(struct platform_device *pdev) skylake_audio_card.dev = &pdev->dev; snd_soc_card_set_drvdata(&skylake_audio_card, ctx); - pdata = dev_get_drvdata(&pdev->dev); - if (pdata) - dmic_constraints = pdata->dmic_num == 2 ? + mach = (&pdev->dev)->platform_data; + if (mach) + dmic_constraints = mach->mach_params.dmic_num == 2 ? &constraints_dmic_2ch : &constraints_dmic_channels; return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card); diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c index e877bb60beb1..8433c521d39f 100644 --- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c +++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c @@ -23,11 +23,11 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> +#include <sound/soc-acpi.h> #include <sound/jack.h> #include <sound/pcm_params.h> #include "../../codecs/nau8825.h" #include "../../codecs/hdac_hdmi.h" -#include "../skylake/skl.h" #define SKL_NUVOTON_CODEC_DAI "nau8825-hifi" #define SKL_SSM_CODEC_DAI "ssm4567-hifi" @@ -449,7 +449,7 @@ static int skylake_refcap_startup(struct snd_pcm_substream *substream) &constraints_16000); } -static const struct snd_soc_ops skylaye_refcap_ops = { +static const struct snd_soc_ops skylake_refcap_ops = { .startup = skylake_refcap_startup, }; @@ -496,7 +496,7 @@ static struct snd_soc_dai_link skylake_dais[] = { .dpcm_capture = 1, .nonatomic = 1, .dynamic = 1, - .ops = &skylaye_refcap_ops, + .ops = &skylake_refcap_ops, }, [SKL_DPCM_AUDIO_DMIC_CP] = { .name = "Skl Audio DMIC cap", @@ -694,7 +694,7 @@ static struct snd_soc_card skylake_audio_card = { static int skylake_audio_probe(struct platform_device *pdev) { struct skl_nau88125_private *ctx; - struct skl_machine_pdata *pdata; + struct snd_soc_acpi_mach *mach; ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -705,9 +705,9 @@ static int skylake_audio_probe(struct platform_device *pdev) skylake_audio_card.dev = &pdev->dev; snd_soc_card_set_drvdata(&skylake_audio_card, ctx); - pdata = dev_get_drvdata(&pdev->dev); - if (pdata) - dmic_constraints = pdata->dmic_num == 2 ? + mach = (&pdev->dev)->platform_data; + if (mach) + dmic_constraints = mach->mach_params.dmic_num == 2 ? &constraints_dmic_2ch : &constraints_dmic_channels; return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card); diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index c1f50a079d34..56c81e20b5bf 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -7,7 +7,7 @@ snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-m soc-acpi-intel-hsw-bdw-match.o \ soc-acpi-intel-skl-match.o soc-acpi-intel-kbl-match.o \ soc-acpi-intel-bxt-match.o soc-acpi-intel-glk-match.o \ - soc-acpi-intel-cnl-match.o \ + soc-acpi-intel-cnl-match.o soc-acpi-intel-icl-match.o \ soc-acpi-intel-hda-match.o obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o diff --git a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c index f39386e540d3..61dedc103b19 100644 --- a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c @@ -6,9 +6,41 @@ * */ +#include <linux/dmi.h> #include <sound/soc-acpi.h> #include <sound/soc-acpi-intel-match.h> +enum { + APL_RVP, +}; + +static const struct dmi_system_id apl_table[] = { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."), + DMI_MATCH(DMI_BOARD_NAME, "Apollolake RVP1A"), + }, + .driver_data = (void *)(APL_RVP), + }, + {} +}; + +static struct snd_soc_acpi_mach *apl_quirk(void *arg) +{ + struct snd_soc_acpi_mach *mach = arg; + const struct dmi_system_id *dmi_id; + unsigned long apl_machine_id; + + dmi_id = dmi_first_match(apl_table); + if (dmi_id) { + apl_machine_id = (unsigned long)dmi_id->driver_data; + if (apl_machine_id == APL_RVP) + return NULL; + } + + return mach; +} + static struct snd_soc_acpi_codecs bxt_codecs = { .num_codecs = 1, .codecs = {"MX98357A"} @@ -19,6 +51,9 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { .id = "INT343A", .drv_name = "bxt_alc298s_i2s", .fw_filename = "intel/dsp_fw_bxtn.bin", + .sof_fw_filename = "intel/sof-apl.ri", + .sof_tplg_filename = "intel/sof-apl-rt298.tplg", + .asoc_plat_name = "0000:00:0e.0", }, { .id = "DLGS7219", @@ -47,6 +82,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { { .id = "INT34C3", .drv_name = "bxt_tdf8532", + .machine_quirk = apl_quirk, .sof_fw_filename = "intel/sof-apl.ri", .sof_tplg_filename = "intel/sof-apl-tdf8532.tplg", .asoc_plat_name = "0000:00:0e.0", diff --git a/sound/soc/intel/common/soc-acpi-intel-icl-match.c b/sound/soc/intel/common/soc-acpi-intel-icl-match.c new file mode 100644 index 000000000000..33b441dca4d3 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-icl-match.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-icl-match.c - tables and support for ICL ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> +#include "../skylake/skl.h" + +static struct skl_machine_pdata icl_pdata = { + .use_tplg_pcm = true, +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[] = { + { + .id = "INT34C2", + .drv_name = "icl_rt274", + .fw_filename = "intel/dsp_fw_icl.bin", + .pdata = &icl_pdata, + .sof_fw_filename = "intel/sof-icl.ri", + .sof_tplg_filename = "intel/sof-icl-rt274.tplg", + .asoc_plat_name = "0000:00:1f.3", + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_icl_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-kbl-match.c b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c index a317b7790fce..e6fa6f470526 100644 --- a/sound/soc/intel/common/soc-acpi-intel-kbl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c @@ -96,6 +96,16 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[] = { .quirk_data = &kbl_7219_98927_codecs, .pdata = &skl_dmic_data }, + { + .id = "10EC5660", + .drv_name = "kbl_rt5660", + .fw_filename = "intel/dsp_fw_kbl.bin", + }, + { + .id = "10EC3277", + .drv_name = "kbl_rt5660", + .fw_filename = "intel/dsp_fw_kbl.bin", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_kbl_machines); diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 8bfb8b0fa3d5..b0e6fb93eaf8 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -247,6 +247,14 @@ static const struct skl_dsp_ops dsp_ops[] = { .init_fw = cnl_sst_init_fw, .cleanup = cnl_sst_dsp_cleanup }, + { + .id = 0xa348, + .num_cores = 4, + .loader_ops = bxt_get_loader_ops, + .init = cnl_sst_dsp_init, + .init_fw = cnl_sst_init_fw, + .cleanup = cnl_sst_dsp_cleanup + }, }; const struct skl_dsp_ops *skl_get_dsp_ops(int pci_id) diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index 01a050cf8775..5d125a3df527 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -180,6 +180,9 @@ int skl_get_dmic_geo(struct skl *skl) unsigned int dmic_geo = 0; u8 j; + if (!nhlt) + return 0; + epnt = (struct nhlt_endpoint *)nhlt->desc; for (j = 0; j < nhlt->endpoint_count; j++) { diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 5234fafb758a..9f3ce73593ae 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -249,6 +249,8 @@ enum skl_ipc_glb_reply { IPC_GLB_REPLY_INVALID_CONFIG_DATA_LEN = 121, IPC_GLB_REPLY_GATEWAY_NOT_INITIALIZED = 140, IPC_GLB_REPLY_GATEWAY_NOT_EXIST = 141, + IPC_GLB_REPLY_SCLK_ALREADY_RUNNING = 150, + IPC_GLB_REPLY_MCLK_ALREADY_RUNNING = 151, IPC_GLB_REPLY_PPL_NOT_INITIALIZED = 160, IPC_GLB_REPLY_PPL_NOT_EXIST = 161, @@ -392,18 +394,47 @@ int skl_ipc_process_notification(struct sst_generic_ipc *ipc, return 0; } -static int skl_ipc_set_reply_error_code(u32 reply) +struct skl_ipc_err_map { + const char *msg; + enum skl_ipc_glb_reply reply; + int err; +}; + +static struct skl_ipc_err_map skl_err_map[] = { + {"DSP out of memory", IPC_GLB_REPLY_OUT_OF_MEMORY, -ENOMEM}, + {"DSP busy", IPC_GLB_REPLY_BUSY, -EBUSY}, + {"SCLK already running", IPC_GLB_REPLY_SCLK_ALREADY_RUNNING, + IPC_GLB_REPLY_SCLK_ALREADY_RUNNING}, + {"MCLK already running", IPC_GLB_REPLY_MCLK_ALREADY_RUNNING, + IPC_GLB_REPLY_MCLK_ALREADY_RUNNING}, +}; + +static int skl_ipc_set_reply_error_code(struct sst_generic_ipc *ipc, u32 reply) { - switch (reply) { - case IPC_GLB_REPLY_OUT_OF_MEMORY: - return -ENOMEM; + int i; - case IPC_GLB_REPLY_BUSY: - return -EBUSY; + for (i = 0; i < ARRAY_SIZE(skl_err_map); i++) { + if (skl_err_map[i].reply == reply) + break; + } - default: + if (i == ARRAY_SIZE(skl_err_map)) { + dev_err(ipc->dev, "ipc FW reply: %d FW Error Code: %u\n", + reply, + ipc->dsp->fw_ops.get_fw_errcode(ipc->dsp)); return -EINVAL; } + + if (skl_err_map[i].err < 0) + dev_err(ipc->dev, "ipc FW reply: %s FW Error Code: %u\n", + skl_err_map[i].msg, + ipc->dsp->fw_ops.get_fw_errcode(ipc->dsp)); + else + dev_info(ipc->dev, "ipc FW reply: %s FW Error Code: %u\n", + skl_err_map[i].msg, + ipc->dsp->fw_ops.get_fw_errcode(ipc->dsp)); + + return skl_err_map[i].err; } void skl_ipc_process_reply(struct sst_generic_ipc *ipc, @@ -441,10 +472,7 @@ void skl_ipc_process_reply(struct sst_generic_ipc *ipc, } } else { - msg->errno = skl_ipc_set_reply_error_code(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)); + msg->errno = skl_ipc_set_reply_error_code(ipc, reply); switch (IPC_GLB_NOTIFY_MSG_TYPE(header.primary)) { case IPC_GLB_LOAD_MULTIPLE_MODS: case IPC_GLB_LOAD_LIBRARY: diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 7487f388e65d..60c94836bf5b 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -40,6 +40,9 @@ #if IS_ENABLED(CONFIG_SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC) #include "../../../soc/codecs/hdac_hda.h" #endif +static int skl_pci_binding; +module_param_named(pci_binding, skl_pci_binding, int, 0444); +MODULE_PARM_DESC(pci_binding, "PCI binding (0=auto, 1=only legacy, 2=only asoc"); /* * initialize the PCI registers @@ -311,7 +314,7 @@ static int skl_suspend(struct device *dev) struct pci_dev *pci = to_pci_dev(dev); struct hdac_bus *bus = pci_get_drvdata(pci); struct skl *skl = bus_to_skl(bus); - int ret = 0; + int ret; /* * Do not suspend if streams which are marked ignore suspend are @@ -333,14 +336,10 @@ static int skl_suspend(struct device *dev) skl->skl_sst->fw_loaded = false; } - if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { - ret = snd_hdac_display_power(bus, false); - if (ret < 0) - dev_err(bus->dev, - "Cannot turn OFF display power on i915\n"); - } + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) + snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false); - return ret; + return 0; } static int skl_resume(struct device *dev) @@ -352,14 +351,8 @@ static int skl_resume(struct device *dev) int ret; /* Turned OFF in HDMI codec driver after codec reconfiguration */ - if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { - ret = snd_hdac_display_power(bus, true); - if (ret < 0) { - dev_err(bus->dev, - "Cannot turn on display power on i915\n"); - return ret; - } - } + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) + snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, true); /* * resume only when we are not in suspend active, otherwise need to @@ -517,7 +510,7 @@ static int skl_find_machine(struct skl *skl, void *driver_data) if (pdata) { skl->use_tplg_pcm = pdata->use_tplg_pcm; - pdata->dmic_num = skl_get_dmic_geo(skl); + mach->mach_params.dmic_num = skl_get_dmic_geo(skl); } return 0; @@ -527,7 +520,6 @@ static int skl_machine_device_register(struct skl *skl) { struct snd_soc_acpi_mach *mach = skl->mach; struct hdac_bus *bus = skl_to_bus(skl); - struct skl_machine_pdata *pdata; struct platform_device *pdev; int ret; @@ -537,6 +529,16 @@ static int skl_machine_device_register(struct skl *skl) return -EIO; } + mach->mach_params.platform = dev_name(bus->dev); + mach->mach_params.codec_mask = bus->codec_mask; + + ret = platform_device_add_data(pdev, (const void *)mach, sizeof(*mach)); + if (ret) { + dev_err(bus->dev, "failed to add machine device platform data\n"); + platform_device_put(pdev); + return ret; + } + ret = platform_device_add(pdev); if (ret) { dev_err(bus->dev, "failed to add machine device\n"); @@ -544,12 +546,6 @@ static int skl_machine_device_register(struct skl *skl) return -EIO; } - if (mach->pdata) { - pdata = (struct skl_machine_pdata *)mach->pdata; - pdata->platform = dev_name(bus->dev); - pdata->codec_mask = bus->codec_mask; - dev_set_drvdata(&pdev->dev, mach->pdata); - } skl->i2s_dev = pdev; @@ -783,11 +779,9 @@ static int skl_i915_init(struct hdac_bus *bus) if (err < 0) return err; - err = snd_hdac_display_power(bus, true); - if (err < 0) - dev_err(bus->dev, "Cannot turn on display power on i915\n"); + snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, true); - return err; + return 0; } static void skl_probe_work(struct work_struct *work) @@ -823,12 +817,10 @@ static void skl_probe_work(struct work_struct *work) return; } - if (bus->ppcap) { - err = skl_machine_device_register(skl); - if (err < 0) { - dev_err(bus->dev, "machine register failed: %d\n", err); - goto out_err; - } + err = skl_machine_device_register(skl); + if (err < 0) { + dev_err(bus->dev, "machine register failed: %d\n", err); + goto out_err; } /* @@ -837,14 +829,8 @@ static void skl_probe_work(struct work_struct *work) list_for_each_entry(hlink, &bus->hlink_list, list) snd_hdac_ext_bus_link_put(bus, hlink); - if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { - err = snd_hdac_display_power(bus, false); - if (err < 0) { - dev_err(bus->dev, "Cannot turn off display power on i915\n"); - skl_machine_device_unregister(skl); - return; - } - } + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) + snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false); /* configure PM */ pm_runtime_put_noidle(bus->dev); @@ -855,7 +841,7 @@ static void skl_probe_work(struct work_struct *work) out_err: if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) - err = snd_hdac_display_power(bus, false); + snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false); } /* @@ -928,6 +914,12 @@ static int skl_first_init(struct hdac_bus *bus) snd_hdac_bus_parse_capabilities(bus); + /* check if PPCAP exists */ + if (!bus->ppcap) { + dev_err(bus->dev, "bus ppcap not set, HDaudio or DSP not present?\n"); + return -ENODEV; + } + if (skl_acquire_irq(bus, 0) < 0) return -EBUSY; @@ -937,23 +929,25 @@ static int skl_first_init(struct hdac_bus *bus) gcap = snd_hdac_chip_readw(bus, GCAP); dev_dbg(bus->dev, "chipset global capabilities = 0x%x\n", gcap); - /* allow 64bit DMA address if supported by H/W */ - if (!dma_set_mask(bus->dev, DMA_BIT_MASK(64))) { - dma_set_coherent_mask(bus->dev, DMA_BIT_MASK(64)); - } else { - dma_set_mask(bus->dev, DMA_BIT_MASK(32)); - dma_set_coherent_mask(bus->dev, DMA_BIT_MASK(32)); - } - /* read number of streams from GCAP register */ cp_streams = (gcap >> 8) & 0x0f; pb_streams = (gcap >> 12) & 0x0f; - if (!pb_streams && !cp_streams) + if (!pb_streams && !cp_streams) { + dev_err(bus->dev, "no streams found in GCAP definitions?\n"); return -EIO; + } bus->num_streams = cp_streams + pb_streams; + /* allow 64bit DMA address if supported by H/W */ + if (!dma_set_mask(bus->dev, DMA_BIT_MASK(64))) { + dma_set_coherent_mask(bus->dev, DMA_BIT_MASK(64)); + } else { + dma_set_mask(bus->dev, DMA_BIT_MASK(32)); + dma_set_coherent_mask(bus->dev, DMA_BIT_MASK(32)); + } + /* initialize streams */ snd_hdac_ext_stream_init_all (bus, 0, cp_streams, SNDRV_PCM_STREAM_CAPTURE); @@ -978,6 +972,36 @@ static int skl_probe(struct pci_dev *pci, struct hdac_bus *bus = NULL; int err; + switch (skl_pci_binding) { + case SND_SKL_PCI_BIND_AUTO: + /* + * detect DSP by checking class/subclass/prog-id information + * class=04 subclass 03 prog-if 00: no DSP, use legacy driver + * class=04 subclass 01 prog-if 00: DSP is present + * (and may be required e.g. for DMIC or SSP support) + * class=04 subclass 03 prog-if 80: use DSP or legacy mode + */ + if (pci->class == 0x040300) { + dev_info(&pci->dev, "The DSP is not enabled on this platform, aborting probe\n"); + return -ENODEV; + } + if (pci->class != 0x040100 && pci->class != 0x040380) { + dev_err(&pci->dev, "Unknown PCI class/subclass/prog-if information (0x%06x) found, aborting probe\n", pci->class); + return -ENODEV; + } + dev_info(&pci->dev, "DSP detected with PCI class/subclass/prog-if info 0x%06x\n", pci->class); + break; + case SND_SKL_PCI_BIND_LEGACY: + dev_info(&pci->dev, "Module parameter forced binding with HDaudio legacy, aborting probe\n"); + return -ENODEV; + case SND_SKL_PCI_BIND_ASOC: + dev_info(&pci->dev, "Module parameter forced binding with SKL driver, bypassed detection logic\n"); + break; + default: + dev_err(&pci->dev, "invalid value for skl_pci_binding module parameter, ignored\n"); + break; + } + /* we use ext core ops, so provide NULL for ops here */ err = skl_create(pci, NULL, &skl); if (err < 0) @@ -986,8 +1010,10 @@ static int skl_probe(struct pci_dev *pci, bus = skl_to_bus(skl); err = skl_first_init(bus); - if (err < 0) + if (err < 0) { + dev_err(bus->dev, "skl_first_init failed with err: %d\n", err); goto out_free; + } skl->pci_id = pci->device; @@ -996,37 +1022,48 @@ static int skl_probe(struct pci_dev *pci, skl->nhlt = skl_nhlt_init(bus->dev); if (skl->nhlt == NULL) { +#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC) + dev_err(bus->dev, "no nhlt info found\n"); err = -ENODEV; goto out_free; - } - - err = skl_nhlt_create_sysfs(skl); - if (err < 0) - goto out_nhlt_free; +#else + dev_warn(bus->dev, "no nhlt info found, continuing to try to enable HDaudio codec\n"); +#endif + } else { - skl_nhlt_update_topology_bin(skl); + err = skl_nhlt_create_sysfs(skl); + if (err < 0) { + dev_err(bus->dev, "skl_nhlt_create_sysfs failed with err: %d\n", err); + goto out_nhlt_free; + } - pci_set_drvdata(skl->pci, bus); + skl_nhlt_update_topology_bin(skl); - /* check if dsp is there */ - if (bus->ppcap) { /* create device for dsp clk */ err = skl_clock_device_register(skl); - if (err < 0) + if (err < 0) { + dev_err(bus->dev, "skl_clock_device_register failed with err: %d\n", err); goto out_clk_free; + } + } - err = skl_find_machine(skl, (void *)pci_id->driver_data); - if (err < 0) - goto out_nhlt_free; + pci_set_drvdata(skl->pci, bus); - err = skl_init_dsp(skl); - if (err < 0) { - dev_dbg(bus->dev, "error failed to register dsp\n"); - goto out_nhlt_free; - } - skl->skl_sst->enable_miscbdcge = skl_enable_miscbdcge; - skl->skl_sst->clock_power_gating = skl_clock_power_gating; + + err = skl_find_machine(skl, (void *)pci_id->driver_data); + if (err < 0) { + dev_err(bus->dev, "skl_find_machine failed with err: %d\n", err); + goto out_nhlt_free; } + + err = skl_init_dsp(skl); + if (err < 0) { + dev_dbg(bus->dev, "error failed to register dsp\n"); + goto out_nhlt_free; + } + skl->skl_sst->enable_miscbdcge = skl_enable_miscbdcge; + skl->skl_sst->clock_power_gating = skl_clock_power_gating; + if (bus->mlcap) snd_hdac_ext_bus_get_ml_capabilities(bus); @@ -1034,8 +1071,10 @@ static int skl_probe(struct pci_dev *pci, /* create device for soc dmic */ err = skl_dmic_device_register(skl); - if (err < 0) + if (err < 0) { + dev_err(bus->dev, "skl_dmic_device_register failed with err: %d\n", err); goto out_dsp_free; + } schedule_work(&skl->probe_work); @@ -1103,21 +1142,36 @@ static void skl_remove(struct pci_dev *pci) /* PCI IDs */ static const struct pci_device_id skl_ids[] = { +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_SKL) /* Sunrise Point-LP */ { PCI_DEVICE(0x8086, 0x9d70), .driver_data = (unsigned long)&snd_soc_acpi_intel_skl_machines}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_APL) /* BXT-P */ { PCI_DEVICE(0x8086, 0x5a98), .driver_data = (unsigned long)&snd_soc_acpi_intel_bxt_machines}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_KBL) /* KBL */ { PCI_DEVICE(0x8086, 0x9D71), .driver_data = (unsigned long)&snd_soc_acpi_intel_kbl_machines}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_GLK) /* GLK */ { PCI_DEVICE(0x8086, 0x3198), .driver_data = (unsigned long)&snd_soc_acpi_intel_glk_machines}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL) /* CNL */ { PCI_DEVICE(0x8086, 0x9dc8), .driver_data = (unsigned long)&snd_soc_acpi_intel_cnl_machines}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CFL) + /* CFL */ + { PCI_DEVICE(0x8086, 0xa348), + .driver_data = (unsigned long)&snd_soc_acpi_intel_cnl_machines}, +#endif { 0, } }; MODULE_DEVICE_TABLE(pci, skl_ids); diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 8d48cd7c56c8..85f8bb6687dc 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -119,10 +119,7 @@ struct skl_dma_params { }; struct skl_machine_pdata { - u32 dmic_num; bool use_tplg_pcm; /* use dais and dai links from topology */ - const char *platform; - u32 codec_mask; }; struct skl_dsp_ops { diff --git a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c index 192f4d7b37b6..bff7d71d0742 100644 --- a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c +++ b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c @@ -828,7 +828,7 @@ static int mt6797_afe_pcm_dev_probe(struct platform_device *pdev) /* request irq */ irq_id = platform_get_irq(pdev, 0); if (!irq_id) { - dev_err(dev, "%s no irq found\n", dev->of_node->name); + dev_err(dev, "%pOFn no irq found\n", dev->of_node); return -ENXIO; } ret = devm_request_irq(dev, irq_id, mt6797_afe_irq_handler, diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c index c0b6697503fd..166aed28330d 100644 --- a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c +++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c @@ -1092,7 +1092,7 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) irq_id = platform_get_irq(pdev, 0); if (irq_id <= 0) { - dev_err(afe->dev, "np %s no irq\n", afe->dev->of_node->name); + dev_err(afe->dev, "np %pOFn no irq\n", afe->dev->of_node); return irq_id < 0 ? irq_id : -ENXIO; } ret = devm_request_irq(afe->dev, irq_id, mt8173_afe_irq_handler, diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig index 8b8426ed2363..8779fe23671d 100644 --- a/sound/soc/meson/Kconfig +++ b/sound/soc/meson/Kconfig @@ -54,6 +54,7 @@ config SND_MESON_AXG_SOUND_CARD imply SND_MESON_AXG_TDMIN imply SND_MESON_AXG_TDMOUT imply SND_MESON_AXG_SPDIFOUT + imply SND_MESON_AXG_SPDIFIN imply SND_MESON_AXG_PDM help Select Y or M to add support for the AXG SoC sound card @@ -67,6 +68,13 @@ config SND_MESON_AXG_SPDIFOUT Select Y or M to add support for SPDIF output serializer embedded in the Amlogic AXG SoC family +config SND_MESON_AXG_SPDIFIN + tristate "Amlogic AXG SPDIF Input Support" + imply SND_SOC_SPDIF + help + Select Y or M to add support for SPDIF input embedded + in the Amlogic AXG SoC family + config SND_MESON_AXG_PDM tristate "Amlogic AXG PDM Input Support" imply SND_SOC_DMIC @@ -74,5 +82,4 @@ config SND_MESON_AXG_PDM help Select Y or M to add support for PDM input embedded in the Amlogic AXG SoC family - endmenu diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile index 4cd25104029d..b45dfb9e2f88 100644 --- a/sound/soc/meson/Makefile +++ b/sound/soc/meson/Makefile @@ -8,6 +8,7 @@ snd-soc-meson-axg-tdm-interface-objs := axg-tdm-interface.o snd-soc-meson-axg-tdmin-objs := axg-tdmin.o snd-soc-meson-axg-tdmout-objs := axg-tdmout.o snd-soc-meson-axg-sound-card-objs := axg-card.o +snd-soc-meson-axg-spdifin-objs := axg-spdifin.o snd-soc-meson-axg-spdifout-objs := axg-spdifout.o snd-soc-meson-axg-pdm-objs := axg-pdm.o @@ -19,5 +20,6 @@ obj-$(CONFIG_SND_MESON_AXG_TDM_INTERFACE) += snd-soc-meson-axg-tdm-interface.o obj-$(CONFIG_SND_MESON_AXG_TDMIN) += snd-soc-meson-axg-tdmin.o obj-$(CONFIG_SND_MESON_AXG_TDMOUT) += snd-soc-meson-axg-tdmout.o obj-$(CONFIG_SND_MESON_AXG_SOUND_CARD) += snd-soc-meson-axg-sound-card.o +obj-$(CONFIG_SND_MESON_AXG_SPDIFIN) += snd-soc-meson-axg-spdifin.o obj-$(CONFIG_SND_MESON_AXG_SPDIFOUT) += snd-soc-meson-axg-spdifout.o obj-$(CONFIG_SND_MESON_AXG_PDM) += snd-soc-meson-axg-pdm.o diff --git a/sound/soc/meson/axg-fifo.h b/sound/soc/meson/axg-fifo.h index cb6c4013ca33..d9f516cfbeda 100644 --- a/sound/soc/meson/axg-fifo.h +++ b/sound/soc/meson/axg-fifo.h @@ -25,7 +25,8 @@ struct snd_soc_pcm_runtime; SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S20_LE | \ SNDRV_PCM_FMTBIT_S24_LE | \ - SNDRV_PCM_FMTBIT_S32_LE) + SNDRV_PCM_FMTBIT_S32_LE | \ + SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) #define AXG_FIFO_BURST 8 #define AXG_FIFO_MIN_CNT 64 diff --git a/sound/soc/meson/axg-spdifin.c b/sound/soc/meson/axg-spdifin.c new file mode 100644 index 000000000000..01b2035fa841 --- /dev/null +++ b/sound/soc/meson/axg-spdifin.c @@ -0,0 +1,521 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2018 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> +#include <sound/pcm_params.h> + +#define SPDIFIN_CTRL0 0x00 +#define SPDIFIN_CTRL0_EN BIT(31) +#define SPDIFIN_CTRL0_RST_OUT BIT(29) +#define SPDIFIN_CTRL0_RST_IN BIT(28) +#define SPDIFIN_CTRL0_WIDTH_SEL BIT(24) +#define SPDIFIN_CTRL0_STATUS_CH_SHIFT 11 +#define SPDIFIN_CTRL0_STATUS_SEL GENMASK(10, 8) +#define SPDIFIN_CTRL0_SRC_SEL GENMASK(5, 4) +#define SPDIFIN_CTRL0_CHK_VALID BIT(3) +#define SPDIFIN_CTRL1 0x04 +#define SPDIFIN_CTRL1_BASE_TIMER GENMASK(19, 0) +#define SPDIFIN_CTRL1_IRQ_MASK GENMASK(27, 20) +#define SPDIFIN_CTRL2 0x08 +#define SPDIFIN_THRES_PER_REG 3 +#define SPDIFIN_THRES_WIDTH 10 +#define SPDIFIN_CTRL3 0x0c +#define SPDIFIN_CTRL4 0x10 +#define SPDIFIN_TIMER_PER_REG 4 +#define SPDIFIN_TIMER_WIDTH 8 +#define SPDIFIN_CTRL5 0x14 +#define SPDIFIN_CTRL6 0x18 +#define SPDIFIN_STAT0 0x1c +#define SPDIFIN_STAT0_MODE GENMASK(30, 28) +#define SPDIFIN_STAT0_MAXW GENMASK(17, 8) +#define SPDIFIN_STAT0_IRQ GENMASK(7, 0) +#define SPDIFIN_IRQ_MODE_CHANGED BIT(2) +#define SPDIFIN_STAT1 0x20 +#define SPDIFIN_STAT2 0x24 +#define SPDIFIN_MUTE_VAL 0x28 + +#define SPDIFIN_MODE_NUM 7 + +struct axg_spdifin_cfg { + const unsigned int *mode_rates; + unsigned int ref_rate; +}; + +struct axg_spdifin { + const struct axg_spdifin_cfg *conf; + struct regmap *map; + struct clk *refclk; + struct clk *pclk; +}; + +/* + * TODO: + * It would have been nice to check the actual rate against the sample rate + * requested in hw_params(). Unfortunately, I was not able to make the mode + * detection and IRQ work reliably: + * + * 1. IRQs are generated on mode change only, so there is no notification + * on transition between no signal and mode 0 (32kHz). + * 2. Mode detection very often has glitches, and may detects the + * lowest or the highest mode before zeroing in on the actual mode. + * + * This makes calling snd_pcm_stop() difficult to get right. Even notifying + * the kcontrol would be very unreliable at this point. + * Let's keep things simple until the magic spell that makes this work is + * found. + */ + +static unsigned int axg_spdifin_get_rate(struct axg_spdifin *priv) +{ + unsigned int stat, mode, rate = 0; + + regmap_read(priv->map, SPDIFIN_STAT0, &stat); + mode = FIELD_GET(SPDIFIN_STAT0_MODE, stat); + + /* + * If max width is zero, we are not capturing anything. + * Also Sometimes, when the capture is on but there is no data, + * mode is SPDIFIN_MODE_NUM, but not always ... + */ + if (FIELD_GET(SPDIFIN_STAT0_MAXW, stat) && + mode < SPDIFIN_MODE_NUM) + rate = priv->conf->mode_rates[mode]; + + return rate; +} + +static int axg_spdifin_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct axg_spdifin *priv = snd_soc_dai_get_drvdata(dai); + + /* Apply both reset */ + regmap_update_bits(priv->map, SPDIFIN_CTRL0, + SPDIFIN_CTRL0_RST_OUT | + SPDIFIN_CTRL0_RST_IN, + 0); + + /* Clear out reset before in reset */ + regmap_update_bits(priv->map, SPDIFIN_CTRL0, + SPDIFIN_CTRL0_RST_OUT, SPDIFIN_CTRL0_RST_OUT); + regmap_update_bits(priv->map, SPDIFIN_CTRL0, + SPDIFIN_CTRL0_RST_IN, SPDIFIN_CTRL0_RST_IN); + + return 0; +} + +static int axg_spdifin_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct axg_spdifin *priv = snd_soc_dai_get_drvdata(dai); + int ret; + + ret = clk_prepare_enable(priv->refclk); + if (ret) { + dev_err(dai->dev, + "failed to enable spdifin reference clock\n"); + return ret; + } + + regmap_update_bits(priv->map, SPDIFIN_CTRL0, SPDIFIN_CTRL0_EN, + SPDIFIN_CTRL0_EN); + + return 0; +} + +static void axg_spdifin_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct axg_spdifin *priv = snd_soc_dai_get_drvdata(dai); + + regmap_update_bits(priv->map, SPDIFIN_CTRL0, SPDIFIN_CTRL0_EN, 0); + clk_disable_unprepare(priv->refclk); +} + +static void axg_spdifin_write_mode_param(struct regmap *map, int mode, + unsigned int val, + unsigned int num_per_reg, + unsigned int base_reg, + unsigned int width) +{ + uint64_t offset = mode; + unsigned int reg, shift, rem; + + rem = do_div(offset, num_per_reg); + + reg = offset * regmap_get_reg_stride(map) + base_reg; + shift = width * (num_per_reg - 1 - rem); + + regmap_update_bits(map, reg, GENMASK(width - 1, 0) << shift, + val << shift); +} + +static void axg_spdifin_write_timer(struct regmap *map, int mode, + unsigned int val) +{ + axg_spdifin_write_mode_param(map, mode, val, SPDIFIN_TIMER_PER_REG, + SPDIFIN_CTRL4, SPDIFIN_TIMER_WIDTH); +} + +static void axg_spdifin_write_threshold(struct regmap *map, int mode, + unsigned int val) +{ + axg_spdifin_write_mode_param(map, mode, val, SPDIFIN_THRES_PER_REG, + SPDIFIN_CTRL2, SPDIFIN_THRES_WIDTH); +} + +static unsigned int axg_spdifin_mode_timer(struct axg_spdifin *priv, + int mode, + unsigned int rate) +{ + /* + * Number of period of the reference clock during a period of the + * input signal reference clock + */ + return rate / (128 * priv->conf->mode_rates[mode]); +} + +static int axg_spdifin_sample_mode_config(struct snd_soc_dai *dai, + struct axg_spdifin *priv) +{ + unsigned int rate, t_next; + int ret, i = SPDIFIN_MODE_NUM - 1; + + /* Set spdif input reference clock */ + ret = clk_set_rate(priv->refclk, priv->conf->ref_rate); + if (ret) { + dev_err(dai->dev, "reference clock rate set failed\n"); + return ret; + } + + /* + * The rate actually set might be slightly different, get + * the actual rate for the following mode calculation + */ + rate = clk_get_rate(priv->refclk); + + /* HW will update mode every 1ms */ + regmap_update_bits(priv->map, SPDIFIN_CTRL1, + SPDIFIN_CTRL1_BASE_TIMER, + FIELD_PREP(SPDIFIN_CTRL1_BASE_TIMER, rate / 1000)); + + /* Threshold based on the minimum width between two edges */ + regmap_update_bits(priv->map, SPDIFIN_CTRL0, + SPDIFIN_CTRL0_WIDTH_SEL, SPDIFIN_CTRL0_WIDTH_SEL); + + /* Calculate the last timer which has no threshold */ + t_next = axg_spdifin_mode_timer(priv, i, rate); + axg_spdifin_write_timer(priv->map, i, t_next); + + do { + unsigned int t; + + i -= 1; + + /* Calculate the timer */ + t = axg_spdifin_mode_timer(priv, i, rate); + + /* Set the timer value */ + axg_spdifin_write_timer(priv->map, i, t); + + /* Set the threshold value */ + axg_spdifin_write_threshold(priv->map, i, t + t_next); + + /* Save the current timer for the next threshold calculation */ + t_next = t; + + } while (i > 0); + + return 0; +} + +static int axg_spdifin_dai_probe(struct snd_soc_dai *dai) +{ + struct axg_spdifin *priv = snd_soc_dai_get_drvdata(dai); + int ret; + + ret = clk_prepare_enable(priv->pclk); + if (ret) { + dev_err(dai->dev, "failed to enable pclk\n"); + return ret; + } + + ret = axg_spdifin_sample_mode_config(dai, priv); + if (ret) { + dev_err(dai->dev, "mode configuration failed\n"); + clk_disable_unprepare(priv->pclk); + return ret; + } + + return 0; +} + +static int axg_spdifin_dai_remove(struct snd_soc_dai *dai) +{ + struct axg_spdifin *priv = snd_soc_dai_get_drvdata(dai); + + clk_disable_unprepare(priv->pclk); + return 0; +} + +static const struct snd_soc_dai_ops axg_spdifin_ops = { + .prepare = axg_spdifin_prepare, + .startup = axg_spdifin_startup, + .shutdown = axg_spdifin_shutdown, +}; + +static int axg_spdifin_iec958_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + + return 0; +} + +static int axg_spdifin_get_status_mask(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int i; + + for (i = 0; i < 24; i++) + ucontrol->value.iec958.status[i] = 0xff; + + return 0; +} + +static int axg_spdifin_get_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *c = snd_kcontrol_chip(kcontrol); + struct axg_spdifin *priv = snd_soc_component_get_drvdata(c); + int i, j; + + for (i = 0; i < 6; i++) { + unsigned int val; + + regmap_update_bits(priv->map, SPDIFIN_CTRL0, + SPDIFIN_CTRL0_STATUS_SEL, + FIELD_PREP(SPDIFIN_CTRL0_STATUS_SEL, i)); + + regmap_read(priv->map, SPDIFIN_STAT1, &val); + + for (j = 0; j < 4; j++) { + unsigned int offset = i * 4 + j; + + ucontrol->value.iec958.status[offset] = + (val >> (j * 8)) & 0xff; + } + } + + return 0; +} + +#define AXG_SPDIFIN_IEC958_MASK \ + { \ + .access = SNDRV_CTL_ELEM_ACCESS_READ, \ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, \ + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK), \ + .info = axg_spdifin_iec958_info, \ + .get = axg_spdifin_get_status_mask, \ + } + +#define AXG_SPDIFIN_IEC958_STATUS \ + { \ + .access = (SNDRV_CTL_ELEM_ACCESS_READ | \ + SNDRV_CTL_ELEM_ACCESS_VOLATILE), \ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, \ + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE), \ + .info = axg_spdifin_iec958_info, \ + .get = axg_spdifin_get_status, \ + } + +static const char * const spdifin_chsts_src_texts[] = { + "A", "B", +}; + +static SOC_ENUM_SINGLE_DECL(axg_spdifin_chsts_src_enum, SPDIFIN_CTRL0, + SPDIFIN_CTRL0_STATUS_CH_SHIFT, + spdifin_chsts_src_texts); + +static int axg_spdifin_rate_lock_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 192000; + + return 0; +} + +static int axg_spdifin_rate_lock_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *c = snd_kcontrol_chip(kcontrol); + struct axg_spdifin *priv = snd_soc_component_get_drvdata(c); + + ucontrol->value.integer.value[0] = axg_spdifin_get_rate(priv); + + return 0; +} + +#define AXG_SPDIFIN_LOCK_RATE(xname) \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, \ + .access = (SNDRV_CTL_ELEM_ACCESS_READ | \ + SNDRV_CTL_ELEM_ACCESS_VOLATILE), \ + .get = axg_spdifin_rate_lock_get, \ + .info = axg_spdifin_rate_lock_info, \ + .name = xname, \ + } + +static const struct snd_kcontrol_new axg_spdifin_controls[] = { + AXG_SPDIFIN_LOCK_RATE("Capture Rate Lock"), + SOC_DOUBLE("Capture Switch", SPDIFIN_CTRL0, 7, 6, 1, 1), + SOC_ENUM(SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE) "Src", + axg_spdifin_chsts_src_enum), + AXG_SPDIFIN_IEC958_MASK, + AXG_SPDIFIN_IEC958_STATUS, +}; + +static const struct snd_soc_component_driver axg_spdifin_component_drv = { + .controls = axg_spdifin_controls, + .num_controls = ARRAY_SIZE(axg_spdifin_controls), +}; + +static const struct regmap_config axg_spdifin_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = SPDIFIN_MUTE_VAL, +}; + +static const unsigned int axg_spdifin_mode_rates[SPDIFIN_MODE_NUM] = { + 32000, 44100, 48000, 88200, 96000, 176400, 192000, +}; + +static const struct axg_spdifin_cfg axg_cfg = { + .mode_rates = axg_spdifin_mode_rates, + .ref_rate = 333333333, +}; + +static const struct of_device_id axg_spdifin_of_match[] = { + { + .compatible = "amlogic,axg-spdifin", + .data = &axg_cfg, + }, {} +}; +MODULE_DEVICE_TABLE(of, axg_spdifin_of_match); + +static struct snd_soc_dai_driver * +axg_spdifin_get_dai_drv(struct device *dev, struct axg_spdifin *priv) +{ + struct snd_soc_dai_driver *drv; + int i; + + drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return ERR_PTR(-ENOMEM); + + drv->name = "SPDIF Input"; + drv->ops = &axg_spdifin_ops; + drv->probe = axg_spdifin_dai_probe; + drv->remove = axg_spdifin_dai_remove; + drv->capture.stream_name = "Capture"; + drv->capture.channels_min = 1; + drv->capture.channels_max = 2; + drv->capture.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE; + + for (i = 0; i < SPDIFIN_MODE_NUM; i++) { + unsigned int rb = + snd_pcm_rate_to_rate_bit(priv->conf->mode_rates[i]); + + if (rb == SNDRV_PCM_RATE_KNOT) + return ERR_PTR(-EINVAL); + + drv->capture.rates |= rb; + } + + return drv; +} + +static int axg_spdifin_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct axg_spdifin *priv; + struct snd_soc_dai_driver *dai_drv; + struct resource *res; + void __iomem *regs; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + platform_set_drvdata(pdev, priv); + + priv->conf = of_device_get_match_data(dev); + if (!priv->conf) { + dev_err(dev, "failed to match device\n"); + return -ENODEV; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + priv->map = devm_regmap_init_mmio(dev, regs, &axg_spdifin_regmap_cfg); + if (IS_ERR(priv->map)) { + dev_err(dev, "failed to init regmap: %ld\n", + PTR_ERR(priv->map)); + return PTR_ERR(priv->map); + } + + priv->pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(priv->pclk)) { + ret = PTR_ERR(priv->pclk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get pclk: %d\n", ret); + return ret; + } + + priv->refclk = devm_clk_get(dev, "refclk"); + if (IS_ERR(priv->refclk)) { + ret = PTR_ERR(priv->refclk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get mclk: %d\n", ret); + return ret; + } + + dai_drv = axg_spdifin_get_dai_drv(dev, priv); + if (IS_ERR(dai_drv)) { + dev_err(dev, "failed to get dai driver: %ld\n", + PTR_ERR(dai_drv)); + return PTR_ERR(dai_drv); + } + + return devm_snd_soc_register_component(dev, &axg_spdifin_component_drv, + dai_drv, 1); +} + +static struct platform_driver axg_spdifin_pdrv = { + .probe = axg_spdifin_probe, + .driver = { + .name = "axg-spdifin", + .of_match_table = axg_spdifin_of_match, + }, +}; +module_platform_driver(axg_spdifin_pdrv); + +MODULE_DESCRIPTION("Amlogic AXG SPDIF Input driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/meson/axg-toddr.c b/sound/soc/meson/axg-toddr.c index c2c9bb312586..0e9ca3882ae5 100644 --- a/sound/soc/meson/axg-toddr.c +++ b/sound/soc/meson/axg-toddr.c @@ -25,6 +25,8 @@ #define CTRL0_TODDR_LSB_POS_MASK GENMASK(7, 3) #define CTRL0_TODDR_LSB_POS(x) ((x) << 3) +#define TODDR_MSB_POS 31 + static int axg_toddr_pcm_new(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) { @@ -36,14 +38,7 @@ static int axg_toddr_dai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); - unsigned int type, width, msb = 31; - - /* - * NOTE: - * Almost all backend will place the MSB at bit 31, except SPDIF Input - * which will put it at index 28. When adding support for the SPDIF - * Input, we'll need to find which type of backend we are connected to. - */ + unsigned int type, width; switch (params_physical_width(params)) { case 8: @@ -66,8 +61,8 @@ static int axg_toddr_dai_hw_params(struct snd_pcm_substream *substream, CTRL0_TODDR_MSB_POS_MASK | CTRL0_TODDR_LSB_POS_MASK, CTRL0_TODDR_TYPE(type) | - CTRL0_TODDR_MSB_POS(msb) | - CTRL0_TODDR_LSB_POS(msb - (width - 1))); + CTRL0_TODDR_MSB_POS(TODDR_MSB_POS) | + CTRL0_TODDR_LSB_POS(TODDR_MSB_POS - (width - 1))); return 0; } diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig deleted file mode 100644 index 6dccea6fdaeb..000000000000 --- a/sound/soc/omap/Kconfig +++ /dev/null @@ -1,129 +0,0 @@ -config SND_OMAP_SOC - tristate "SoC Audio for Texas Instruments OMAP chips (deprecated)" - depends on (ARCH_OMAP && DMA_OMAP) || (ARM && COMPILE_TEST) - select SND_SDMA_SOC - -config SND_SDMA_SOC - tristate "SoC Audio for Texas Instruments chips using sDMA" - depends on DMA_OMAP || COMPILE_TEST - select SND_SOC_GENERIC_DMAENGINE_PCM - -config SND_OMAP_SOC_DMIC - tristate - -config SND_OMAP_SOC_MCBSP - tristate - -config SND_OMAP_SOC_MCPDM - tristate - -config SND_OMAP_SOC_HDMI_AUDIO - tristate "HDMI audio support for OMAP4+ based SoCs" - depends on SND_SDMA_SOC - help - For HDMI audio to work OMAPDSS HDMI support should be - enabled. - The hdmi audio driver implements cpu-dai component using the - callbacks provided by OMAPDSS and registers the component - under DSS HDMI device. Omap-pcm is registered for platform - component also under DSS HDMI device. Dummy codec is used as - as codec component. The hdmi audio driver implements also - the card and registers it under its own platform device. - The device for the driver is registered by OMAPDSS hdmi - driver. - -config SND_OMAP_SOC_N810 - tristate "SoC Audio support for Nokia N810" - depends on SND_SDMA_SOC && MACH_NOKIA_N810 && I2C - select SND_OMAP_SOC_MCBSP - select SND_SOC_TLV320AIC3X - help - Say Y if you want to add support for SoC audio on Nokia N810. - -config SND_OMAP_SOC_RX51 - tristate "SoC Audio support for Nokia N900 (RX-51)" - depends on SND_SDMA_SOC && ARM && I2C - select SND_OMAP_SOC_MCBSP - select SND_SOC_TLV320AIC3X - select SND_SOC_TPA6130A2 - depends on GPIOLIB - help - Say Y if you want to add support for SoC audio on Nokia N900 - cellphone. - -config SND_OMAP_SOC_AMS_DELTA - tristate "SoC Audio support for Amstrad E3 (Delta) videophone" - depends on SND_SDMA_SOC && MACH_AMS_DELTA && TTY - select SND_OMAP_SOC_MCBSP - select SND_SOC_CX20442 - help - Say Y if you want to add support for SoC audio device connected to - a handset and a speakerphone found on Amstrad E3 (Delta) videophone. - - Note that in order to get those devices fully supported, you have to - build the kernel with standard serial port driver included and - configured for at least 4 ports. Then, from userspace, you must load - a line discipline #19 on the modem (ttyS3) serial line. The simplest - way to achieve this is to install util-linux-ng and use the included - ldattach utility. This can be started automatically from udev, - a simple rule like this one should do the trick (it does for me): - ACTION=="add", KERNEL=="controlC0", \ - RUN+="/usr/sbin/ldattach 19 /dev/ttyS3" - -config SND_OMAP_SOC_OSK5912 - tristate "SoC Audio support for omap osk5912" - depends on SND_SDMA_SOC && MACH_OMAP_OSK && I2C - select SND_OMAP_SOC_MCBSP - select SND_SOC_TLV320AIC23_I2C - help - Say Y if you want to add support for SoC audio on osk5912. - -config SND_OMAP_SOC_AM3517EVM - tristate "SoC Audio support for OMAP3517 / AM3517 EVM" - depends on SND_SDMA_SOC && MACH_OMAP3517EVM && I2C - select SND_OMAP_SOC_MCBSP - select SND_SOC_TLV320AIC23_I2C - help - Say Y if you want to add support for SoC audio on the OMAP3517 / AM3517 - EVM. - -config SND_OMAP_SOC_OMAP_TWL4030 - tristate "SoC Audio support for TI SoC based boards with twl4030 codec" - depends on TWL4030_CORE && SND_SDMA_SOC - select SND_OMAP_SOC_MCBSP - select SND_SOC_TWL4030 - help - Say Y if you want to add support for SoC audio on TI SoC based boards - using twl4030 as c codec. This driver currently supports: - - Beagleboard or Devkit8000 - - Gumstix Overo or CompuLab CM-T35/CM-T3730 - - IGEP v2 - - OMAP3EVM - - SDP3430 - - Zoom2 - -config SND_OMAP_SOC_OMAP_ABE_TWL6040 - tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec" - depends on TWL6040_CORE && SND_SDMA_SOC && COMMON_CLK - depends on ARCH_OMAP4 || (SOC_OMAP5 && MFD_PALMAS) || COMPILE_TEST - select SND_OMAP_SOC_DMIC - select SND_OMAP_SOC_MCPDM - select SND_SOC_TWL6040 - select SND_SOC_DMIC - select COMMON_CLK_PALMAS if (SOC_OMAP5 && MFD_PALMAS) - select CLK_TWL6040 - help - Say Y if you want to add support for SoC audio on OMAP boards using - ABE and twl6040 codec. This driver currently supports: - - SDP4430/Blaze boards - - PandaBoard (4430) - - PandaBoardES (4460) - - omap5-uevm (5432) - -config SND_OMAP_SOC_OMAP3_PANDORA - tristate "SoC Audio support for OMAP3 Pandora" - depends on TWL4030_CORE && SND_SDMA_SOC && MACH_OMAP3_PANDORA - select SND_OMAP_SOC_MCBSP - select SND_SOC_TWL4030 - help - Say Y if you want to add support for SoC audio on the OMAP3 Pandora. diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile deleted file mode 100644 index 53eba3413485..000000000000 --- a/sound/soc/omap/Makefile +++ /dev/null @@ -1,32 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# OMAP Platform Support -snd-soc-sdma-objs := sdma-pcm.o -snd-soc-omap-dmic-objs := omap-dmic.o -snd-soc-omap-mcbsp-objs := omap-mcbsp.o mcbsp.o -snd-soc-omap-mcpdm-objs := omap-mcpdm.o -snd-soc-omap-hdmi-audio-objs := omap-hdmi-audio.o - -obj-$(CONFIG_SND_SDMA_SOC) += snd-soc-sdma.o -obj-$(CONFIG_SND_OMAP_SOC_DMIC) += snd-soc-omap-dmic.o -obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o -obj-$(CONFIG_SND_OMAP_SOC_MCPDM) += snd-soc-omap-mcpdm.o -obj-$(CONFIG_SND_OMAP_SOC_HDMI_AUDIO) += snd-soc-omap-hdmi-audio.o - -# OMAP Machine Support -snd-soc-n810-objs := n810.o -snd-soc-rx51-objs := rx51.o -snd-soc-ams-delta-objs := ams-delta.o -snd-soc-osk5912-objs := osk5912.o -snd-soc-am3517evm-objs := am3517evm.o -snd-soc-omap-abe-twl6040-objs := omap-abe-twl6040.o -snd-soc-omap-twl4030-objs := omap-twl4030.o -snd-soc-omap3pandora-objs := omap3pandora.o - -obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o -obj-$(CONFIG_SND_OMAP_SOC_RX51) += snd-soc-rx51.o -obj-$(CONFIG_SND_OMAP_SOC_AMS_DELTA) += snd-soc-ams-delta.o -obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o -obj-$(CONFIG_SND_OMAP_SOC_AM3517EVM) += snd-soc-am3517evm.o -obj-$(CONFIG_SND_OMAP_SOC_OMAP_ABE_TWL6040) += snd-soc-omap-abe-twl6040.o -obj-$(CONFIG_SND_OMAP_SOC_OMAP_TWL4030) += snd-soc-omap-twl4030.o -obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o diff --git a/sound/soc/omap/am3517evm.c b/sound/soc/omap/am3517evm.c deleted file mode 100644 index d5651026ec10..000000000000 --- a/sound/soc/omap/am3517evm.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * am3517evm.c -- ALSA SoC support for OMAP3517 / AM3517 EVM - * - * Author: Anuj Aggarwal <anuj.aggarwal@ti.com> - * - * Based on sound/soc/omap/beagle.c by Steve Sakoman - * - * Copyright (C) 2009 Texas Instruments Incorporated - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, - * whether express or implied; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ - -#include <linux/clk.h> -#include <linux/platform_device.h> -#include <linux/module.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/soc.h> - -#include <asm/mach-types.h> -#include <linux/platform_data/asoc-ti-mcbsp.h> - -#include "omap-mcbsp.h" - -#include "../codecs/tlv320aic23.h" - -#define CODEC_CLOCK 12000000 - -static int am3517evm_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; - - /* Set the codec system clock for DAC and ADC */ - ret = snd_soc_dai_set_sysclk(codec_dai, 0, - CODEC_CLOCK, SND_SOC_CLOCK_IN); - if (ret < 0) - printk(KERN_ERR "can't set codec system clock\n"); - - return ret; -} - -static const struct snd_soc_ops am3517evm_ops = { - .hw_params = am3517evm_hw_params, -}; - -/* am3517evm machine dapm widgets */ -static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = { - SND_SOC_DAPM_HP("Line Out", NULL), - SND_SOC_DAPM_LINE("Line In", NULL), - SND_SOC_DAPM_MIC("Mic In", NULL), -}; - -static const struct snd_soc_dapm_route audio_map[] = { - /* Line Out connected to LLOUT, RLOUT */ - {"Line Out", NULL, "LOUT"}, - {"Line Out", NULL, "ROUT"}, - - {"LLINEIN", NULL, "Line In"}, - {"RLINEIN", NULL, "Line In"}, - - {"MICIN", NULL, "Mic In"}, -}; - -/* Digital audio interface glue - connects codec <--> CPU */ -static struct snd_soc_dai_link am3517evm_dai = { - .name = "TLV320AIC23", - .stream_name = "AIC23", - .cpu_dai_name = "omap-mcbsp.1", - .codec_dai_name = "tlv320aic23-hifi", - .platform_name = "omap-mcbsp.1", - .codec_name = "tlv320aic23-codec.2-001a", - .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM, - .ops = &am3517evm_ops, -}; - -/* Audio machine driver */ -static struct snd_soc_card snd_soc_am3517evm = { - .name = "am3517evm", - .owner = THIS_MODULE, - .dai_link = &am3517evm_dai, - .num_links = 1, - - .dapm_widgets = tlv320aic23_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets), - .dapm_routes = audio_map, - .num_dapm_routes = ARRAY_SIZE(audio_map), -}; - -static struct platform_device *am3517evm_snd_device; - -static int __init am3517evm_soc_init(void) -{ - int ret; - - if (!machine_is_omap3517evm()) - return -ENODEV; - pr_info("OMAP3517 / AM3517 EVM SoC init\n"); - - am3517evm_snd_device = platform_device_alloc("soc-audio", -1); - if (!am3517evm_snd_device) { - printk(KERN_ERR "Platform device allocation failed\n"); - return -ENOMEM; - } - - platform_set_drvdata(am3517evm_snd_device, &snd_soc_am3517evm); - - ret = platform_device_add(am3517evm_snd_device); - if (ret) - goto err1; - - return 0; - -err1: - printk(KERN_ERR "Unable to add platform device\n"); - platform_device_put(am3517evm_snd_device); - - return ret; -} - -static void __exit am3517evm_soc_exit(void) -{ - platform_device_unregister(am3517evm_snd_device); -} - -module_init(am3517evm_soc_init); -module_exit(am3517evm_soc_exit); - -MODULE_AUTHOR("Anuj Aggarwal <anuj.aggarwal@ti.com>"); -MODULE_DESCRIPTION("ALSA SoC OMAP3517 / AM3517 EVM"); -MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c deleted file mode 100644 index 79d4dc785e5c..000000000000 --- a/sound/soc/omap/mcbsp.c +++ /dev/null @@ -1,1104 +0,0 @@ -/* - * sound/soc/omap/mcbsp.c - * - * Copyright (C) 2004 Nokia Corporation - * Author: Samuel Ortiz <samuel.ortiz@nokia.com> - * - * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com> - * Peter Ujfalusi <peter.ujfalusi@ti.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. - * - * Multichannel mode not supported. - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/device.h> -#include <linux/platform_device.h> -#include <linux/interrupt.h> -#include <linux/err.h> -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/io.h> -#include <linux/slab.h> -#include <linux/pm_runtime.h> - -#include <linux/platform_data/asoc-ti-mcbsp.h> - -#include "mcbsp.h" - -static void omap_mcbsp_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val) -{ - void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step; - - if (mcbsp->pdata->reg_size == 2) { - ((u16 *)mcbsp->reg_cache)[reg] = (u16)val; - writew_relaxed((u16)val, addr); - } else { - ((u32 *)mcbsp->reg_cache)[reg] = val; - writel_relaxed(val, addr); - } -} - -static int omap_mcbsp_read(struct omap_mcbsp *mcbsp, u16 reg, bool from_cache) -{ - void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step; - - if (mcbsp->pdata->reg_size == 2) { - return !from_cache ? readw_relaxed(addr) : - ((u16 *)mcbsp->reg_cache)[reg]; - } else { - return !from_cache ? readl_relaxed(addr) : - ((u32 *)mcbsp->reg_cache)[reg]; - } -} - -static void omap_mcbsp_st_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val) -{ - writel_relaxed(val, mcbsp->st_data->io_base_st + reg); -} - -static int omap_mcbsp_st_read(struct omap_mcbsp *mcbsp, u16 reg) -{ - return readl_relaxed(mcbsp->st_data->io_base_st + reg); -} - -#define MCBSP_READ(mcbsp, reg) \ - omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 0) -#define MCBSP_WRITE(mcbsp, reg, val) \ - omap_mcbsp_write(mcbsp, OMAP_MCBSP_REG_##reg, val) -#define MCBSP_READ_CACHE(mcbsp, reg) \ - omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 1) - -#define MCBSP_ST_READ(mcbsp, reg) \ - omap_mcbsp_st_read(mcbsp, OMAP_ST_REG_##reg) -#define MCBSP_ST_WRITE(mcbsp, reg, val) \ - omap_mcbsp_st_write(mcbsp, OMAP_ST_REG_##reg, val) - -static void omap_mcbsp_dump_reg(struct omap_mcbsp *mcbsp) -{ - dev_dbg(mcbsp->dev, "**** McBSP%d regs ****\n", mcbsp->id); - dev_dbg(mcbsp->dev, "DRR2: 0x%04x\n", - MCBSP_READ(mcbsp, DRR2)); - dev_dbg(mcbsp->dev, "DRR1: 0x%04x\n", - MCBSP_READ(mcbsp, DRR1)); - dev_dbg(mcbsp->dev, "DXR2: 0x%04x\n", - MCBSP_READ(mcbsp, DXR2)); - dev_dbg(mcbsp->dev, "DXR1: 0x%04x\n", - MCBSP_READ(mcbsp, DXR1)); - dev_dbg(mcbsp->dev, "SPCR2: 0x%04x\n", - MCBSP_READ(mcbsp, SPCR2)); - dev_dbg(mcbsp->dev, "SPCR1: 0x%04x\n", - MCBSP_READ(mcbsp, SPCR1)); - dev_dbg(mcbsp->dev, "RCR2: 0x%04x\n", - MCBSP_READ(mcbsp, RCR2)); - dev_dbg(mcbsp->dev, "RCR1: 0x%04x\n", - MCBSP_READ(mcbsp, RCR1)); - dev_dbg(mcbsp->dev, "XCR2: 0x%04x\n", - MCBSP_READ(mcbsp, XCR2)); - dev_dbg(mcbsp->dev, "XCR1: 0x%04x\n", - MCBSP_READ(mcbsp, XCR1)); - dev_dbg(mcbsp->dev, "SRGR2: 0x%04x\n", - MCBSP_READ(mcbsp, SRGR2)); - dev_dbg(mcbsp->dev, "SRGR1: 0x%04x\n", - MCBSP_READ(mcbsp, SRGR1)); - dev_dbg(mcbsp->dev, "PCR0: 0x%04x\n", - MCBSP_READ(mcbsp, PCR0)); - dev_dbg(mcbsp->dev, "***********************\n"); -} - -static irqreturn_t omap_mcbsp_irq_handler(int irq, void *dev_id) -{ - struct omap_mcbsp *mcbsp = dev_id; - u16 irqst; - - irqst = MCBSP_READ(mcbsp, IRQST); - dev_dbg(mcbsp->dev, "IRQ callback : 0x%x\n", irqst); - - if (irqst & RSYNCERREN) - dev_err(mcbsp->dev, "RX Frame Sync Error!\n"); - if (irqst & RFSREN) - dev_dbg(mcbsp->dev, "RX Frame Sync\n"); - if (irqst & REOFEN) - dev_dbg(mcbsp->dev, "RX End Of Frame\n"); - if (irqst & RRDYEN) - dev_dbg(mcbsp->dev, "RX Buffer Threshold Reached\n"); - if (irqst & RUNDFLEN) - dev_err(mcbsp->dev, "RX Buffer Underflow!\n"); - if (irqst & ROVFLEN) - dev_err(mcbsp->dev, "RX Buffer Overflow!\n"); - - if (irqst & XSYNCERREN) - dev_err(mcbsp->dev, "TX Frame Sync Error!\n"); - if (irqst & XFSXEN) - dev_dbg(mcbsp->dev, "TX Frame Sync\n"); - if (irqst & XEOFEN) - dev_dbg(mcbsp->dev, "TX End Of Frame\n"); - if (irqst & XRDYEN) - dev_dbg(mcbsp->dev, "TX Buffer threshold Reached\n"); - if (irqst & XUNDFLEN) - dev_err(mcbsp->dev, "TX Buffer Underflow!\n"); - if (irqst & XOVFLEN) - dev_err(mcbsp->dev, "TX Buffer Overflow!\n"); - if (irqst & XEMPTYEOFEN) - dev_dbg(mcbsp->dev, "TX Buffer empty at end of frame\n"); - - MCBSP_WRITE(mcbsp, IRQST, irqst); - - return IRQ_HANDLED; -} - -static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id) -{ - struct omap_mcbsp *mcbsp_tx = dev_id; - u16 irqst_spcr2; - - irqst_spcr2 = MCBSP_READ(mcbsp_tx, SPCR2); - dev_dbg(mcbsp_tx->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2); - - if (irqst_spcr2 & XSYNC_ERR) { - dev_err(mcbsp_tx->dev, "TX Frame Sync Error! : 0x%x\n", - irqst_spcr2); - /* Writing zero to XSYNC_ERR clears the IRQ */ - MCBSP_WRITE(mcbsp_tx, SPCR2, MCBSP_READ_CACHE(mcbsp_tx, SPCR2)); - } - - return IRQ_HANDLED; -} - -static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *dev_id) -{ - struct omap_mcbsp *mcbsp_rx = dev_id; - u16 irqst_spcr1; - - irqst_spcr1 = MCBSP_READ(mcbsp_rx, SPCR1); - dev_dbg(mcbsp_rx->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1); - - if (irqst_spcr1 & RSYNC_ERR) { - dev_err(mcbsp_rx->dev, "RX Frame Sync Error! : 0x%x\n", - irqst_spcr1); - /* Writing zero to RSYNC_ERR clears the IRQ */ - MCBSP_WRITE(mcbsp_rx, SPCR1, MCBSP_READ_CACHE(mcbsp_rx, SPCR1)); - } - - return IRQ_HANDLED; -} - -/* - * omap_mcbsp_config simply write a config to the - * appropriate McBSP. - * You either call this function or set the McBSP registers - * by yourself before calling omap_mcbsp_start(). - */ -void omap_mcbsp_config(struct omap_mcbsp *mcbsp, - const struct omap_mcbsp_reg_cfg *config) -{ - dev_dbg(mcbsp->dev, "Configuring McBSP%d phys_base: 0x%08lx\n", - mcbsp->id, mcbsp->phys_base); - - /* We write the given config */ - MCBSP_WRITE(mcbsp, SPCR2, config->spcr2); - MCBSP_WRITE(mcbsp, SPCR1, config->spcr1); - MCBSP_WRITE(mcbsp, RCR2, config->rcr2); - MCBSP_WRITE(mcbsp, RCR1, config->rcr1); - MCBSP_WRITE(mcbsp, XCR2, config->xcr2); - MCBSP_WRITE(mcbsp, XCR1, config->xcr1); - MCBSP_WRITE(mcbsp, SRGR2, config->srgr2); - MCBSP_WRITE(mcbsp, SRGR1, config->srgr1); - MCBSP_WRITE(mcbsp, MCR2, config->mcr2); - MCBSP_WRITE(mcbsp, MCR1, config->mcr1); - MCBSP_WRITE(mcbsp, PCR0, config->pcr0); - if (mcbsp->pdata->has_ccr) { - MCBSP_WRITE(mcbsp, XCCR, config->xccr); - MCBSP_WRITE(mcbsp, RCCR, config->rccr); - } - /* Enable wakeup behavior */ - if (mcbsp->pdata->has_wakeup) - MCBSP_WRITE(mcbsp, WAKEUPEN, XRDYEN | RRDYEN); - - /* Enable TX/RX sync error interrupts by default */ - if (mcbsp->irq) - MCBSP_WRITE(mcbsp, IRQEN, RSYNCERREN | XSYNCERREN | - RUNDFLEN | ROVFLEN | XUNDFLEN | XOVFLEN); -} - -/** - * omap_mcbsp_dma_reg_params - returns the address of mcbsp data register - * @id - mcbsp id - * @stream - indicates the direction of data flow (rx or tx) - * - * Returns the address of mcbsp data transmit register or data receive register - * to be used by DMA for transferring/receiving data based on the value of - * @stream for the requested mcbsp given by @id - */ -static int omap_mcbsp_dma_reg_params(struct omap_mcbsp *mcbsp, - unsigned int stream) -{ - int data_reg; - - if (mcbsp->pdata->reg_size == 2) { - if (stream) - data_reg = OMAP_MCBSP_REG_DRR1; - else - data_reg = OMAP_MCBSP_REG_DXR1; - } else { - if (stream) - data_reg = OMAP_MCBSP_REG_DRR; - else - data_reg = OMAP_MCBSP_REG_DXR; - } - - return mcbsp->phys_dma_base + data_reg * mcbsp->pdata->reg_step; -} - -static void omap_st_on(struct omap_mcbsp *mcbsp) -{ - unsigned int w; - - if (mcbsp->pdata->force_ick_on) - mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, true); - - /* Disable Sidetone clock auto-gating for normal operation */ - w = MCBSP_ST_READ(mcbsp, SYSCONFIG); - MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w & ~(ST_AUTOIDLE)); - - /* Enable McBSP Sidetone */ - w = MCBSP_READ(mcbsp, SSELCR); - MCBSP_WRITE(mcbsp, SSELCR, w | SIDETONEEN); - - /* Enable Sidetone from Sidetone Core */ - w = MCBSP_ST_READ(mcbsp, SSELCR); - MCBSP_ST_WRITE(mcbsp, SSELCR, w | ST_SIDETONEEN); -} - -static void omap_st_off(struct omap_mcbsp *mcbsp) -{ - unsigned int w; - - w = MCBSP_ST_READ(mcbsp, SSELCR); - MCBSP_ST_WRITE(mcbsp, SSELCR, w & ~(ST_SIDETONEEN)); - - w = MCBSP_READ(mcbsp, SSELCR); - MCBSP_WRITE(mcbsp, SSELCR, w & ~(SIDETONEEN)); - - /* Enable Sidetone clock auto-gating to reduce power consumption */ - w = MCBSP_ST_READ(mcbsp, SYSCONFIG); - MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w | ST_AUTOIDLE); - - if (mcbsp->pdata->force_ick_on) - mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, false); -} - -static void omap_st_fir_write(struct omap_mcbsp *mcbsp, s16 *fir) -{ - u16 val, i; - - val = MCBSP_ST_READ(mcbsp, SSELCR); - - if (val & ST_COEFFWREN) - MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN)); - - MCBSP_ST_WRITE(mcbsp, SSELCR, val | ST_COEFFWREN); - - for (i = 0; i < 128; i++) - MCBSP_ST_WRITE(mcbsp, SFIRCR, fir[i]); - - i = 0; - - val = MCBSP_ST_READ(mcbsp, SSELCR); - while (!(val & ST_COEFFWRDONE) && (++i < 1000)) - val = MCBSP_ST_READ(mcbsp, SSELCR); - - MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN)); - - if (i == 1000) - dev_err(mcbsp->dev, "McBSP FIR load error!\n"); -} - -static void omap_st_chgain(struct omap_mcbsp *mcbsp) -{ - u16 w; - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - - w = MCBSP_ST_READ(mcbsp, SSELCR); - - MCBSP_ST_WRITE(mcbsp, SGAINCR, ST_CH0GAIN(st_data->ch0gain) | \ - ST_CH1GAIN(st_data->ch1gain)); -} - -int omap_st_set_chgain(struct omap_mcbsp *mcbsp, int channel, s16 chgain) -{ - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - int ret = 0; - - if (!st_data) - return -ENOENT; - - spin_lock_irq(&mcbsp->lock); - if (channel == 0) - st_data->ch0gain = chgain; - else if (channel == 1) - st_data->ch1gain = chgain; - else - ret = -EINVAL; - - if (st_data->enabled) - omap_st_chgain(mcbsp); - spin_unlock_irq(&mcbsp->lock); - - return ret; -} - -int omap_st_get_chgain(struct omap_mcbsp *mcbsp, int channel, s16 *chgain) -{ - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - int ret = 0; - - if (!st_data) - return -ENOENT; - - spin_lock_irq(&mcbsp->lock); - if (channel == 0) - *chgain = st_data->ch0gain; - else if (channel == 1) - *chgain = st_data->ch1gain; - else - ret = -EINVAL; - spin_unlock_irq(&mcbsp->lock); - - return ret; -} - -static int omap_st_start(struct omap_mcbsp *mcbsp) -{ - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - - if (st_data->enabled && !st_data->running) { - omap_st_fir_write(mcbsp, st_data->taps); - omap_st_chgain(mcbsp); - - if (!mcbsp->free) { - omap_st_on(mcbsp); - st_data->running = 1; - } - } - - return 0; -} - -int omap_st_enable(struct omap_mcbsp *mcbsp) -{ - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - - if (!st_data) - return -ENODEV; - - spin_lock_irq(&mcbsp->lock); - st_data->enabled = 1; - omap_st_start(mcbsp); - spin_unlock_irq(&mcbsp->lock); - - return 0; -} - -static int omap_st_stop(struct omap_mcbsp *mcbsp) -{ - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - - if (st_data->running) { - if (!mcbsp->free) { - omap_st_off(mcbsp); - st_data->running = 0; - } - } - - return 0; -} - -int omap_st_disable(struct omap_mcbsp *mcbsp) -{ - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - int ret = 0; - - if (!st_data) - return -ENODEV; - - spin_lock_irq(&mcbsp->lock); - omap_st_stop(mcbsp); - st_data->enabled = 0; - spin_unlock_irq(&mcbsp->lock); - - return ret; -} - -int omap_st_is_enabled(struct omap_mcbsp *mcbsp) -{ - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - - if (!st_data) - return -ENODEV; - - return st_data->enabled; -} - -/* - * omap_mcbsp_set_rx_threshold configures the transmit threshold in words. - * The threshold parameter is 1 based, and it is converted (threshold - 1) - * for the THRSH2 register. - */ -void omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) -{ - if (mcbsp->pdata->buffer_size == 0) - return; - - if (threshold && threshold <= mcbsp->max_tx_thres) - MCBSP_WRITE(mcbsp, THRSH2, threshold - 1); -} - -/* - * omap_mcbsp_set_rx_threshold configures the receive threshold in words. - * The threshold parameter is 1 based, and it is converted (threshold - 1) - * for the THRSH1 register. - */ -void omap_mcbsp_set_rx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) -{ - if (mcbsp->pdata->buffer_size == 0) - return; - - if (threshold && threshold <= mcbsp->max_rx_thres) - MCBSP_WRITE(mcbsp, THRSH1, threshold - 1); -} - -/* - * omap_mcbsp_get_tx_delay returns the number of used slots in the McBSP FIFO - */ -u16 omap_mcbsp_get_tx_delay(struct omap_mcbsp *mcbsp) -{ - u16 buffstat; - - if (mcbsp->pdata->buffer_size == 0) - return 0; - - /* Returns the number of free locations in the buffer */ - buffstat = MCBSP_READ(mcbsp, XBUFFSTAT); - - /* Number of slots are different in McBSP ports */ - return mcbsp->pdata->buffer_size - buffstat; -} - -/* - * omap_mcbsp_get_rx_delay returns the number of free slots in the McBSP FIFO - * to reach the threshold value (when the DMA will be triggered to read it) - */ -u16 omap_mcbsp_get_rx_delay(struct omap_mcbsp *mcbsp) -{ - u16 buffstat, threshold; - - if (mcbsp->pdata->buffer_size == 0) - return 0; - - /* Returns the number of used locations in the buffer */ - buffstat = MCBSP_READ(mcbsp, RBUFFSTAT); - /* RX threshold */ - threshold = MCBSP_READ(mcbsp, THRSH1); - - /* Return the number of location till we reach the threshold limit */ - if (threshold <= buffstat) - return 0; - else - return threshold - buffstat; -} - -int omap_mcbsp_request(struct omap_mcbsp *mcbsp) -{ - void *reg_cache; - int err; - - reg_cache = kzalloc(mcbsp->reg_cache_size, GFP_KERNEL); - if (!reg_cache) { - return -ENOMEM; - } - - spin_lock(&mcbsp->lock); - if (!mcbsp->free) { - dev_err(mcbsp->dev, "McBSP%d is currently in use\n", - mcbsp->id); - err = -EBUSY; - goto err_kfree; - } - - mcbsp->free = false; - mcbsp->reg_cache = reg_cache; - spin_unlock(&mcbsp->lock); - - if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->request) - mcbsp->pdata->ops->request(mcbsp->id - 1); - - /* - * Make sure that transmitter, receiver and sample-rate generator are - * not running before activating IRQs. - */ - MCBSP_WRITE(mcbsp, SPCR1, 0); - MCBSP_WRITE(mcbsp, SPCR2, 0); - - if (mcbsp->irq) { - err = request_irq(mcbsp->irq, omap_mcbsp_irq_handler, 0, - "McBSP", (void *)mcbsp); - if (err != 0) { - dev_err(mcbsp->dev, "Unable to request IRQ\n"); - goto err_clk_disable; - } - } else { - err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler, 0, - "McBSP TX", (void *)mcbsp); - if (err != 0) { - dev_err(mcbsp->dev, "Unable to request TX IRQ\n"); - goto err_clk_disable; - } - - err = request_irq(mcbsp->rx_irq, omap_mcbsp_rx_irq_handler, 0, - "McBSP RX", (void *)mcbsp); - if (err != 0) { - dev_err(mcbsp->dev, "Unable to request RX IRQ\n"); - goto err_free_irq; - } - } - - return 0; -err_free_irq: - free_irq(mcbsp->tx_irq, (void *)mcbsp); -err_clk_disable: - if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free) - mcbsp->pdata->ops->free(mcbsp->id - 1); - - /* Disable wakeup behavior */ - if (mcbsp->pdata->has_wakeup) - MCBSP_WRITE(mcbsp, WAKEUPEN, 0); - - spin_lock(&mcbsp->lock); - mcbsp->free = true; - mcbsp->reg_cache = NULL; -err_kfree: - spin_unlock(&mcbsp->lock); - kfree(reg_cache); - - return err; -} - -void omap_mcbsp_free(struct omap_mcbsp *mcbsp) -{ - void *reg_cache; - - if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free) - mcbsp->pdata->ops->free(mcbsp->id - 1); - - /* Disable wakeup behavior */ - if (mcbsp->pdata->has_wakeup) - MCBSP_WRITE(mcbsp, WAKEUPEN, 0); - - /* Disable interrupt requests */ - if (mcbsp->irq) - MCBSP_WRITE(mcbsp, IRQEN, 0); - - if (mcbsp->irq) { - free_irq(mcbsp->irq, (void *)mcbsp); - } else { - free_irq(mcbsp->rx_irq, (void *)mcbsp); - free_irq(mcbsp->tx_irq, (void *)mcbsp); - } - - reg_cache = mcbsp->reg_cache; - - /* - * Select CLKS source from internal source unconditionally before - * marking the McBSP port as free. - * If the external clock source via MCBSP_CLKS pin has been selected the - * system will refuse to enter idle if the CLKS pin source is not reset - * back to internal source. - */ - if (!mcbsp_omap1()) - omap2_mcbsp_set_clks_src(mcbsp, MCBSP_CLKS_PRCM_SRC); - - spin_lock(&mcbsp->lock); - if (mcbsp->free) - dev_err(mcbsp->dev, "McBSP%d was not reserved\n", mcbsp->id); - else - mcbsp->free = true; - mcbsp->reg_cache = NULL; - spin_unlock(&mcbsp->lock); - - kfree(reg_cache); -} - -/* - * Here we start the McBSP, by enabling transmitter, receiver or both. - * If no transmitter or receiver is active prior calling, then sample-rate - * generator and frame sync are started. - */ -void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int tx, int rx) -{ - int enable_srg = 0; - u16 w; - - if (mcbsp->st_data) - omap_st_start(mcbsp); - - /* Only enable SRG, if McBSP is master */ - w = MCBSP_READ_CACHE(mcbsp, PCR0); - if (w & (FSXM | FSRM | CLKXM | CLKRM)) - enable_srg = !((MCBSP_READ_CACHE(mcbsp, SPCR2) | - MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1); - - if (enable_srg) { - /* Start the sample generator */ - w = MCBSP_READ_CACHE(mcbsp, SPCR2); - MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 6)); - } - - /* Enable transmitter and receiver */ - tx &= 1; - w = MCBSP_READ_CACHE(mcbsp, SPCR2); - MCBSP_WRITE(mcbsp, SPCR2, w | tx); - - rx &= 1; - w = MCBSP_READ_CACHE(mcbsp, SPCR1); - MCBSP_WRITE(mcbsp, SPCR1, w | rx); - - /* - * Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec - * REVISIT: 100us may give enough time for two CLKSRG, however - * due to some unknown PM related, clock gating etc. reason it - * is now at 500us. - */ - udelay(500); - - if (enable_srg) { - /* Start frame sync */ - w = MCBSP_READ_CACHE(mcbsp, SPCR2); - MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 7)); - } - - if (mcbsp->pdata->has_ccr) { - /* Release the transmitter and receiver */ - w = MCBSP_READ_CACHE(mcbsp, XCCR); - w &= ~(tx ? XDISABLE : 0); - MCBSP_WRITE(mcbsp, XCCR, w); - w = MCBSP_READ_CACHE(mcbsp, RCCR); - w &= ~(rx ? RDISABLE : 0); - MCBSP_WRITE(mcbsp, RCCR, w); - } - - /* Dump McBSP Regs */ - omap_mcbsp_dump_reg(mcbsp); -} - -void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int tx, int rx) -{ - int idle; - u16 w; - - /* Reset transmitter */ - tx &= 1; - if (mcbsp->pdata->has_ccr) { - w = MCBSP_READ_CACHE(mcbsp, XCCR); - w |= (tx ? XDISABLE : 0); - MCBSP_WRITE(mcbsp, XCCR, w); - } - w = MCBSP_READ_CACHE(mcbsp, SPCR2); - MCBSP_WRITE(mcbsp, SPCR2, w & ~tx); - - /* Reset receiver */ - rx &= 1; - if (mcbsp->pdata->has_ccr) { - w = MCBSP_READ_CACHE(mcbsp, RCCR); - w |= (rx ? RDISABLE : 0); - MCBSP_WRITE(mcbsp, RCCR, w); - } - w = MCBSP_READ_CACHE(mcbsp, SPCR1); - MCBSP_WRITE(mcbsp, SPCR1, w & ~rx); - - idle = !((MCBSP_READ_CACHE(mcbsp, SPCR2) | - MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1); - - if (idle) { - /* Reset the sample rate generator */ - w = MCBSP_READ_CACHE(mcbsp, SPCR2); - MCBSP_WRITE(mcbsp, SPCR2, w & ~(1 << 6)); - } - - if (mcbsp->st_data) - omap_st_stop(mcbsp); -} - -int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id) -{ - struct clk *fck_src; - const char *src; - int r; - - if (fck_src_id == MCBSP_CLKS_PAD_SRC) - src = "pad_fck"; - else if (fck_src_id == MCBSP_CLKS_PRCM_SRC) - src = "prcm_fck"; - else - return -EINVAL; - - fck_src = clk_get(mcbsp->dev, src); - if (IS_ERR(fck_src)) { - dev_err(mcbsp->dev, "CLKS: could not clk_get() %s\n", src); - return -EINVAL; - } - - pm_runtime_put_sync(mcbsp->dev); - - r = clk_set_parent(mcbsp->fclk, fck_src); - if (r) { - dev_err(mcbsp->dev, "CLKS: could not clk_set_parent() to %s\n", - src); - clk_put(fck_src); - return r; - } - - pm_runtime_get_sync(mcbsp->dev); - - clk_put(fck_src); - - return 0; - -} - -#define max_thres(m) (mcbsp->pdata->buffer_size) -#define valid_threshold(m, val) ((val) <= max_thres(m)) -#define THRESHOLD_PROP_BUILDER(prop) \ -static ssize_t prop##_show(struct device *dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \ - \ - return sprintf(buf, "%u\n", mcbsp->prop); \ -} \ - \ -static ssize_t prop##_store(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t size) \ -{ \ - struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \ - unsigned long val; \ - int status; \ - \ - status = kstrtoul(buf, 0, &val); \ - if (status) \ - return status; \ - \ - if (!valid_threshold(mcbsp, val)) \ - return -EDOM; \ - \ - mcbsp->prop = val; \ - return size; \ -} \ - \ -static DEVICE_ATTR(prop, 0644, prop##_show, prop##_store); - -THRESHOLD_PROP_BUILDER(max_tx_thres); -THRESHOLD_PROP_BUILDER(max_rx_thres); - -static const char *dma_op_modes[] = { - "element", "threshold", -}; - -static ssize_t dma_op_mode_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); - int dma_op_mode, i = 0; - ssize_t len = 0; - const char * const *s; - - dma_op_mode = mcbsp->dma_op_mode; - - for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) { - if (dma_op_mode == i) - len += sprintf(buf + len, "[%s] ", *s); - else - len += sprintf(buf + len, "%s ", *s); - } - len += sprintf(buf + len, "\n"); - - return len; -} - -static ssize_t dma_op_mode_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); - int i; - - i = sysfs_match_string(dma_op_modes, buf); - if (i < 0) - return i; - - spin_lock_irq(&mcbsp->lock); - if (!mcbsp->free) { - size = -EBUSY; - goto unlock; - } - mcbsp->dma_op_mode = i; - -unlock: - spin_unlock_irq(&mcbsp->lock); - - return size; -} - -static DEVICE_ATTR_RW(dma_op_mode); - -static const struct attribute *additional_attrs[] = { - &dev_attr_max_tx_thres.attr, - &dev_attr_max_rx_thres.attr, - &dev_attr_dma_op_mode.attr, - NULL, -}; - -static const struct attribute_group additional_attr_group = { - .attrs = (struct attribute **)additional_attrs, -}; - -static ssize_t st_taps_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - ssize_t status = 0; - int i; - - spin_lock_irq(&mcbsp->lock); - for (i = 0; i < st_data->nr_taps; i++) - status += sprintf(&buf[status], (i ? ", %d" : "%d"), - st_data->taps[i]); - if (i) - status += sprintf(&buf[status], "\n"); - spin_unlock_irq(&mcbsp->lock); - - return status; -} - -static ssize_t st_taps_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - int val, tmp, status, i = 0; - - spin_lock_irq(&mcbsp->lock); - memset(st_data->taps, 0, sizeof(st_data->taps)); - st_data->nr_taps = 0; - - do { - status = sscanf(buf, "%d%n", &val, &tmp); - if (status < 0 || status == 0) { - size = -EINVAL; - goto out; - } - if (val < -32768 || val > 32767) { - size = -EINVAL; - goto out; - } - st_data->taps[i++] = val; - buf += tmp; - if (*buf != ',') - break; - buf++; - } while (1); - - st_data->nr_taps = i; - -out: - spin_unlock_irq(&mcbsp->lock); - - return size; -} - -static DEVICE_ATTR_RW(st_taps); - -static const struct attribute *sidetone_attrs[] = { - &dev_attr_st_taps.attr, - NULL, -}; - -static const struct attribute_group sidetone_attr_group = { - .attrs = (struct attribute **)sidetone_attrs, -}; - -static int omap_st_add(struct omap_mcbsp *mcbsp, struct resource *res) -{ - struct omap_mcbsp_st_data *st_data; - int err; - - st_data = devm_kzalloc(mcbsp->dev, sizeof(*mcbsp->st_data), GFP_KERNEL); - if (!st_data) - return -ENOMEM; - - st_data->mcbsp_iclk = clk_get(mcbsp->dev, "ick"); - if (IS_ERR(st_data->mcbsp_iclk)) { - dev_warn(mcbsp->dev, - "Failed to get ick, sidetone might be broken\n"); - st_data->mcbsp_iclk = NULL; - } - - st_data->io_base_st = devm_ioremap(mcbsp->dev, res->start, - resource_size(res)); - if (!st_data->io_base_st) - return -ENOMEM; - - err = sysfs_create_group(&mcbsp->dev->kobj, &sidetone_attr_group); - if (err) - return err; - - mcbsp->st_data = st_data; - return 0; -} - -/* - * McBSP1 and McBSP3 are directly mapped on 1610 and 1510. - * 730 has only 2 McBSP, and both of them are MPU peripherals. - */ -int omap_mcbsp_init(struct platform_device *pdev) -{ - struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); - struct resource *res; - int ret = 0; - - spin_lock_init(&mcbsp->lock); - mcbsp->free = true; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); - if (!res) - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - mcbsp->io_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(mcbsp->io_base)) - return PTR_ERR(mcbsp->io_base); - - mcbsp->phys_base = res->start; - mcbsp->reg_cache_size = resource_size(res); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); - if (!res) - mcbsp->phys_dma_base = mcbsp->phys_base; - else - mcbsp->phys_dma_base = res->start; - - /* - * OMAP1, 2 uses two interrupt lines: TX, RX - * OMAP2430, OMAP3 SoC have combined IRQ line as well. - * OMAP4 and newer SoC only have the combined IRQ line. - * Use the combined IRQ if available since it gives better debugging - * possibilities. - */ - mcbsp->irq = platform_get_irq_byname(pdev, "common"); - if (mcbsp->irq == -ENXIO) { - mcbsp->tx_irq = platform_get_irq_byname(pdev, "tx"); - - if (mcbsp->tx_irq == -ENXIO) { - mcbsp->irq = platform_get_irq(pdev, 0); - mcbsp->tx_irq = 0; - } else { - mcbsp->rx_irq = platform_get_irq_byname(pdev, "rx"); - mcbsp->irq = 0; - } - } - - if (!pdev->dev.of_node) { - res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); - if (!res) { - dev_err(&pdev->dev, "invalid tx DMA channel\n"); - return -ENODEV; - } - mcbsp->dma_req[0] = res->start; - mcbsp->dma_data[0].filter_data = &mcbsp->dma_req[0]; - - res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); - if (!res) { - dev_err(&pdev->dev, "invalid rx DMA channel\n"); - return -ENODEV; - } - mcbsp->dma_req[1] = res->start; - mcbsp->dma_data[1].filter_data = &mcbsp->dma_req[1]; - } else { - mcbsp->dma_data[0].filter_data = "tx"; - mcbsp->dma_data[1].filter_data = "rx"; - } - - mcbsp->dma_data[0].addr = omap_mcbsp_dma_reg_params(mcbsp, 0); - mcbsp->dma_data[0].maxburst = 4; - - mcbsp->dma_data[1].addr = omap_mcbsp_dma_reg_params(mcbsp, 1); - mcbsp->dma_data[1].maxburst = 4; - - mcbsp->fclk = clk_get(&pdev->dev, "fck"); - if (IS_ERR(mcbsp->fclk)) { - ret = PTR_ERR(mcbsp->fclk); - dev_err(mcbsp->dev, "unable to get fck: %d\n", ret); - return ret; - } - - mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT; - if (mcbsp->pdata->buffer_size) { - /* - * Initially configure the maximum thresholds to a safe value. - * The McBSP FIFO usage with these values should not go under - * 16 locations. - * If the whole FIFO without safety buffer is used, than there - * is a possibility that the DMA will be not able to push the - * new data on time, causing channel shifts in runtime. - */ - mcbsp->max_tx_thres = max_thres(mcbsp) - 0x10; - mcbsp->max_rx_thres = max_thres(mcbsp) - 0x10; - - ret = sysfs_create_group(&mcbsp->dev->kobj, - &additional_attr_group); - if (ret) { - dev_err(mcbsp->dev, - "Unable to create additional controls\n"); - goto err_thres; - } - } else { - mcbsp->max_tx_thres = -EINVAL; - mcbsp->max_rx_thres = -EINVAL; - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sidetone"); - if (res) { - ret = omap_st_add(mcbsp, res); - if (ret) { - dev_err(mcbsp->dev, - "Unable to create sidetone controls\n"); - goto err_st; - } - } - - return 0; - -err_st: - if (mcbsp->pdata->buffer_size) - sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group); -err_thres: - clk_put(mcbsp->fclk); - return ret; -} - -void omap_mcbsp_cleanup(struct omap_mcbsp *mcbsp) -{ - if (mcbsp->pdata->buffer_size) - sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group); - - if (mcbsp->st_data) { - sysfs_remove_group(&mcbsp->dev->kobj, &sidetone_attr_group); - clk_put(mcbsp->st_data->mcbsp_iclk); - } -} diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index 943b44de1464..67159a6b90a8 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -79,7 +79,7 @@ config SND_PXA2XX_SOC_TOSA tristate "SoC AC97 Audio support for Tosa" depends on SND_PXA2XX_SOC && MACH_TOSA depends on MFD_TC6393XB - depends on !AC97_BUS + depends on AC97_BUS=n select SND_PXA2XX_SOC_AC97 select SND_SOC_WM9712 help @@ -89,7 +89,7 @@ config SND_PXA2XX_SOC_TOSA config SND_PXA2XX_SOC_E740 tristate "SoC AC97 Audio support for e740" depends on SND_PXA2XX_SOC && MACH_E740 - depends on !AC97_BUS + depends on AC97_BUS=n select SND_SOC_WM9705 select SND_PXA2XX_SOC_AC97 help @@ -99,7 +99,7 @@ config SND_PXA2XX_SOC_E740 config SND_PXA2XX_SOC_E750 tristate "SoC AC97 Audio support for e750" depends on SND_PXA2XX_SOC && MACH_E750 - depends on !AC97_BUS + depends on AC97_BUS=n select SND_SOC_WM9705 select SND_PXA2XX_SOC_AC97 help @@ -109,7 +109,7 @@ config SND_PXA2XX_SOC_E750 config SND_PXA2XX_SOC_E800 tristate "SoC AC97 Audio support for e800" depends on SND_PXA2XX_SOC && MACH_E800 - depends on !AC97_BUS + depends on AC97_BUS=n select SND_SOC_WM9712 select SND_PXA2XX_SOC_AC97 help @@ -120,7 +120,7 @@ config SND_PXA2XX_SOC_EM_X270 tristate "SoC Audio support for CompuLab EM-x270, eXeda and CM-X300" depends on SND_PXA2XX_SOC && (MACH_EM_X270 || MACH_EXEDA || \ MACH_CM_X300) - depends on !AC97_BUS + depends on AC97_BUS=n select SND_PXA2XX_SOC_AC97 select SND_SOC_WM9712 help @@ -131,7 +131,7 @@ config SND_PXA2XX_SOC_PALM27X bool "SoC Audio support for Palm T|X, T5, E2 and LifeDrive" depends on SND_PXA2XX_SOC && (MACH_PALMLD || MACH_PALMTX || \ MACH_PALMT5 || MACH_PALMTE2) - depends on !AC97_BUS + depends on AC97_BUS=n select SND_PXA2XX_SOC_AC97 select SND_SOC_WM9712 help @@ -161,7 +161,7 @@ config SND_SOC_TTC_DKB config SND_SOC_ZYLONITE tristate "SoC Audio support for Marvell Zylonite" depends on SND_PXA2XX_SOC && MACH_ZYLONITE - depends on !AC97_BUS + depends on AC97_BUS=n select SND_PXA2XX_SOC_AC97 select SND_PXA_SOC_SSP select SND_SOC_WM9713 @@ -169,16 +169,6 @@ config SND_SOC_ZYLONITE Say Y if you want to add support for SoC audio on the Marvell Zylonite reference platform. -config SND_SOC_RAUMFELD - tristate "SoC Audio support Raumfeld audio adapter" - depends on SND_PXA2XX_SOC && (MACH_RAUMFELD_SPEAKER || MACH_RAUMFELD_CONNECTOR) - depends on I2C && SPI_MASTER - select SND_PXA_SOC_SSP - select SND_SOC_CS4270 - select SND_SOC_AK4104 - help - Say Y if you want to add support for SoC audio on Raumfeld devices - config SND_PXA2XX_SOC_HX4700 tristate "SoC Audio support for HP iPAQ hx4700" depends on SND_PXA2XX_SOC && MACH_H4700 && I2C @@ -201,7 +191,7 @@ config SND_PXA2XX_SOC_MAGICIAN config SND_PXA2XX_SOC_MIOA701 tristate "SoC Audio support for MIO A701" depends on SND_PXA2XX_SOC && MACH_MIOA701 - depends on !AC97_BUS + depends on AC97_BUS=n select SND_PXA2XX_SOC_AC97 select SND_SOC_WM9713 help diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile index 5b265662f04f..0ab2a9dcb720 100644 --- a/sound/soc/pxa/Makefile +++ b/sound/soc/pxa/Makefile @@ -49,6 +49,5 @@ obj-$(CONFIG_SND_PXA2XX_SOC_MIOA701) += snd-soc-mioa701.o obj-$(CONFIG_SND_PXA2XX_SOC_Z2) += snd-soc-z2.o obj-$(CONFIG_SND_SOC_ZYLONITE) += snd-soc-zylonite.o obj-$(CONFIG_SND_PXA2XX_SOC_IMOTE2) += snd-soc-imote2.o -obj-$(CONFIG_SND_SOC_RAUMFELD) += snd-soc-raumfeld.o obj-$(CONFIG_SND_MMP_SOC_BROWNSTONE) += snd-soc-brownstone.o obj-$(CONFIG_SND_SOC_TTC_DKB) += snd-soc-ttc-dkb.o diff --git a/sound/soc/pxa/raumfeld.c b/sound/soc/pxa/raumfeld.c deleted file mode 100644 index 111a907c4eb9..000000000000 --- a/sound/soc/pxa/raumfeld.c +++ /dev/null @@ -1,318 +0,0 @@ -/* - * raumfeld_audio.c -- SoC audio for Raumfeld audio devices - * - * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> - * - * based on code from: - * - * Wolfson Microelectronics PLC. - * Openedhand Ltd. - * Liam Girdwood <lrg@slimlogic.co.uk> - * Richard Purdie <richard@openedhand.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 <linux/i2c.h> -#include <linux/delay.h> -#include <linux/gpio.h> -#include <sound/pcm.h> -#include <sound/soc.h> - -#include <asm/mach-types.h> - -#include "pxa-ssp.h" - -#define GPIO_SPDIF_RESET (38) -#define GPIO_MCLK_RESET (111) -#define GPIO_CODEC_RESET (120) - -static struct i2c_client *max9486_client; -static struct i2c_board_info max9486_hwmon_info = { - I2C_BOARD_INFO("max9485", 0x63), -}; - -#define MAX9485_MCLK_FREQ_112896 0x22 -#define MAX9485_MCLK_FREQ_122880 0x23 -#define MAX9485_MCLK_FREQ_225792 0x32 -#define MAX9485_MCLK_FREQ_245760 0x33 - -static void set_max9485_clk(char clk) -{ - i2c_master_send(max9486_client, &clk, 1); -} - -static void raumfeld_enable_audio(bool en) -{ - if (en) { - gpio_set_value(GPIO_MCLK_RESET, 1); - - /* wait some time to let the clocks become stable */ - msleep(100); - - gpio_set_value(GPIO_SPDIF_RESET, 1); - gpio_set_value(GPIO_CODEC_RESET, 1); - } else { - gpio_set_value(GPIO_MCLK_RESET, 0); - gpio_set_value(GPIO_SPDIF_RESET, 0); - gpio_set_value(GPIO_CODEC_RESET, 0); - } -} - -/* CS4270 */ -static int raumfeld_cs4270_startup(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - - /* set freq to 0 to enable all possible codec sample rates */ - return snd_soc_dai_set_sysclk(codec_dai, 0, 0, 0); -} - -static void raumfeld_cs4270_shutdown(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - - /* set freq to 0 to enable all possible codec sample rates */ - snd_soc_dai_set_sysclk(codec_dai, 0, 0, 0); -} - -static int raumfeld_cs4270_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; - unsigned int clk = 0; - int ret = 0; - - switch (params_rate(params)) { - case 44100: - set_max9485_clk(MAX9485_MCLK_FREQ_112896); - clk = 11289600; - break; - case 48000: - set_max9485_clk(MAX9485_MCLK_FREQ_122880); - clk = 12288000; - break; - case 88200: - set_max9485_clk(MAX9485_MCLK_FREQ_225792); - clk = 22579200; - break; - case 96000: - set_max9485_clk(MAX9485_MCLK_FREQ_245760); - clk = 24576000; - break; - default: - return -EINVAL; - } - - ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk, 0); - if (ret < 0) - return ret; - - /* setup the CPU DAI */ - ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, clk); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1); - if (ret < 0) - return ret; - - return 0; -} - -static const struct snd_soc_ops raumfeld_cs4270_ops = { - .startup = raumfeld_cs4270_startup, - .shutdown = raumfeld_cs4270_shutdown, - .hw_params = raumfeld_cs4270_hw_params, -}; - -static int raumfeld_analog_suspend(struct snd_soc_card *card) -{ - raumfeld_enable_audio(false); - return 0; -} - -static int raumfeld_analog_resume(struct snd_soc_card *card) -{ - raumfeld_enable_audio(true); - return 0; -} - -/* AK4104 */ - -static int raumfeld_ak4104_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; - int ret = 0, clk = 0; - - switch (params_rate(params)) { - case 44100: - set_max9485_clk(MAX9485_MCLK_FREQ_112896); - clk = 11289600; - break; - case 48000: - set_max9485_clk(MAX9485_MCLK_FREQ_122880); - clk = 12288000; - break; - case 88200: - set_max9485_clk(MAX9485_MCLK_FREQ_225792); - clk = 22579200; - break; - case 96000: - set_max9485_clk(MAX9485_MCLK_FREQ_245760); - clk = 24576000; - break; - default: - return -EINVAL; - } - - /* setup the CPU DAI */ - ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, clk); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1); - if (ret < 0) - return ret; - - return 0; -} - -static struct snd_soc_ops raumfeld_ak4104_ops = { - .hw_params = raumfeld_ak4104_hw_params, -}; - -#define DAI_LINK_CS4270 \ -{ \ - .name = "CS4270", \ - .stream_name = "CS4270", \ - .cpu_dai_name = "pxa-ssp-dai.0", \ - .platform_name = "pxa-pcm-audio", \ - .codec_dai_name = "cs4270-hifi", \ - .codec_name = "cs4270.0-0048", \ - .dai_fmt = SND_SOC_DAIFMT_I2S | \ - SND_SOC_DAIFMT_NB_NF | \ - SND_SOC_DAIFMT_CBS_CFS, \ - .ops = &raumfeld_cs4270_ops, \ -} - -#define DAI_LINK_AK4104 \ -{ \ - .name = "ak4104", \ - .stream_name = "Playback", \ - .cpu_dai_name = "pxa-ssp-dai.1", \ - .codec_dai_name = "ak4104-hifi", \ - .platform_name = "pxa-pcm-audio", \ - .dai_fmt = SND_SOC_DAIFMT_I2S | \ - SND_SOC_DAIFMT_NB_NF | \ - SND_SOC_DAIFMT_CBS_CFS, \ - .ops = &raumfeld_ak4104_ops, \ - .codec_name = "spi0.0", \ -} - -static struct snd_soc_dai_link snd_soc_raumfeld_connector_dai[] = { - DAI_LINK_CS4270, - DAI_LINK_AK4104, -}; - -static struct snd_soc_dai_link snd_soc_raumfeld_speaker_dai[] = { - DAI_LINK_CS4270, -}; - -static struct snd_soc_card snd_soc_raumfeld_connector = { - .name = "Raumfeld Connector", - .owner = THIS_MODULE, - .dai_link = snd_soc_raumfeld_connector_dai, - .num_links = ARRAY_SIZE(snd_soc_raumfeld_connector_dai), - .suspend_post = raumfeld_analog_suspend, - .resume_pre = raumfeld_analog_resume, -}; - -static struct snd_soc_card snd_soc_raumfeld_speaker = { - .name = "Raumfeld Speaker", - .owner = THIS_MODULE, - .dai_link = snd_soc_raumfeld_speaker_dai, - .num_links = ARRAY_SIZE(snd_soc_raumfeld_speaker_dai), - .suspend_post = raumfeld_analog_suspend, - .resume_pre = raumfeld_analog_resume, -}; - -static struct platform_device *raumfeld_audio_device; - -static int __init raumfeld_audio_init(void) -{ - int ret; - - if (!machine_is_raumfeld_speaker() && - !machine_is_raumfeld_connector()) - return 0; - - max9486_client = i2c_new_device(i2c_get_adapter(0), - &max9486_hwmon_info); - - if (!max9486_client) - return -ENOMEM; - - set_max9485_clk(MAX9485_MCLK_FREQ_122880); - - /* Register analog device */ - raumfeld_audio_device = platform_device_alloc("soc-audio", 0); - if (!raumfeld_audio_device) - return -ENOMEM; - - if (machine_is_raumfeld_speaker()) - platform_set_drvdata(raumfeld_audio_device, - &snd_soc_raumfeld_speaker); - - if (machine_is_raumfeld_connector()) - platform_set_drvdata(raumfeld_audio_device, - &snd_soc_raumfeld_connector); - - ret = platform_device_add(raumfeld_audio_device); - if (ret < 0) { - platform_device_put(raumfeld_audio_device); - return ret; - } - - raumfeld_enable_audio(true); - return 0; -} - -static void __exit raumfeld_audio_exit(void) -{ - raumfeld_enable_audio(false); - - platform_device_unregister(raumfeld_audio_device); - - i2c_unregister_device(max9486_client); - - gpio_free(GPIO_MCLK_RESET); - gpio_free(GPIO_CODEC_RESET); - gpio_free(GPIO_SPDIF_RESET); -} - -module_init(raumfeld_audio_init); -module_exit(raumfeld_audio_exit); - -/* Module information */ -MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); -MODULE_DESCRIPTION("Raumfeld audio SoC"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 2a4c912d1e48..804ae0d93058 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -66,6 +66,7 @@ config SND_SOC_QDSP6_ASM tristate config SND_SOC_QDSP6_ASM_DAI + select SND_SOC_COMPRESS tristate config SND_SOC_QDSP6 @@ -100,6 +101,7 @@ config SND_SOC_SDM845 depends on QCOM_APR select SND_SOC_QDSP6 select SND_SOC_QCOM_COMMON + select SND_SOC_RT5663 help To add support for audio on Qualcomm Technologies Inc. SDM845 SoC-based systems. diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index d07271ea4c45..028bce671cbc 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -91,7 +91,7 @@ static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream) if (ret) { dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n", ret); - return ret; + return ret; } data->dma_ch = dma_ch; diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c index 8f6c8fc073a9..dc645ba4d8d0 100644 --- a/sound/soc/qcom/qdsp6/q6afe-dai.c +++ b/sound/soc/qcom/qdsp6/q6afe-dai.c @@ -341,6 +341,7 @@ static int q6afe_dai_prepare(struct snd_pcm_substream *substream, switch (dai->id) { case HDMI_RX: + case DISPLAY_PORT_RX: q6afe_hdmi_port_prepare(dai_data->port[dai->id], &dai_data->port_config[dai->id].hdmi); break; @@ -445,6 +446,7 @@ static int q6afe_mi2s_set_sysclk(struct snd_soc_dai *dai, static const struct snd_soc_dapm_route q6afe_dapm_routes[] = { {"HDMI Playback", NULL, "HDMI_RX"}, + {"Display Port Playback", NULL, "DISPLAY_PORT_RX"}, {"Slimbus1 Playback", NULL, "SLIMBUS_1_RX"}, {"Slimbus2 Playback", NULL, "SLIMBUS_2_RX"}, {"Slimbus3 Playback", NULL, "SLIMBUS_3_RX"}, @@ -561,13 +563,13 @@ static const struct snd_soc_dapm_route q6afe_dapm_routes[] = { {"QUAT_MI2S_TX", NULL, "Quaternary MI2S Capture"}, }; -static struct snd_soc_dai_ops q6hdmi_ops = { +static const struct snd_soc_dai_ops q6hdmi_ops = { .prepare = q6afe_dai_prepare, .hw_params = q6hdmi_hw_params, .shutdown = q6afe_dai_shutdown, }; -static struct snd_soc_dai_ops q6i2s_ops = { +static const struct snd_soc_dai_ops q6i2s_ops = { .prepare = q6afe_dai_prepare, .hw_params = q6i2s_hw_params, .set_fmt = q6i2s_set_fmt, @@ -575,14 +577,14 @@ static struct snd_soc_dai_ops q6i2s_ops = { .set_sysclk = q6afe_mi2s_set_sysclk, }; -static struct snd_soc_dai_ops q6slim_ops = { +static const struct snd_soc_dai_ops q6slim_ops = { .prepare = q6afe_dai_prepare, .hw_params = q6slim_hw_params, .shutdown = q6afe_dai_shutdown, .set_channel_map = q6slim_set_channel_map, }; -static struct snd_soc_dai_ops q6tdm_ops = { +static const struct snd_soc_dai_ops q6tdm_ops = { .prepare = q6afe_dai_prepare, .shutdown = q6afe_dai_shutdown, .set_sysclk = q6afe_mi2s_set_sysclk, @@ -1090,6 +1092,25 @@ static struct snd_soc_dai_driver q6afe_dais[] = { Q6AFE_TDM_CAP_DAI("Quinary", 5, QUINARY_TDM_TX_5), Q6AFE_TDM_CAP_DAI("Quinary", 6, QUINARY_TDM_TX_6), Q6AFE_TDM_CAP_DAI("Quinary", 7, QUINARY_TDM_TX_7), + { + .playback = { + .stream_name = "Display Port Playback", + .rates = SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 2, + .channels_max = 8, + .rate_max = 192000, + .rate_min = 48000, + }, + .ops = &q6hdmi_ops, + .id = DISPLAY_PORT_RX, + .name = "DISPLAY_PORT", + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, }; static int q6afe_of_xlate_dai_name(struct snd_soc_component *component, @@ -1311,6 +1332,7 @@ static const struct snd_soc_dapm_widget q6afe_dai_widgets[] = { 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_7", NULL, 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("DISPLAY_PORT_RX", "NULL", 0, 0, 0, 0), }; static const struct snd_soc_component_driver q6afe_dai_component = { diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index 829b5e987b2a..e0945f7a58c8 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -71,6 +71,7 @@ /* Port IDs */ #define AFE_API_VERSION_HDMI_CONFIG 0x1 #define AFE_PORT_ID_MULTICHAN_HDMI_RX 0x100E +#define AFE_PORT_ID_HDMI_OVER_DP_RX 0x6020 #define AFE_API_VERSION_SLIMBUS_CONFIG 0x1 /* Clock set API version */ @@ -704,6 +705,8 @@ static struct afe_port_map port_maps[AFE_PORT_MAX] = { QUINARY_TDM_RX_7, 1, 1}, [QUINARY_TDM_TX_7] = { AFE_PORT_ID_QUINARY_TDM_TX_7, QUINARY_TDM_TX_7, 0, 1}, + [DISPLAY_PORT_RX] = { AFE_PORT_ID_HDMI_OVER_DP_RX, + DISPLAY_PORT_RX, 1, 1}, }; static void q6afe_port_free(struct kref *ref) @@ -1384,6 +1387,7 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id) switch (port_id) { case AFE_PORT_ID_MULTICHAN_HDMI_RX: + case AFE_PORT_ID_HDMI_OVER_DP_RX: cfg_type = AFE_PARAM_ID_HDMI_CONFIG; break; case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_TX: diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c index 86115de5c1b2..5b986b74dd36 100644 --- a/sound/soc/qcom/qdsp6/q6asm-dai.c +++ b/sound/soc/qcom/qdsp6/q6asm-dai.c @@ -10,6 +10,8 @@ #include <sound/soc.h> #include <sound/soc-dapm.h> #include <sound/pcm.h> +#include <linux/spinlock.h> +#include <sound/compress_driver.h> #include <asm/dma.h> #include <linux/dma-mapping.h> #include <linux/of_device.h> @@ -30,6 +32,15 @@ #define CAPTURE_MIN_PERIOD_SIZE 320 #define SID_MASK_DEFAULT 0xF +/* Default values used if user space does not set */ +#define COMPR_PLAYBACK_MIN_FRAGMENT_SIZE (8 * 1024) +#define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024) +#define COMPR_PLAYBACK_MIN_NUM_FRAGMENTS (4) +#define COMPR_PLAYBACK_MAX_NUM_FRAGMENTS (16 * 4) +#define Q6ASM_DAI_TX_RX 0 +#define Q6ASM_DAI_TX 1 +#define Q6ASM_DAI_RX 2 + enum stream_state { Q6ASM_STREAM_IDLE = 0, Q6ASM_STREAM_STOPPED, @@ -38,11 +49,18 @@ enum stream_state { struct q6asm_dai_rtd { struct snd_pcm_substream *substream; + struct snd_compr_stream *cstream; + struct snd_compr_params codec_param; + struct snd_dma_buffer dma_buffer; + spinlock_t lock; phys_addr_t phys; unsigned int pcm_size; unsigned int pcm_count; unsigned int pcm_irq_pos; /* IRQ position */ unsigned int periods; + unsigned int bytes_sent; + unsigned int bytes_received; + unsigned int copied_total; uint16_t bits_per_sample; uint16_t source; /* Encoding source bit mask */ struct audio_client *audio_client; @@ -137,6 +155,21 @@ static struct snd_pcm_hw_constraint_list constraints_sample_rates = { .mask = 0, }; +static const struct snd_compr_codec_caps q6asm_compr_caps = { + .num_descriptors = 1, + .descriptor[0].max_ch = 2, + .descriptor[0].sample_rates = { 8000, 11025, 12000, 16000, 22050, + 24000, 32000, 44100, 48000, 88200, + 96000, 176400, 192000 }, + .descriptor[0].num_sample_rates = 13, + .descriptor[0].bit_rate[0] = 320, + .descriptor[0].bit_rate[1] = 128, + .descriptor[0].num_bitrates = 2, + .descriptor[0].profiles = 0, + .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO, + .descriptor[0].formats = 0, +}; + static void event_handler(uint32_t opcode, uint32_t token, uint32_t *payload, void *priv) { @@ -460,6 +493,306 @@ static struct snd_pcm_ops q6asm_dai_ops = { .mmap = q6asm_dai_mmap, }; +static void compress_event_handler(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct q6asm_dai_rtd *prtd = priv; + struct snd_compr_stream *substream = prtd->cstream; + unsigned long flags; + uint64_t avail; + + switch (opcode) { + case ASM_CLIENT_EVENT_CMD_RUN_DONE: + spin_lock_irqsave(&prtd->lock, flags); + if (!prtd->bytes_sent) { + q6asm_write_async(prtd->audio_client, prtd->pcm_count, + 0, 0, NO_TIMESTAMP); + prtd->bytes_sent += prtd->pcm_count; + } + + spin_unlock_irqrestore(&prtd->lock, flags); + break; + + case ASM_CLIENT_EVENT_CMD_EOS_DONE: + prtd->state = Q6ASM_STREAM_STOPPED; + break; + + case ASM_CLIENT_EVENT_DATA_WRITE_DONE: + spin_lock_irqsave(&prtd->lock, flags); + + prtd->copied_total += prtd->pcm_count; + snd_compr_fragment_elapsed(substream); + + if (prtd->state != Q6ASM_STREAM_RUNNING) { + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } + + avail = prtd->bytes_received - prtd->bytes_sent; + + if (avail >= prtd->pcm_count) { + q6asm_write_async(prtd->audio_client, + prtd->pcm_count, 0, 0, NO_TIMESTAMP); + prtd->bytes_sent += prtd->pcm_count; + } + + spin_unlock_irqrestore(&prtd->lock, flags); + break; + + default: + break; + } +} + +static int q6asm_dai_compr_open(struct snd_compr_stream *stream) +{ + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct snd_soc_component *c = snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct snd_compr_runtime *runtime = stream->runtime; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct q6asm_dai_data *pdata; + struct device *dev = c->dev; + struct q6asm_dai_rtd *prtd; + int stream_id, size, ret; + + stream_id = cpu_dai->driver->id; + pdata = snd_soc_component_get_drvdata(c); + if (!pdata) { + dev_err(dev, "Drv data not found ..\n"); + return -EINVAL; + } + + prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); + if (!prtd) + return -ENOMEM; + + prtd->cstream = stream; + prtd->audio_client = q6asm_audio_client_alloc(dev, + (q6asm_cb)compress_event_handler, + prtd, stream_id, LEGACY_PCM_MODE); + if (!prtd->audio_client) { + dev_err(dev, "Could not allocate memory\n"); + kfree(prtd); + return -ENOMEM; + } + + size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE * + COMPR_PLAYBACK_MAX_NUM_FRAGMENTS; + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, + &prtd->dma_buffer); + if (ret) { + dev_err(dev, "Cannot allocate buffer(s)\n"); + return ret; + } + + if (pdata->sid < 0) + prtd->phys = prtd->dma_buffer.addr; + else + prtd->phys = prtd->dma_buffer.addr | (pdata->sid << 32); + + snd_compr_set_runtime_buffer(stream, &prtd->dma_buffer); + spin_lock_init(&prtd->lock); + runtime->private_data = prtd; + + return 0; +} + +static int q6asm_dai_compr_free(struct snd_compr_stream *stream) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6asm_dai_rtd *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = stream->private_data; + + if (prtd->audio_client) { + if (prtd->state) + q6asm_cmd(prtd->audio_client, CMD_CLOSE); + + snd_dma_free_pages(&prtd->dma_buffer); + q6asm_unmap_memory_regions(stream->direction, + prtd->audio_client); + q6asm_audio_client_free(prtd->audio_client); + prtd->audio_client = NULL; + } + q6routing_stream_close(rtd->dai_link->id, stream->direction); + kfree(prtd); + + return 0; +} + +static int q6asm_dai_compr_set_params(struct snd_compr_stream *stream, + struct snd_compr_params *params) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6asm_dai_rtd *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct snd_soc_component *c = snd_soc_rtdcom_lookup(rtd, DRV_NAME); + int dir = stream->direction; + struct q6asm_dai_data *pdata; + struct device *dev = c->dev; + int ret; + + memcpy(&prtd->codec_param, params, sizeof(*params)); + + pdata = snd_soc_component_get_drvdata(c); + if (!pdata) + return -EINVAL; + + if (!prtd || !prtd->audio_client) { + dev_err(dev, "private data null or audio client freed\n"); + return -EINVAL; + } + + prtd->periods = runtime->fragments; + prtd->pcm_count = runtime->fragment_size; + prtd->pcm_size = runtime->fragments * runtime->fragment_size; + prtd->bits_per_sample = 16; + if (dir == SND_COMPRESS_PLAYBACK) { + ret = q6asm_open_write(prtd->audio_client, params->codec.id, + prtd->bits_per_sample); + + if (ret < 0) { + dev_err(dev, "q6asm_open_write failed\n"); + q6asm_audio_client_free(prtd->audio_client); + prtd->audio_client = NULL; + return ret; + } + } + + prtd->session_id = q6asm_get_session_id(prtd->audio_client); + ret = q6routing_stream_open(rtd->dai_link->id, LEGACY_PCM_MODE, + prtd->session_id, dir); + if (ret) { + dev_err(dev, "Stream reg failed ret:%d\n", ret); + return ret; + } + + ret = q6asm_map_memory_regions(dir, prtd->audio_client, prtd->phys, + (prtd->pcm_size / prtd->periods), + prtd->periods); + + if (ret < 0) { + dev_err(dev, "Buffer Mapping failed ret:%d\n", ret); + return -ENOMEM; + } + + prtd->state = Q6ASM_STREAM_RUNNING; + + return 0; +} + +static int q6asm_dai_compr_trigger(struct snd_compr_stream *stream, int cmd) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6asm_dai_rtd *prtd = runtime->private_data; + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = q6asm_run_nowait(prtd->audio_client, 0, 0, 0); + break; + case SNDRV_PCM_TRIGGER_STOP: + prtd->state = Q6ASM_STREAM_STOPPED; + ret = q6asm_cmd_nowait(prtd->audio_client, CMD_EOS); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int q6asm_dai_compr_pointer(struct snd_compr_stream *stream, + struct snd_compr_tstamp *tstamp) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6asm_dai_rtd *prtd = runtime->private_data; + unsigned long flags; + + spin_lock_irqsave(&prtd->lock, flags); + + tstamp->copied_total = prtd->copied_total; + tstamp->byte_offset = prtd->copied_total % prtd->pcm_size; + + spin_unlock_irqrestore(&prtd->lock, flags); + + return 0; +} + +static int q6asm_dai_compr_ack(struct snd_compr_stream *stream, + size_t count) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6asm_dai_rtd *prtd = runtime->private_data; + unsigned long flags; + + spin_lock_irqsave(&prtd->lock, flags); + prtd->bytes_received += count; + spin_unlock_irqrestore(&prtd->lock, flags); + + return count; +} + +static int q6asm_dai_compr_mmap(struct snd_compr_stream *stream, + struct vm_area_struct *vma) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6asm_dai_rtd *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct snd_soc_component *c = snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct device *dev = c->dev; + + return dma_mmap_coherent(dev, vma, + prtd->dma_buffer.area, prtd->dma_buffer.addr, + prtd->dma_buffer.bytes); +} + +static int q6asm_dai_compr_get_caps(struct snd_compr_stream *stream, + struct snd_compr_caps *caps) +{ + caps->direction = SND_COMPRESS_PLAYBACK; + caps->min_fragment_size = COMPR_PLAYBACK_MIN_FRAGMENT_SIZE; + caps->max_fragment_size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE; + caps->min_fragments = COMPR_PLAYBACK_MIN_NUM_FRAGMENTS; + caps->max_fragments = COMPR_PLAYBACK_MAX_NUM_FRAGMENTS; + caps->num_codecs = 1; + caps->codecs[0] = SND_AUDIOCODEC_MP3; + + return 0; +} + +static int q6asm_dai_compr_get_codec_caps(struct snd_compr_stream *stream, + struct snd_compr_codec_caps *codec) +{ + switch (codec->codec) { + case SND_AUDIOCODEC_MP3: + *codec = q6asm_compr_caps; + break; + default: + break; + } + + return 0; +} + +static struct snd_compr_ops q6asm_dai_compr_ops = { + .open = q6asm_dai_compr_open, + .free = q6asm_dai_compr_free, + .set_params = q6asm_dai_compr_set_params, + .pointer = q6asm_dai_compr_pointer, + .trigger = q6asm_dai_compr_trigger, + .get_caps = q6asm_dai_compr_get_caps, + .get_codec_caps = q6asm_dai_compr_get_codec_caps, + .mmap = q6asm_dai_compr_mmap, + .ack = q6asm_dai_compr_ack, +}; + static int q6asm_dai_pcm_new(struct snd_soc_pcm_runtime *rtd) { struct snd_pcm_substream *psubstream, *csubstream; @@ -515,7 +848,7 @@ static const struct snd_soc_component_driver q6asm_fe_dai_component = { .ops = &q6asm_dai_ops, .pcm_new = q6asm_dai_pcm_new, .pcm_free = q6asm_dai_pcm_free, - + .compr_ops = &q6asm_dai_compr_ops, }; static struct snd_soc_dai_driver q6asm_fe_dais[] = { @@ -529,6 +862,41 @@ static struct snd_soc_dai_driver q6asm_fe_dais[] = { Q6ASM_FEDAI_DRIVER(8), }; +static int of_q6asm_parse_dai_data(struct device *dev, + struct q6asm_dai_data *pdata) +{ + static struct snd_soc_dai_driver *dai_drv; + struct snd_soc_pcm_stream empty_stream; + struct device_node *node; + int ret, id, dir; + + memset(&empty_stream, 0, sizeof(empty_stream)); + + for_each_child_of_node(dev->of_node, node) { + ret = of_property_read_u32(node, "reg", &id); + if (ret || id > MAX_SESSIONS || id < 0) { + dev_err(dev, "valid dai id not found:%d\n", ret); + continue; + } + + dai_drv = &q6asm_fe_dais[id]; + + ret = of_property_read_u32(node, "direction", &dir); + if (ret) + continue; + + if (dir == Q6ASM_DAI_RX) + dai_drv->capture = empty_stream; + else if (dir == Q6ASM_DAI_TX) + dai_drv->playback = empty_stream; + + if (of_property_read_bool(node, "is-compress-dai")) + dai_drv->compress_new = snd_soc_new_compress; + } + + return 0; +} + static int q6asm_dai_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -549,6 +917,8 @@ static int q6asm_dai_probe(struct platform_device *pdev) dev_set_drvdata(dev, pdata); + of_q6asm_parse_dai_data(dev, pdata); + return devm_snd_soc_register_component(dev, &q6asm_fe_dai_component, q6asm_fe_dais, ARRAY_SIZE(q6asm_fe_dais)); diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c index e1cfa846a1dc..4f85cb19a309 100644 --- a/sound/soc/qcom/qdsp6/q6asm.c +++ b/sound/soc/qcom/qdsp6/q6asm.c @@ -12,6 +12,7 @@ #include <linux/kref.h> #include <linux/of.h> #include <uapi/sound/asound.h> +#include <uapi/sound/compress_params.h> #include <linux/delay.h> #include <linux/slab.h> #include <linux/mm.h> @@ -36,6 +37,7 @@ #define ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2 0x00010DA3 #define ASM_SESSION_CMD_RUN_V2 0x00010DAA #define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 0x00010DA5 +#define ASM_MEDIA_FMT_MP3 0x00010BE9 #define ASM_DATA_CMD_WRITE_V2 0x00010DAB #define ASM_DATA_CMD_READ_V2 0x00010DAC #define ASM_SESSION_CMD_SUSPEND 0x00010DEC @@ -868,6 +870,9 @@ int q6asm_open_write(struct audio_client *ac, uint32_t format, open->postprocopo_id = ASM_NULL_POPP_TOPOLOGY; switch (format) { + case SND_AUDIOCODEC_MP3: + open->dec_fmt_id = ASM_MEDIA_FMT_MP3; + break; case FORMAT_LINEAR_PCM: open->dec_fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; break; diff --git a/sound/soc/qcom/qdsp6/q6routing.c b/sound/soc/qcom/qdsp6/q6routing.c index d61b8404f7da..ddcd9978cf57 100644 --- a/sound/soc/qcom/qdsp6/q6routing.c +++ b/sound/soc/qcom/qdsp6/q6routing.c @@ -453,6 +453,9 @@ static int msm_routing_put_audio_mixer(struct snd_kcontrol *kcontrol, static const struct snd_kcontrol_new hdmi_mixer_controls[] = { Q6ROUTING_RX_MIXERS(HDMI_RX) }; +static const struct snd_kcontrol_new display_port_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(DISPLAY_PORT_RX) }; + static const struct snd_kcontrol_new primary_mi2s_rx_mixer_controls[] = { Q6ROUTING_RX_MIXERS(PRIMARY_MI2S_RX) }; @@ -655,6 +658,10 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { hdmi_mixer_controls, ARRAY_SIZE(hdmi_mixer_controls)), + SND_SOC_DAPM_MIXER("DISPLAY_PORT_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + display_port_mixer_controls, + ARRAY_SIZE(display_port_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Audio Mixer", SND_SOC_NOPM, 0, 0, slimbus_rx_mixer_controls, ARRAY_SIZE(slimbus_rx_mixer_controls)), @@ -833,6 +840,8 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { static const struct snd_soc_dapm_route intercon[] = { Q6ROUTING_RX_DAPM_ROUTE("HDMI Mixer", "HDMI_RX"), + Q6ROUTING_RX_DAPM_ROUTE("DISPLAY_PORT_RX Audio Mixer", + "DISPLAY_PORT_RX"), Q6ROUTING_RX_DAPM_ROUTE("SLIMBUS_0_RX Audio Mixer", "SLIMBUS_0_RX"), Q6ROUTING_RX_DAPM_ROUTE("SLIMBUS_1_RX Audio Mixer", "SLIMBUS_1_RX"), Q6ROUTING_RX_DAPM_ROUTE("SLIMBUS_2_RX Audio Mixer", "SLIMBUS_2_RX"), diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c index 9effbecc571f..1db8ef668223 100644 --- a/sound/soc/qcom/sdm845.c +++ b/sound/soc/qcom/sdm845.c @@ -6,18 +6,31 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/of_device.h> +#include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> +#include <sound/jack.h> +#include <sound/soc.h> +#include <uapi/linux/input-event-codes.h> #include "common.h" #include "qdsp6/q6afe.h" +#include "../codecs/rt5663.h" #define DEFAULT_SAMPLE_RATE_48K 48000 #define DEFAULT_MCLK_RATE 24576000 -#define DEFAULT_BCLK_RATE 12288000 +#define TDM_BCLK_RATE 6144000 +#define MI2S_BCLK_RATE 1536000 +#define LEFT_SPK_TDM_TX_MASK 0x30 +#define RIGHT_SPK_TDM_TX_MASK 0xC0 +#define SPK_TDM_RX_MASK 0x03 +#define NUM_TDM_SLOTS 8 struct sdm845_snd_data { + struct snd_soc_jack jack; + bool jack_setup; struct snd_soc_card *card; uint32_t pri_mi2s_clk_count; + uint32_t sec_mi2s_clk_count; uint32_t quat_tdm_clk_count; }; @@ -28,12 +41,12 @@ static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0; + int ret = 0, j; int channels, slot_width; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: - slot_width = 32; + slot_width = 16; break; default: dev_err(rtd->dev, "%s: invalid param format 0x%x\n", @@ -75,6 +88,35 @@ static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream, goto end; } } + + for (j = 0; j < rtd->num_codecs; j++) { + struct snd_soc_dai *codec_dai = rtd->codec_dais[j]; + + if (!strcmp(codec_dai->component->name_prefix, "Left")) { + ret = snd_soc_dai_set_tdm_slot( + codec_dai, LEFT_SPK_TDM_TX_MASK, + SPK_TDM_RX_MASK, NUM_TDM_SLOTS, + slot_width); + if (ret < 0) { + dev_err(rtd->dev, + "DEV0 TDM slot err:%d\n", ret); + return ret; + } + } + + if (!strcmp(codec_dai->component->name_prefix, "Right")) { + ret = snd_soc_dai_set_tdm_slot( + codec_dai, RIGHT_SPK_TDM_TX_MASK, + SPK_TDM_RX_MASK, NUM_TDM_SLOTS, + slot_width); + if (ret < 0) { + dev_err(rtd->dev, + "DEV1 TDM slot err:%d\n", ret); + return ret; + } + } + } + end: return ret; } @@ -84,9 +126,27 @@ static int sdm845_snd_hw_params(struct snd_pcm_substream *substream, { 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 ret = 0; switch (cpu_dai->id) { + case PRIMARY_MI2S_RX: + case PRIMARY_MI2S_TX: + /* + * Use ASRC for internal clocks, as PLL rate isn't multiple + * of BCLK. + */ + rt5663_sel_asrc_clk_src( + codec_dai->component, + RT5663_DA_STEREO_FILTER | RT5663_AD_STEREO_FILTER, + RT5663_CLK_SEL_I2S1_ASRC); + ret = snd_soc_dai_set_sysclk( + codec_dai, RT5663_SCLK_S_MCLK, DEFAULT_MCLK_RATE, + SND_SOC_CLOCK_IN); + if (ret < 0) + dev_err(rtd->dev, + "snd_soc_dai_set_sysclk err = %d\n", ret); + break; case QUATERNARY_TDM_RX_0: case QUATERNARY_TDM_TX_0: ret = sdm845_tdm_snd_hw_params(substream, params); @@ -98,24 +158,87 @@ static int sdm845_snd_hw_params(struct snd_pcm_substream *substream, return ret; } +static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_component *component; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + struct snd_soc_card *card = rtd->card; + struct sdm845_snd_data *pdata = snd_soc_card_get_drvdata(card); + int i, rval; + + if (!pdata->jack_setup) { + struct snd_jack *jack; + + rval = snd_soc_card_jack_new(card, "Headset Jack", + SND_JACK_HEADSET | + SND_JACK_HEADPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &pdata->jack, NULL, 0); + + if (rval < 0) { + dev_err(card->dev, "Unable to add Headphone Jack\n"); + return rval; + } + + jack = pdata->jack.jack; + + snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); + snd_jack_set_key(jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + pdata->jack_setup = true; + } + + for (i = 0 ; i < dai_link->num_codecs; i++) { + struct snd_soc_dai *dai = rtd->codec_dais[i]; + + component = dai->component; + rval = snd_soc_component_set_jack( + component, &pdata->jack, NULL); + if (rval != 0 && rval != -ENOTSUPP) { + dev_warn(card->dev, "Failed to set jack: %d\n", rval); + return rval; + } + } + + return 0; +} + + static int sdm845_snd_startup(struct snd_pcm_substream *substream) { unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS; + unsigned int codec_dai_fmt = SND_SOC_DAIFMT_CBS_CFS; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_card *card = rtd->card; struct sdm845_snd_data *data = snd_soc_card_get_drvdata(card); struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int j; + int ret; switch (cpu_dai->id) { case PRIMARY_MI2S_RX: case PRIMARY_MI2S_TX: + codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF; if (++(data->pri_mi2s_clk_count) == 1) { snd_soc_dai_set_sysclk(cpu_dai, Q6AFE_LPASS_CLK_ID_MCLK_1, DEFAULT_MCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); snd_soc_dai_set_sysclk(cpu_dai, Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT, - DEFAULT_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); + MI2S_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); + } + snd_soc_dai_set_fmt(cpu_dai, fmt); + snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); + break; + + case SECONDARY_MI2S_TX: + if (++(data->sec_mi2s_clk_count) == 1) { + snd_soc_dai_set_sysclk(cpu_dai, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT, + MI2S_BCLK_RATE, SNDRV_PCM_STREAM_CAPTURE); } snd_soc_dai_set_fmt(cpu_dai, fmt); break; @@ -125,7 +248,35 @@ static int sdm845_snd_startup(struct snd_pcm_substream *substream) if (++(data->quat_tdm_clk_count) == 1) { snd_soc_dai_set_sysclk(cpu_dai, Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT, - DEFAULT_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); + TDM_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); + } + + codec_dai_fmt |= SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_DSP_B; + + for (j = 0; j < rtd->num_codecs; j++) { + codec_dai = rtd->codec_dais[j]; + + if (!strcmp(codec_dai->component->name_prefix, + "Left")) { + ret = snd_soc_dai_set_fmt( + codec_dai, codec_dai_fmt); + if (ret < 0) { + dev_err(rtd->dev, + "Left TDM fmt err:%d\n", ret); + return ret; + } + } + + if (!strcmp(codec_dai->component->name_prefix, + "Right")) { + ret = snd_soc_dai_set_fmt( + codec_dai, codec_dai_fmt); + if (ret < 0) { + dev_err(rtd->dev, + "Right TDM slot err:%d\n", ret); + return ret; + } + } } break; @@ -156,6 +307,14 @@ static void sdm845_snd_shutdown(struct snd_pcm_substream *substream) }; break; + case SECONDARY_MI2S_TX: + if (--(data->sec_mi2s_clk_count) == 0) { + snd_soc_dai_set_sysclk(cpu_dai, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT, + 0, SNDRV_PCM_STREAM_CAPTURE); + } + break; + case QUATERNARY_TDM_RX_0: case QUATERNARY_TDM_TX_0: if (--(data->quat_tdm_clk_count) == 0) { @@ -171,7 +330,7 @@ static void sdm845_snd_shutdown(struct snd_pcm_substream *substream) } } -static struct snd_soc_ops sdm845_be_ops = { +static const struct snd_soc_ops sdm845_be_ops = { .hw_params = sdm845_snd_hw_params, .startup = sdm845_snd_startup, .shutdown = sdm845_snd_shutdown, @@ -193,7 +352,15 @@ static int sdm845_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -static void sdm845_add_be_ops(struct snd_soc_card *card) +static const struct snd_soc_dapm_widget sdm845_snd_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_SPK("Left Spk", NULL), + SND_SOC_DAPM_SPK("Right Spk", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), +}; + +static void sdm845_add_ops(struct snd_soc_card *card) { struct snd_soc_dai_link *link; int i; @@ -203,6 +370,7 @@ static void sdm845_add_be_ops(struct snd_soc_card *card) link->ops = &sdm845_be_ops; link->be_hw_params_fixup = sdm845_be_hw_params_fixup; } + link->init = sdm845_dai_init; } } @@ -224,6 +392,8 @@ static int sdm845_snd_platform_probe(struct platform_device *pdev) goto data_alloc_fail; } + card->dapm_widgets = sdm845_snd_widgets; + card->num_dapm_widgets = ARRAY_SIZE(sdm845_snd_widgets); card->dev = dev; dev_set_drvdata(dev, card); ret = qcom_snd_parse_of(card); @@ -235,7 +405,7 @@ static int sdm845_snd_platform_probe(struct platform_device *pdev) data->card = card; snd_soc_card_set_drvdata(card, data); - sdm845_add_be_ops(card); + sdm845_add_ops(card); ret = snd_soc_register_card(card); if (ret) { dev_err(dev, "Sound card registration failed\n"); diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 28327dd2c6cb..e821ccc70f47 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -249,28 +249,8 @@ int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod, out = out << shift; mask = 0x0f1f << shift; - switch (id / 2) { - case 0: - rsnd_mod_bset(adg_mod, SRCIN_TIMSEL0, mask, in); - rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL0, mask, out); - break; - case 1: - rsnd_mod_bset(adg_mod, SRCIN_TIMSEL1, mask, in); - rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL1, mask, out); - break; - case 2: - rsnd_mod_bset(adg_mod, SRCIN_TIMSEL2, mask, in); - rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL2, mask, out); - break; - case 3: - rsnd_mod_bset(adg_mod, SRCIN_TIMSEL3, mask, in); - rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL3, mask, out); - break; - case 4: - rsnd_mod_bset(adg_mod, SRCIN_TIMSEL4, mask, in); - rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL4, mask, out); - break; - } + rsnd_mod_bset(adg_mod, SRCIN_TIMSEL(id / 2), mask, in); + rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL(id / 2), mask, out); if (en) rsnd_mod_bset(adg_mod, DIV_EN, en, en); @@ -299,17 +279,7 @@ static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val) if (id == 8) return; - switch (id / 4) { - case 0: - rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL0, mask, val); - break; - case 1: - rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL1, mask, val); - break; - case 2: - rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL2, mask, val); - break; - } + rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL(id / 4), mask, val); dev_dbg(dev, "AUDIO_CLK_SEL is 0x%x\n", val); } @@ -613,7 +583,7 @@ int rsnd_adg_probe(struct rsnd_priv *priv) return -ENOMEM; ret = rsnd_mod_init(priv, &adg->mod, &adg_ops, - NULL, NULL, 0, 0); + NULL, 0, 0); if (ret) return ret; diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c index cc191cd5fb82..e6bb6a9a0684 100644 --- a/sound/soc/sh/rcar/cmd.c +++ b/sound/soc/sh/rcar/cmd.c @@ -116,10 +116,11 @@ static int rsnd_cmd_stop(struct rsnd_mod *mod, } static struct rsnd_mod_ops rsnd_cmd_ops = { - .name = CMD_NAME, - .init = rsnd_cmd_init, - .start = rsnd_cmd_start, - .stop = rsnd_cmd_stop, + .name = CMD_NAME, + .init = rsnd_cmd_init, + .start = rsnd_cmd_start, + .stop = rsnd_cmd_stop, + .get_status = rsnd_mod_get_status, }; static struct rsnd_mod *rsnd_cmd_mod_get(struct rsnd_priv *priv, int id) @@ -162,7 +163,7 @@ int rsnd_cmd_probe(struct rsnd_priv *priv) for_each_rsnd_cmd(cmd, priv, i) { ret = rsnd_mod_init(priv, rsnd_mod_get(cmd), &rsnd_cmd_ops, NULL, - rsnd_mod_get_status, RSND_MOD_CMD, i); + RSND_MOD_CMD, i); if (ret) return ret; } diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index f930f51b686f..59e250cc2e9d 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -123,8 +123,8 @@ void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type) struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); - dev_warn(dev, "%s[%d] is not your expected module\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); + dev_warn(dev, "%s is not your expected module\n", + rsnd_mod_name(mod)); } } @@ -137,20 +137,69 @@ struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, return mod->ops->dma_req(io, mod); } -u32 *rsnd_mod_get_status(struct rsnd_dai_stream *io, - struct rsnd_mod *mod, +#define MOD_NAME_NUM 5 +#define MOD_NAME_SIZE 16 +char *rsnd_mod_name(struct rsnd_mod *mod) +{ + static char names[MOD_NAME_NUM][MOD_NAME_SIZE]; + static int num; + char *name = names[num]; + + num++; + if (num >= MOD_NAME_NUM) + num = 0; + + /* + * Let's use same char to avoid pointlessness memory + * Thus, rsnd_mod_name() should be used immediately + * Don't keep pointer + */ + if ((mod)->ops->id_sub) { + snprintf(name, MOD_NAME_SIZE, "%s[%d%d]", + mod->ops->name, + rsnd_mod_id(mod), + rsnd_mod_id_sub(mod)); + } else { + snprintf(name, MOD_NAME_SIZE, "%s[%d]", + mod->ops->name, + rsnd_mod_id(mod)); + } + + return name; +} + +u32 *rsnd_mod_get_status(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, enum rsnd_mod_type type) { return &mod->status; } +int rsnd_mod_id_raw(struct rsnd_mod *mod) +{ + return mod->id; +} + +int rsnd_mod_id(struct rsnd_mod *mod) +{ + if ((mod)->ops->id) + return (mod)->ops->id(mod); + + return rsnd_mod_id_raw(mod); +} + +int rsnd_mod_id_sub(struct rsnd_mod *mod) +{ + if ((mod)->ops->id_sub) + return (mod)->ops->id_sub(mod); + + return 0; +} + int rsnd_mod_init(struct rsnd_priv *priv, struct rsnd_mod *mod, struct rsnd_mod_ops *ops, struct clk *clk, - u32* (*get_status)(struct rsnd_dai_stream *io, - struct rsnd_mod *mod, - enum rsnd_mod_type type), enum rsnd_mod_type type, int id) { @@ -164,7 +213,6 @@ int rsnd_mod_init(struct rsnd_priv *priv, mod->type = type; mod->clk = clk; mod->priv = priv; - mod->get_status = get_status; return ret; } @@ -228,7 +276,20 @@ int rsnd_runtime_channel_after_ctu_with_params(struct rsnd_dai_stream *io, struct rsnd_mod *ctu_mod = rsnd_io_to_mod_ctu(io); if (ctu_mod) { - u32 converted_chan = rsnd_ctu_converted_channel(ctu_mod); + u32 converted_chan = rsnd_io_converted_chan(io); + + /* + * !! Note !! + * + * converted_chan will be used for CTU, + * or TDM Split mode. + * User shouldn't use CTU with TDM Split mode. + */ + if (rsnd_runtime_is_tdm_split(io)) { + struct device *dev = rsnd_priv_to_dev(rsnd_io_to_priv(io)); + + dev_err(dev, "CTU and TDM Split should be used\n"); + } if (converted_chan) return converted_chan; @@ -246,7 +307,7 @@ int rsnd_runtime_channel_for_ssi_with_params(struct rsnd_dai_stream *io, rsnd_runtime_channel_original_with_params(io, params); /* Use Multi SSI */ - if (rsnd_runtime_is_ssi_multi(io)) + if (rsnd_runtime_is_multi_ssi(io)) chan /= rsnd_rdai_ssi_lane_get(rdai); /* TDM Extend Mode needs 8ch */ @@ -256,7 +317,7 @@ int rsnd_runtime_channel_for_ssi_with_params(struct rsnd_dai_stream *io, return chan; } -int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io) +int rsnd_runtime_is_multi_ssi(struct rsnd_dai_stream *io) { struct rsnd_dai *rdai = rsnd_io_to_rdai(io); int lane = rsnd_rdai_ssi_lane_get(rdai); @@ -267,11 +328,16 @@ int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io) return (chan > 2) && (lane > 1); } -int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io) +int rsnd_runtime_is_tdm(struct rsnd_dai_stream *io) { return rsnd_runtime_channel_for_ssi(io) >= 6; } +int rsnd_runtime_is_tdm_split(struct rsnd_dai_stream *io) +{ + return !!rsnd_flags_has(io, RSND_STREAM_TDM_SPLIT); +} + /* * ADINR function */ @@ -472,20 +538,19 @@ static int rsnd_status_update(u32 *status, 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]); \ + u32 *status = mod->ops->get_status(mod, io, types[i]); \ int func_call = rsnd_status_update(status, \ __rsnd_mod_shift_##fn, \ __rsnd_mod_add_##fn, \ __rsnd_mod_call_##fn); \ - rsnd_dbg_dai_call(dev, "%s[%d]\t0x%08x %s\n", \ - rsnd_mod_name(mod), rsnd_mod_id(mod), *status, \ + rsnd_dbg_dai_call(dev, "%s\t0x%08x %s\n", \ + rsnd_mod_name(mod), *status, \ (func_call && (mod)->ops->fn) ? #fn : ""); \ if (func_call && (mod)->ops->fn) \ tmp = (mod)->ops->fn(mod, io, param); \ if (tmp && (tmp != -EPROBE_DEFER)) \ - dev_err(dev, "%s[%d] : %s error %d\n", \ - rsnd_mod_name(mod), rsnd_mod_id(mod), \ - #fn, tmp); \ + dev_err(dev, "%s : %s error %d\n", \ + rsnd_mod_name(mod), #fn, tmp); \ ret |= tmp; \ } \ ret; \ @@ -512,8 +577,8 @@ int rsnd_dai_connect(struct rsnd_mod *mod, io->mod[type] = mod; - dev_dbg(dev, "%s[%d] is connected to io (%s)\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), + dev_dbg(dev, "%s is connected to io (%s)\n", + rsnd_mod_name(mod), rsnd_io_is_play(io) ? "Playback" : "Capture"); return 0; @@ -750,6 +815,7 @@ static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai, switch (slots) { case 2: + /* TDM Split Mode */ case 6: case 8: /* TDM Extend Mode */ @@ -965,6 +1031,82 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { .prepare = rsnd_soc_dai_prepare, }; +static void rsnd_parse_connect_simple(struct rsnd_priv *priv, + struct device_node *dai_np, + int dai_i, int is_play) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_dai *rdai = rsnd_rdai_get(priv, dai_i); + struct rsnd_dai_stream *io = is_play ? + &rdai->playback : + &rdai->capture; + struct device_node *ssiu_np = rsnd_ssiu_of_node(priv); + struct device_node *np; + int i, j; + + if (!ssiu_np) + return; + + if (!rsnd_io_to_mod_ssi(io)) + return; + + /* + * This driver assumes that it is TDM Split mode + * if it includes ssiu node + */ + for (i = 0;; i++) { + struct device_node *node = is_play ? + of_parse_phandle(dai_np, "playback", i) : + of_parse_phandle(dai_np, "capture", i); + + if (!node) + break; + + j = 0; + for_each_child_of_node(ssiu_np, np) { + if (np == node) { + rsnd_flags_set(io, RSND_STREAM_TDM_SPLIT); + dev_dbg(dev, "%s is part of TDM Split\n", io->name); + } + j++; + } + + } +} + +static void rsnd_parse_connect_graph(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + struct device_node *endpoint) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct device_node *remote_port = of_graph_get_remote_port(endpoint); + struct device_node *remote_node = of_graph_get_remote_port_parent(endpoint); + + if (!rsnd_io_to_mod_ssi(io)) + return; + + /* HDMI0 */ + if (strstr(remote_node->full_name, "hdmi@fead0000")) { + rsnd_flags_set(io, RSND_STREAM_HDMI0); + dev_dbg(dev, "%s connected to HDMI0\n", io->name); + } + + /* HDMI1 */ + if (strstr(remote_node->full_name, "hdmi@feae0000")) { + rsnd_flags_set(io, RSND_STREAM_HDMI1); + dev_dbg(dev, "%s connected to HDMI1\n", io->name); + } + + /* + * This driver assumes that it is TDM Split mode + * if remote node has multi endpoint + */ + if (of_get_child_count(remote_port) > 1) { + rsnd_flags_set(io, RSND_STREAM_TDM_SPLIT); + dev_dbg(dev, "%s is part of TDM Split\n", io->name); + } +} + void rsnd_parse_connect_common(struct rsnd_dai *rdai, struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id), struct device_node *node, @@ -1051,24 +1193,24 @@ static void __rsnd_dai_probe(struct rsnd_priv *priv, drv->name = rdai->name; drv->ops = &rsnd_soc_dai_ops; - snprintf(rdai->playback.name, RSND_DAI_NAME_SIZE, + snprintf(io_playback->name, RSND_DAI_NAME_SIZE, "DAI%d Playback", dai_i); drv->playback.rates = RSND_RATES; drv->playback.formats = RSND_FMTS; drv->playback.channels_min = 2; drv->playback.channels_max = 8; - drv->playback.stream_name = rdai->playback.name; + drv->playback.stream_name = io_playback->name; - snprintf(rdai->capture.name, RSND_DAI_NAME_SIZE, + snprintf(io_capture->name, RSND_DAI_NAME_SIZE, "DAI%d Capture", dai_i); drv->capture.rates = RSND_RATES; drv->capture.formats = RSND_FMTS; drv->capture.channels_min = 2; drv->capture.channels_max = 8; - drv->capture.stream_name = rdai->capture.name; + drv->capture.stream_name = io_capture->name; - rdai->playback.rdai = rdai; - rdai->capture.rdai = rdai; + io_playback->rdai = rdai; + io_capture->rdai = rdai; rsnd_rdai_channels_set(rdai, 2); /* default 2ch */ rsnd_rdai_ssi_lane_set(rdai, 1); /* default 1lane */ rsnd_rdai_width_set(rdai, 32); /* default 32bit width */ @@ -1081,6 +1223,7 @@ static void __rsnd_dai_probe(struct rsnd_priv *priv, break; rsnd_parse_connect_ssi(rdai, playback, capture); + rsnd_parse_connect_ssiu(rdai, playback, capture); rsnd_parse_connect_src(rdai, playback, capture); rsnd_parse_connect_ctu(rdai, playback, capture); rsnd_parse_connect_mix(rdai, playback, capture); @@ -1137,12 +1280,23 @@ static int rsnd_dai_probe(struct rsnd_priv *priv) if (is_graph) { for_each_endpoint_of_node(dai_node, dai_np) { __rsnd_dai_probe(priv, dai_np, dai_i); - rsnd_ssi_parse_hdmi_connection(priv, dai_np, dai_i); + if (rsnd_is_gen3(priv)) { + struct rsnd_dai *rdai = rsnd_rdai_get(priv, dai_i); + + rsnd_parse_connect_graph(priv, &rdai->playback, dai_np); + rsnd_parse_connect_graph(priv, &rdai->capture, dai_np); + } dai_i++; } } else { - for_each_child_of_node(dai_node, dai_np) - __rsnd_dai_probe(priv, dai_np, dai_i++); + for_each_child_of_node(dai_node, dai_np) { + __rsnd_dai_probe(priv, dai_np, dai_i); + if (rsnd_is_gen3(priv)) { + rsnd_parse_connect_simple(priv, dai_np, dai_i, 1); + rsnd_parse_connect_simple(priv, dai_np, dai_i, 0); + } + dai_i++; + } } return 0; @@ -1157,8 +1311,40 @@ static int rsnd_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai = rsnd_substream_to_dai(substream); struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); + struct snd_soc_pcm_runtime *fe = substream->private_data; int ret; + /* + * rsnd assumes that it might be used under DPCM if user want to use + * channel / rate convert. Then, rsnd should be FE. + * And then, this function will be called *after* BE settings. + * this means, each BE already has fixuped hw_params. + * see + * dpcm_fe_dai_hw_params() + * dpcm_be_dai_hw_params() + */ + io->converted_rate = 0; + io->converted_chan = 0; + if (fe->dai_link->dynamic) { + struct rsnd_priv *priv = rsnd_io_to_priv(io); + struct device *dev = rsnd_priv_to_dev(priv); + struct snd_soc_dpcm *dpcm; + struct snd_pcm_hw_params *be_params; + int stream = substream->stream; + + for_each_dpcm_be(fe, stream, dpcm) { + be_params = &dpcm->hw_params; + if (params_channels(hw_params) != params_channels(be_params)) + io->converted_chan = params_channels(be_params); + if (params_rate(hw_params) != params_rate(be_params)) + io->converted_rate = params_rate(be_params); + } + if (io->converted_chan) + dev_dbg(dev, "convert channels = %d\n", io->converted_chan); + if (io->converted_rate) + dev_dbg(dev, "convert rate = %d\n", io->converted_rate); + } + ret = rsnd_dai_call(hw_params, io, substream, hw_params); if (ret) return ret; @@ -1339,6 +1525,18 @@ int rsnd_kctrl_new(struct rsnd_mod *mod, }; int ret; + /* + * 1) Avoid duplicate register (ex. MIXer case) + * 2) re-register if card was rebinded + */ + list_for_each_entry(kctrl, &card->controls, list) { + struct rsnd_kctrl_cfg *c = kctrl->private_data; + + if (strcmp(kctrl->id.name, name) == 0 && + c->mod == mod) + return 0; + } + if (size > RSND_MAX_CHANNELS) return -EINVAL; diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index ad702377a6c3..8cb06dab234e 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -72,10 +72,7 @@ struct rsnd_ctu { struct rsnd_mod mod; struct rsnd_kctrl_cfg_m pass; - struct rsnd_kctrl_cfg_m sv0; - struct rsnd_kctrl_cfg_m sv1; - struct rsnd_kctrl_cfg_m sv2; - struct rsnd_kctrl_cfg_m sv3; + struct rsnd_kctrl_cfg_m sv[4]; struct rsnd_kctrl_cfg_s reset; int channels; u32 flags; @@ -107,13 +104,6 @@ static void rsnd_ctu_halt(struct rsnd_mod *mod) rsnd_mod_write(mod, CTU_SWRSR, 0); } -int rsnd_ctu_converted_channel(struct rsnd_mod *mod) -{ - struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); - - return ctu->channels; -} - static int rsnd_ctu_probe_(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) @@ -127,7 +117,7 @@ static void rsnd_ctu_value_init(struct rsnd_dai_stream *io, struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); u32 cpmdr = 0; u32 scmdr = 0; - int i; + int i, j; for (i = 0; i < RSND_MAX_CHANNELS; i++) { u32 val = rsnd_kctrl_valm(ctu->pass, i); @@ -146,45 +136,13 @@ static void rsnd_ctu_value_init(struct rsnd_dai_stream *io, rsnd_mod_write(mod, CTU_SCMDR, scmdr); - if (scmdr > 0) { - rsnd_mod_write(mod, CTU_SV00R, rsnd_kctrl_valm(ctu->sv0, 0)); - rsnd_mod_write(mod, CTU_SV01R, rsnd_kctrl_valm(ctu->sv0, 1)); - rsnd_mod_write(mod, CTU_SV02R, rsnd_kctrl_valm(ctu->sv0, 2)); - rsnd_mod_write(mod, CTU_SV03R, rsnd_kctrl_valm(ctu->sv0, 3)); - rsnd_mod_write(mod, CTU_SV04R, rsnd_kctrl_valm(ctu->sv0, 4)); - rsnd_mod_write(mod, CTU_SV05R, rsnd_kctrl_valm(ctu->sv0, 5)); - rsnd_mod_write(mod, CTU_SV06R, rsnd_kctrl_valm(ctu->sv0, 6)); - rsnd_mod_write(mod, CTU_SV07R, rsnd_kctrl_valm(ctu->sv0, 7)); - } - if (scmdr > 1) { - rsnd_mod_write(mod, CTU_SV10R, rsnd_kctrl_valm(ctu->sv1, 0)); - rsnd_mod_write(mod, CTU_SV11R, rsnd_kctrl_valm(ctu->sv1, 1)); - rsnd_mod_write(mod, CTU_SV12R, rsnd_kctrl_valm(ctu->sv1, 2)); - rsnd_mod_write(mod, CTU_SV13R, rsnd_kctrl_valm(ctu->sv1, 3)); - rsnd_mod_write(mod, CTU_SV14R, rsnd_kctrl_valm(ctu->sv1, 4)); - rsnd_mod_write(mod, CTU_SV15R, rsnd_kctrl_valm(ctu->sv1, 5)); - rsnd_mod_write(mod, CTU_SV16R, rsnd_kctrl_valm(ctu->sv1, 6)); - rsnd_mod_write(mod, CTU_SV17R, rsnd_kctrl_valm(ctu->sv1, 7)); - } - if (scmdr > 2) { - rsnd_mod_write(mod, CTU_SV20R, rsnd_kctrl_valm(ctu->sv2, 0)); - rsnd_mod_write(mod, CTU_SV21R, rsnd_kctrl_valm(ctu->sv2, 1)); - rsnd_mod_write(mod, CTU_SV22R, rsnd_kctrl_valm(ctu->sv2, 2)); - rsnd_mod_write(mod, CTU_SV23R, rsnd_kctrl_valm(ctu->sv2, 3)); - rsnd_mod_write(mod, CTU_SV24R, rsnd_kctrl_valm(ctu->sv2, 4)); - rsnd_mod_write(mod, CTU_SV25R, rsnd_kctrl_valm(ctu->sv2, 5)); - rsnd_mod_write(mod, CTU_SV26R, rsnd_kctrl_valm(ctu->sv2, 6)); - rsnd_mod_write(mod, CTU_SV27R, rsnd_kctrl_valm(ctu->sv2, 7)); - } - if (scmdr > 3) { - rsnd_mod_write(mod, CTU_SV30R, rsnd_kctrl_valm(ctu->sv3, 0)); - rsnd_mod_write(mod, CTU_SV31R, rsnd_kctrl_valm(ctu->sv3, 1)); - rsnd_mod_write(mod, CTU_SV32R, rsnd_kctrl_valm(ctu->sv3, 2)); - rsnd_mod_write(mod, CTU_SV33R, rsnd_kctrl_valm(ctu->sv3, 3)); - rsnd_mod_write(mod, CTU_SV34R, rsnd_kctrl_valm(ctu->sv3, 4)); - rsnd_mod_write(mod, CTU_SV35R, rsnd_kctrl_valm(ctu->sv3, 5)); - rsnd_mod_write(mod, CTU_SV36R, rsnd_kctrl_valm(ctu->sv3, 6)); - rsnd_mod_write(mod, CTU_SV37R, rsnd_kctrl_valm(ctu->sv3, 7)); + for (i = 0; i < 4; i++) { + + if (i >= scmdr) + break; + + for (j = 0; j < RSND_MAX_CHANNELS; j++) + rsnd_mod_write(mod, CTU_SVxxR(i, j), rsnd_kctrl_valm(ctu->sv[i], j)); } rsnd_mod_write(mod, CTU_CTUIR, 0); @@ -201,10 +159,10 @@ static void rsnd_ctu_value_reset(struct rsnd_dai_stream *io, for (i = 0; i < RSND_MAX_CHANNELS; i++) { rsnd_kctrl_valm(ctu->pass, i) = 0; - rsnd_kctrl_valm(ctu->sv0, i) = 0; - rsnd_kctrl_valm(ctu->sv1, i) = 0; - rsnd_kctrl_valm(ctu->sv2, i) = 0; - rsnd_kctrl_valm(ctu->sv3, i) = 0; + rsnd_kctrl_valm(ctu->sv[0], i) = 0; + rsnd_kctrl_valm(ctu->sv[1], i) = 0; + rsnd_kctrl_valm(ctu->sv[2], i) = 0; + rsnd_kctrl_valm(ctu->sv[3], i) = 0; } rsnd_kctrl_vals(ctu->reset) = 0; } @@ -233,43 +191,6 @@ static int rsnd_ctu_quit(struct rsnd_mod *mod, return 0; } -static int rsnd_ctu_hw_params(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *fe_params) -{ - struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); - struct snd_soc_pcm_runtime *fe = substream->private_data; - - /* - * CTU assumes that it is used under DPCM if user want to use - * channel transfer. Then, CTU should be FE. - * And then, this function will be called *after* BE settings. - * this means, each BE already has fixuped hw_params. - * see - * dpcm_fe_dai_hw_params() - * dpcm_be_dai_hw_params() - */ - ctu->channels = 0; - if (fe->dai_link->dynamic) { - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); - struct snd_soc_dpcm *dpcm; - struct snd_pcm_hw_params *be_params; - int stream = substream->stream; - - for_each_dpcm_be(fe, stream, dpcm) { - be_params = &dpcm->hw_params; - if (params_channels(fe_params) != params_channels(be_params)) - ctu->channels = params_channels(be_params); - } - - dev_dbg(dev, "CTU convert channels %d\n", ctu->channels); - } - - return 0; -} - static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd) @@ -291,7 +212,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV0", rsnd_kctrl_accept_anytime, NULL, - &ctu->sv0, RSND_MAX_CHANNELS, + &ctu->sv[0], RSND_MAX_CHANNELS, 0x00FFFFFF); if (ret < 0) return ret; @@ -300,7 +221,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV1", rsnd_kctrl_accept_anytime, NULL, - &ctu->sv1, RSND_MAX_CHANNELS, + &ctu->sv[1], RSND_MAX_CHANNELS, 0x00FFFFFF); if (ret < 0) return ret; @@ -309,7 +230,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV2", rsnd_kctrl_accept_anytime, NULL, - &ctu->sv2, RSND_MAX_CHANNELS, + &ctu->sv[2], RSND_MAX_CHANNELS, 0x00FFFFFF); if (ret < 0) return ret; @@ -318,7 +239,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV3", rsnd_kctrl_accept_anytime, NULL, - &ctu->sv3, RSND_MAX_CHANNELS, + &ctu->sv[3], RSND_MAX_CHANNELS, 0x00FFFFFF); if (ret < 0) return ret; @@ -334,13 +255,34 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, return ret; } +static int rsnd_ctu_id(struct rsnd_mod *mod) +{ + /* + * ctu00: -> 0, ctu01: -> 0, ctu02: -> 0, ctu03: -> 0 + * ctu10: -> 1, ctu11: -> 1, ctu12: -> 1, ctu13: -> 1 + */ + return mod->id / 4; +} + +static int rsnd_ctu_id_sub(struct rsnd_mod *mod) +{ + /* + * ctu00: -> 0, ctu01: -> 1, ctu02: -> 2, ctu03: -> 3 + * ctu10: -> 0, ctu11: -> 1, ctu12: -> 2, ctu13: -> 3 + */ + return mod->id % 4; +} + static struct rsnd_mod_ops rsnd_ctu_ops = { .name = CTU_NAME, .probe = rsnd_ctu_probe_, .init = rsnd_ctu_init, .quit = rsnd_ctu_quit, - .hw_params = rsnd_ctu_hw_params, .pcm_new = rsnd_ctu_pcm_new, + .get_status = rsnd_mod_get_status, + .id = rsnd_ctu_id, + .id_sub = rsnd_ctu_id_sub, + .id_cmd = rsnd_mod_id_raw, }; struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id) @@ -404,7 +346,7 @@ int rsnd_ctu_probe(struct rsnd_priv *priv) } ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops, - clk, rsnd_mod_get_status, RSND_MOD_CTU, i); + clk, RSND_MOD_CTU, i); if (ret) { of_node_put(np); goto rsnd_ctu_probe_done; diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index 6d1947515dc8..0324a5c39619 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -174,8 +174,8 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod, 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), + dev_dbg(dev, "%s %pad -> %pad\n", + rsnd_mod_name(mod), &cfg.src_addr, &cfg.dst_addr); ret = dmaengine_slave_config(dmaen->chan, &cfg); @@ -218,7 +218,7 @@ struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, int i = 0; for_each_child_of_node(of_node, np) { - if (i == rsnd_mod_id(mod) && (!chan)) + if (i == rsnd_mod_id_raw(mod) && (!chan)) chan = of_dma_request_slave_channel(np, name); i++; } @@ -289,12 +289,13 @@ static int rsnd_dmaen_pointer(struct rsnd_mod *mod, } static struct rsnd_mod_ops rsnd_dmaen_ops = { - .name = "audmac", - .prepare = rsnd_dmaen_prepare, - .cleanup = rsnd_dmaen_cleanup, - .start = rsnd_dmaen_start, - .stop = rsnd_dmaen_stop, - .pointer= rsnd_dmaen_pointer, + .name = "audmac", + .prepare = rsnd_dmaen_prepare, + .cleanup = rsnd_dmaen_cleanup, + .start = rsnd_dmaen_start, + .stop = rsnd_dmaen_stop, + .pointer = rsnd_dmaen_pointer, + .get_status = rsnd_mod_get_status, }; /* @@ -343,14 +344,16 @@ static u32 rsnd_dmapp_get_id(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); + struct rsnd_mod *ssiu = rsnd_io_to_mod_ssiu(io); struct rsnd_mod *src = rsnd_io_to_mod_src(io); struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); const u8 *entry = NULL; int id = 255; int size = 0; - if (mod == ssi) { - int busif = rsnd_ssi_get_busif(io); + if ((mod == ssi) || + (mod == ssiu)) { + int busif = rsnd_mod_id_sub(ssiu); entry = gen2_id_table_ssiu; size = ARRAY_SIZE(gen2_id_table_ssiu); @@ -368,8 +371,7 @@ static u32 rsnd_dmapp_get_id(struct rsnd_dai_stream *io, if ((!entry) || (size <= id)) { struct device *dev = rsnd_priv_to_dev(rsnd_io_to_priv(io)); - dev_err(dev, "unknown connection (%s[%d])\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); + dev_err(dev, "unknown connection (%s)\n", rsnd_mod_name(mod)); /* use non-prohibited SRS number as error */ return 0x00; /* SSI00 */ @@ -477,10 +479,11 @@ static int rsnd_dmapp_attach(struct rsnd_dai_stream *io, } static struct rsnd_mod_ops rsnd_dmapp_ops = { - .name = "audmac-pp", - .start = rsnd_dmapp_start, - .stop = rsnd_dmapp_stop, - .quit = rsnd_dmapp_stop, + .name = "audmac-pp", + .start = rsnd_dmapp_start, + .stop = rsnd_dmapp_stop, + .quit = rsnd_dmapp_stop, + .get_status = rsnd_mod_get_status, }; /* @@ -529,13 +532,14 @@ rsnd_gen2_dma_addr(struct rsnd_dai_stream *io, struct device *dev = rsnd_priv_to_dev(priv); phys_addr_t ssi_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SSI); phys_addr_t src_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SCU); - int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod); + int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod) || + !!(rsnd_io_to_mod_ssiu(io) == mod); int use_src = !!rsnd_io_to_mod_src(io); int use_cmd = !!rsnd_io_to_mod_dvc(io) || !!rsnd_io_to_mod_mix(io) || !!rsnd_io_to_mod_ctu(io); int id = rsnd_mod_id(mod); - int busif = rsnd_ssi_get_busif(io); + int busif = rsnd_mod_id_sub(rsnd_io_to_mod_ssiu(io)); struct dma_addr { dma_addr_t out_addr; dma_addr_t in_addr; @@ -619,7 +623,7 @@ static void rsnd_dma_of_path(struct rsnd_mod *this, struct rsnd_mod **mod_from, struct rsnd_mod **mod_to) { - struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); + struct rsnd_mod *ssi; struct rsnd_mod *src = rsnd_io_to_mod_src(io); struct rsnd_mod *ctu = rsnd_io_to_mod_ctu(io); struct rsnd_mod *mix = rsnd_io_to_mod_mix(io); @@ -630,6 +634,28 @@ static void rsnd_dma_of_path(struct rsnd_mod *this, struct device *dev = rsnd_priv_to_dev(priv); int nr, i, idx; + /* + * It should use "rcar_sound,ssiu" on DT. + * But, we need to keep compatibility for old version. + * + * If it has "rcar_sound.ssiu", it will be used. + * If not, "rcar_sound.ssi" will be used. + * see + * rsnd_ssiu_dma_req() + * rsnd_ssi_dma_req() + */ + if (rsnd_ssiu_of_node(priv)) { + struct rsnd_mod *ssiu = rsnd_io_to_mod_ssiu(io); + + /* use SSIU */ + ssi = ssiu; + if (this == rsnd_io_to_mod_ssi(io)) + this = ssiu; + } else { + /* keep compatible, use SSI */ + ssi = rsnd_io_to_mod_ssi(io); + } + if (!ssi) return; @@ -690,12 +716,10 @@ static void rsnd_dma_of_path(struct rsnd_mod *this, *mod_to = mod[1]; } - dev_dbg(dev, "module connection (this is %s[%d])\n", - rsnd_mod_name(this), rsnd_mod_id(this)); + dev_dbg(dev, "module connection (this is %s)\n", rsnd_mod_name(this)); for (i = 0; i <= idx; i++) { - dev_dbg(dev, " %s[%d]%s\n", + dev_dbg(dev, " %s%s\n", rsnd_mod_name(mod[i] ? mod[i] : &mem), - rsnd_mod_id (mod[i] ? mod[i] : &mem), (mod[i] == *mod_from) ? " from" : (mod[i] == *mod_to) ? " to" : ""); } @@ -756,16 +780,14 @@ static int rsnd_dma_alloc(struct rsnd_dai_stream *io, struct rsnd_mod *mod, *dma_mod = rsnd_mod_get(dma); ret = rsnd_mod_init(priv, *dma_mod, ops, NULL, - rsnd_mod_get_status, type, dma_id); + type, dma_id); if (ret < 0) return ret; - dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n", - rsnd_mod_name(*dma_mod), rsnd_mod_id(*dma_mod), + dev_dbg(dev, "%s %s -> %s\n", + rsnd_mod_name(*dma_mod), rsnd_mod_name(mod_from ? mod_from : &mem), - rsnd_mod_id (mod_from ? mod_from : &mem), - rsnd_mod_name(mod_to ? mod_to : &mem), - rsnd_mod_id (mod_to ? mod_to : &mem)); + rsnd_mod_name(mod_to ? mod_to : &mem)); ret = attach(io, dma, mod_from, mod_to); if (ret < 0) @@ -823,5 +845,5 @@ int rsnd_dma_probe(struct rsnd_priv *priv) priv->dma = dmac; /* dummy mem mod for debug */ - return rsnd_mod_init(NULL, &mem, &mem_ops, NULL, NULL, 0, 0); + return rsnd_mod_init(NULL, &mem, &mem_ops, NULL, 0, 0); } diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index 2b16e0ce6bc5..8d91c0eb0880 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -40,11 +40,8 @@ struct rsnd_dvc { struct rsnd_kctrl_cfg_s ren; /* Ramp Enable */ struct rsnd_kctrl_cfg_s rup; /* Ramp Rate Up */ struct rsnd_kctrl_cfg_s rdown; /* Ramp Rate Down */ - u32 flags; }; -#define KCTRL_INITIALIZED (1 << 0) - #define rsnd_dvc_get(priv, id) ((struct rsnd_dvc *)(priv->dvc) + id) #define rsnd_dvc_nr(priv) ((priv)->dvc_nr) @@ -89,14 +86,8 @@ static void rsnd_dvc_volume_parameter(struct rsnd_dai_stream *io, val[i] = rsnd_kctrl_valm(dvc->volume, i); /* Enable Digital Volume */ - rsnd_mod_write(mod, DVC_VOL0R, val[0]); - rsnd_mod_write(mod, DVC_VOL1R, val[1]); - rsnd_mod_write(mod, DVC_VOL2R, val[2]); - rsnd_mod_write(mod, DVC_VOL3R, val[3]); - rsnd_mod_write(mod, DVC_VOL4R, val[4]); - rsnd_mod_write(mod, DVC_VOL5R, val[5]); - rsnd_mod_write(mod, DVC_VOL6R, val[6]); - rsnd_mod_write(mod, DVC_VOL7R, val[7]); + for (i = 0; i < RSND_MAX_CHANNELS; i++) + rsnd_mod_write(mod, DVC_VOLxR(i), val[i]); } static void rsnd_dvc_volume_init(struct rsnd_dai_stream *io, @@ -227,9 +218,6 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, int channels = rsnd_rdai_channels_get(rdai); int ret; - if (rsnd_flags_has(dvc, KCTRL_INITIALIZED)) - return 0; - /* Volume */ ret = rsnd_kctrl_new_m(mod, io, rtd, is_play ? @@ -285,8 +273,6 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, if (ret < 0) return ret; - rsnd_flags_set(dvc, KCTRL_INITIALIZED); - return 0; } @@ -306,6 +292,7 @@ static struct rsnd_mod_ops rsnd_dvc_ops = { .init = rsnd_dvc_init, .quit = rsnd_dvc_quit, .pcm_new = rsnd_dvc_pcm_new, + .get_status = rsnd_mod_get_status, }; struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id) @@ -365,7 +352,7 @@ int rsnd_dvc_probe(struct rsnd_priv *priv) } ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops, - clk, rsnd_mod_get_status, RSND_MOD_DVC, i); + clk, RSND_MOD_DVC, i); if (ret) { of_node_put(np); goto rsnd_dvc_probe_done; diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 1f7881cc16b2..7cda60188f41 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -26,8 +26,8 @@ struct rsnd_gen { struct regmap *regmap[RSND_BASE_MAX]; /* RSND_REG_MAX base */ - struct regmap_field *regs[RSND_REG_MAX]; - const char *reg_name[RSND_REG_MAX]; + struct regmap_field *regs[REG_MAX]; + const char *reg_name[REG_MAX]; }; #define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen) @@ -49,11 +49,11 @@ struct rsnd_regmap_field_conf { } /* single address mapping */ #define RSND_GEN_S_REG(id, offset) \ - RSND_REG_SET(RSND_REG_##id, offset, 0, #id) + RSND_REG_SET(id, offset, 0, #id) /* multi address mapping */ #define RSND_GEN_M_REG(id, offset, _id_offset) \ - RSND_REG_SET(RSND_REG_##id, offset, _id_offset, #id) + RSND_REG_SET(id, offset, _id_offset, #id) /* * basic function @@ -71,9 +71,17 @@ static int rsnd_is_accessible_reg(struct rsnd_priv *priv, return 1; } -u32 rsnd_read(struct rsnd_priv *priv, - struct rsnd_mod *mod, enum rsnd_reg reg) +static int rsnd_mod_id_cmd(struct rsnd_mod *mod) { + if (mod->ops->id_cmd) + return mod->ops->id_cmd(mod); + + return rsnd_mod_id(mod); +} + +u32 rsnd_mod_read(struct rsnd_mod *mod, enum rsnd_reg reg) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_gen *gen = rsnd_priv_to_gen(priv); u32 val; @@ -81,35 +89,36 @@ u32 rsnd_read(struct rsnd_priv *priv, if (!rsnd_is_accessible_reg(priv, gen, reg)) return 0; - regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val); + regmap_fields_read(gen->regs[reg], rsnd_mod_id_cmd(mod), &val); - dev_dbg(dev, "r %s[%d] - %-18s (%4d) : %08x\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), + dev_dbg(dev, "r %s - %-18s (%4d) : %08x\n", + rsnd_mod_name(mod), rsnd_reg_name(gen, reg), reg, val); return val; } -void rsnd_write(struct rsnd_priv *priv, - struct rsnd_mod *mod, - enum rsnd_reg reg, u32 data) +void rsnd_mod_write(struct rsnd_mod *mod, + enum rsnd_reg reg, u32 data) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_gen *gen = rsnd_priv_to_gen(priv); if (!rsnd_is_accessible_reg(priv, gen, reg)) return; - regmap_fields_force_write(gen->regs[reg], rsnd_mod_id(mod), data); + regmap_fields_force_write(gen->regs[reg], rsnd_mod_id_cmd(mod), data); - dev_dbg(dev, "w %s[%d] - %-18s (%4d) : %08x\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), + dev_dbg(dev, "w %s - %-18s (%4d) : %08x\n", + rsnd_mod_name(mod), rsnd_reg_name(gen, reg), reg, data); } -void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, - enum rsnd_reg reg, u32 mask, u32 data) +void rsnd_mod_bset(struct rsnd_mod *mod, + enum rsnd_reg reg, u32 mask, u32 data) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_gen *gen = rsnd_priv_to_gen(priv); @@ -117,10 +126,10 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, return; regmap_fields_force_update_bits(gen->regs[reg], - rsnd_mod_id(mod), mask, data); + rsnd_mod_id_cmd(mod), mask, data); - dev_dbg(dev, "b %s[%d] - %-18s (%4d) : %08x/%08x\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), + dev_dbg(dev, "b %s - %-18s (%4d) : %08x/%08x\n", + rsnd_mod_name(mod), rsnd_reg_name(gen, reg), reg, data, mask); } diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c index 8e3b57eaa708..a3e0370f5704 100644 --- a/sound/soc/sh/rcar/mix.c +++ b/sound/soc/sh/rcar/mix.c @@ -256,6 +256,7 @@ static struct rsnd_mod_ops rsnd_mix_ops = { .init = rsnd_mix_init, .quit = rsnd_mix_quit, .pcm_new = rsnd_mix_pcm_new, + .get_status = rsnd_mod_get_status, }; struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id) @@ -315,7 +316,7 @@ int rsnd_mix_probe(struct rsnd_priv *priv) } ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops, - clk, rsnd_mod_get_status, RSND_MOD_MIX, i); + clk, RSND_MOD_MIX, i); if (ret) { of_node_put(np); goto rsnd_mix_probe_done; diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 4464d1d0a042..605e4b934982 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -42,165 +42,175 @@ */ enum rsnd_reg { /* SCU (MIX/CTU/DVC) */ - RSND_REG_SRC_I_BUSIF_MODE, - RSND_REG_SRC_O_BUSIF_MODE, - RSND_REG_SRC_ROUTE_MODE0, - RSND_REG_SRC_SWRSR, - RSND_REG_SRC_SRCIR, - RSND_REG_SRC_ADINR, - RSND_REG_SRC_IFSCR, - RSND_REG_SRC_IFSVR, - RSND_REG_SRC_SRCCR, - 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, - RSND_REG_SCU_SYS_INT_EN0, - RSND_REG_SCU_SYS_INT_EN1, - RSND_REG_CMD_CTRL, - RSND_REG_CMD_BUSIF_MODE, - RSND_REG_CMD_BUSIF_DALIGN, - RSND_REG_CMD_ROUTE_SLCT, - RSND_REG_CMDOUT_TIMSEL, - RSND_REG_CTU_SWRSR, - RSND_REG_CTU_CTUIR, - RSND_REG_CTU_ADINR, - RSND_REG_CTU_CPMDR, - RSND_REG_CTU_SCMDR, - RSND_REG_CTU_SV00R, - RSND_REG_CTU_SV01R, - RSND_REG_CTU_SV02R, - RSND_REG_CTU_SV03R, - RSND_REG_CTU_SV04R, - RSND_REG_CTU_SV05R, - RSND_REG_CTU_SV06R, - RSND_REG_CTU_SV07R, - RSND_REG_CTU_SV10R, - RSND_REG_CTU_SV11R, - RSND_REG_CTU_SV12R, - RSND_REG_CTU_SV13R, - RSND_REG_CTU_SV14R, - RSND_REG_CTU_SV15R, - RSND_REG_CTU_SV16R, - RSND_REG_CTU_SV17R, - RSND_REG_CTU_SV20R, - RSND_REG_CTU_SV21R, - RSND_REG_CTU_SV22R, - RSND_REG_CTU_SV23R, - RSND_REG_CTU_SV24R, - RSND_REG_CTU_SV25R, - RSND_REG_CTU_SV26R, - RSND_REG_CTU_SV27R, - RSND_REG_CTU_SV30R, - RSND_REG_CTU_SV31R, - RSND_REG_CTU_SV32R, - RSND_REG_CTU_SV33R, - RSND_REG_CTU_SV34R, - RSND_REG_CTU_SV35R, - RSND_REG_CTU_SV36R, - RSND_REG_CTU_SV37R, - RSND_REG_MIX_SWRSR, - RSND_REG_MIX_MIXIR, - RSND_REG_MIX_ADINR, - RSND_REG_MIX_MIXMR, - RSND_REG_MIX_MVPDR, - RSND_REG_MIX_MDBAR, - RSND_REG_MIX_MDBBR, - RSND_REG_MIX_MDBCR, - RSND_REG_MIX_MDBDR, - RSND_REG_MIX_MDBER, - RSND_REG_DVC_SWRSR, - RSND_REG_DVC_DVUIR, - RSND_REG_DVC_ADINR, - RSND_REG_DVC_DVUCR, - RSND_REG_DVC_ZCMCR, - RSND_REG_DVC_VOL0R, - RSND_REG_DVC_VOL1R, - RSND_REG_DVC_VOL2R, - RSND_REG_DVC_VOL3R, - RSND_REG_DVC_VOL4R, - RSND_REG_DVC_VOL5R, - RSND_REG_DVC_VOL6R, - RSND_REG_DVC_VOL7R, - RSND_REG_DVC_DVUER, - RSND_REG_DVC_VRCTR, - RSND_REG_DVC_VRPDR, - RSND_REG_DVC_VRDBR, + SRC_I_BUSIF_MODE, + SRC_O_BUSIF_MODE, + SRC_ROUTE_MODE0, + SRC_SWRSR, + SRC_SRCIR, + SRC_ADINR, + SRC_IFSCR, + SRC_IFSVR, + SRC_SRCCR, + SRC_CTRL, + SRC_BSDSR, + SRC_BSISR, + SRC_INT_ENABLE0, + SRC_BUSIF_DALIGN, + SRCIN_TIMSEL0, + SRCIN_TIMSEL1, + SRCIN_TIMSEL2, + SRCIN_TIMSEL3, + SRCIN_TIMSEL4, + SRCOUT_TIMSEL0, + SRCOUT_TIMSEL1, + SRCOUT_TIMSEL2, + SRCOUT_TIMSEL3, + SRCOUT_TIMSEL4, + SCU_SYS_STATUS0, + SCU_SYS_STATUS1, + SCU_SYS_INT_EN0, + SCU_SYS_INT_EN1, + CMD_CTRL, + CMD_BUSIF_MODE, + CMD_BUSIF_DALIGN, + CMD_ROUTE_SLCT, + CMDOUT_TIMSEL, + CTU_SWRSR, + CTU_CTUIR, + CTU_ADINR, + CTU_CPMDR, + CTU_SCMDR, + CTU_SV00R, + CTU_SV01R, + CTU_SV02R, + CTU_SV03R, + CTU_SV04R, + CTU_SV05R, + CTU_SV06R, + CTU_SV07R, + CTU_SV10R, + CTU_SV11R, + CTU_SV12R, + CTU_SV13R, + CTU_SV14R, + CTU_SV15R, + CTU_SV16R, + CTU_SV17R, + CTU_SV20R, + CTU_SV21R, + CTU_SV22R, + CTU_SV23R, + CTU_SV24R, + CTU_SV25R, + CTU_SV26R, + CTU_SV27R, + CTU_SV30R, + CTU_SV31R, + CTU_SV32R, + CTU_SV33R, + CTU_SV34R, + CTU_SV35R, + CTU_SV36R, + CTU_SV37R, + MIX_SWRSR, + MIX_MIXIR, + MIX_ADINR, + MIX_MIXMR, + MIX_MVPDR, + MIX_MDBAR, + MIX_MDBBR, + MIX_MDBCR, + MIX_MDBDR, + MIX_MDBER, + DVC_SWRSR, + DVC_DVUIR, + DVC_ADINR, + DVC_DVUCR, + DVC_ZCMCR, + DVC_VOL0R, + DVC_VOL1R, + DVC_VOL2R, + DVC_VOL3R, + DVC_VOL4R, + DVC_VOL5R, + DVC_VOL6R, + DVC_VOL7R, + DVC_DVUER, + DVC_VRCTR, + DVC_VRPDR, + DVC_VRDBR, /* ADG */ - RSND_REG_BRRA, - RSND_REG_BRRB, - RSND_REG_BRGCKR, - RSND_REG_DIV_EN, - RSND_REG_AUDIO_CLK_SEL0, - RSND_REG_AUDIO_CLK_SEL1, - RSND_REG_AUDIO_CLK_SEL2, + BRRA, + BRRB, + BRGCKR, + DIV_EN, + AUDIO_CLK_SEL0, + AUDIO_CLK_SEL1, + 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_BUSIF0_MODE, - RSND_REG_SSI_BUSIF0_ADINR, - RSND_REG_SSI_BUSIF0_DALIGN, - RSND_REG_SSI_BUSIF1_MODE, - RSND_REG_SSI_BUSIF1_ADINR, - RSND_REG_SSI_BUSIF1_DALIGN, - RSND_REG_SSI_BUSIF2_MODE, - RSND_REG_SSI_BUSIF2_ADINR, - RSND_REG_SSI_BUSIF2_DALIGN, - RSND_REG_SSI_BUSIF3_MODE, - RSND_REG_SSI_BUSIF3_ADINR, - RSND_REG_SSI_BUSIF3_DALIGN, - RSND_REG_SSI_BUSIF4_MODE, - RSND_REG_SSI_BUSIF4_ADINR, - RSND_REG_SSI_BUSIF4_DALIGN, - RSND_REG_SSI_BUSIF5_MODE, - RSND_REG_SSI_BUSIF5_ADINR, - RSND_REG_SSI_BUSIF5_DALIGN, - RSND_REG_SSI_BUSIF6_MODE, - RSND_REG_SSI_BUSIF6_ADINR, - RSND_REG_SSI_BUSIF6_DALIGN, - RSND_REG_SSI_BUSIF7_MODE, - RSND_REG_SSI_BUSIF7_ADINR, - RSND_REG_SSI_BUSIF7_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, - RSND_REG_HDMI0_SEL, - RSND_REG_HDMI1_SEL, + SSI_MODE, + SSI_MODE0, + SSI_MODE1, + SSI_MODE2, + SSI_CONTROL, + SSI_CTRL, + SSI_BUSIF0_MODE, + SSI_BUSIF1_MODE, + SSI_BUSIF2_MODE, + SSI_BUSIF3_MODE, + SSI_BUSIF4_MODE, + SSI_BUSIF5_MODE, + SSI_BUSIF6_MODE, + SSI_BUSIF7_MODE, + SSI_BUSIF0_ADINR, + SSI_BUSIF1_ADINR, + SSI_BUSIF2_ADINR, + SSI_BUSIF3_ADINR, + SSI_BUSIF4_ADINR, + SSI_BUSIF5_ADINR, + SSI_BUSIF6_ADINR, + SSI_BUSIF7_ADINR, + SSI_BUSIF0_DALIGN, + SSI_BUSIF1_DALIGN, + SSI_BUSIF2_DALIGN, + SSI_BUSIF3_DALIGN, + SSI_BUSIF4_DALIGN, + SSI_BUSIF5_DALIGN, + SSI_BUSIF6_DALIGN, + SSI_BUSIF7_DALIGN, + SSI_INT_ENABLE, + SSI_SYS_STATUS0, + SSI_SYS_STATUS1, + SSI_SYS_STATUS2, + SSI_SYS_STATUS3, + SSI_SYS_STATUS4, + SSI_SYS_STATUS5, + SSI_SYS_STATUS6, + SSI_SYS_STATUS7, + HDMI0_SEL, + HDMI1_SEL, /* SSI */ - RSND_REG_SSICR, - RSND_REG_SSISR, - RSND_REG_SSITDR, - RSND_REG_SSIRDR, - RSND_REG_SSIWSR, + SSICR, + SSISR, + SSITDR, + SSIRDR, + SSIWSR, - RSND_REG_MAX, + REG_MAX, }; +#define SRCIN_TIMSEL(i) (SRCIN_TIMSEL0 + (i)) +#define SRCOUT_TIMSEL(i) (SRCOUT_TIMSEL0 + (i)) +#define CTU_SVxxR(i, j) (CTU_SV00R + (i * 8) + (j)) +#define DVC_VOLxR(i) (DVC_VOL0R + (i)) +#define AUDIO_CLK_SEL(i) (AUDIO_CLK_SEL0 + (i)) +#define SSI_BUSIF_MODE(i) (SSI_BUSIF0_MODE + (i)) +#define SSI_BUSIF_ADINR(i) (SSI_BUSIF0_ADINR + (i)) +#define SSI_BUSIF_DALIGN(i) (SSI_BUSIF0_DALIGN + (i)) +#define SSI_SYS_STATUS(i) (SSI_SYS_STATUS0 + (i)) + struct rsnd_priv; struct rsnd_mod; @@ -210,20 +220,9 @@ struct rsnd_dai_stream; /* * R-Car basic functions */ -#define rsnd_mod_read(m, r) \ - rsnd_read(rsnd_mod_to_priv(m), m, RSND_REG_##r) -#define rsnd_mod_write(m, r, d) \ - rsnd_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d) -#define rsnd_mod_bset(m, r, s, d) \ - rsnd_bset(rsnd_mod_to_priv(m), m, RSND_REG_##r, s, d) - -u32 rsnd_read(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg); -void rsnd_write(struct rsnd_priv *priv, struct rsnd_mod *mod, - enum rsnd_reg reg, u32 data); -void rsnd_force_write(struct rsnd_priv *priv, struct rsnd_mod *mod, - enum rsnd_reg reg, u32 data); -void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, - u32 mask, u32 data); +u32 rsnd_mod_read(struct rsnd_mod *mod, enum rsnd_reg reg); +void rsnd_mod_write(struct rsnd_mod *mod, enum rsnd_reg reg, u32 data); +void rsnd_mod_bset(struct rsnd_mod *mod, enum rsnd_reg reg, u32 mask, u32 data); 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); u32 rsnd_get_busif_shift(struct rsnd_dai_stream *io, struct rsnd_mod *mod); @@ -301,6 +300,12 @@ struct rsnd_mod_ops { int (*cleanup)(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv); + u32 *(*get_status)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + enum rsnd_mod_type type); + int (*id)(struct rsnd_mod *mod); + int (*id_sub)(struct rsnd_mod *mod); + int (*id_cmd)(struct rsnd_mod *mod); }; struct rsnd_dai_stream; @@ -310,9 +315,6 @@ struct rsnd_mod { struct rsnd_mod_ops *ops; struct rsnd_priv *priv; struct clk *clk; - u32 *(*get_status)(struct rsnd_dai_stream *io, - struct rsnd_mod *mod, - enum rsnd_mod_type type); u32 status; }; /* @@ -375,8 +377,6 @@ struct rsnd_mod { #define __rsnd_mod_call_pointer 0 #define rsnd_mod_to_priv(mod) ((mod)->priv) -#define rsnd_mod_name(mod) ((mod)->ops->name) -#define rsnd_mod_id(mod) ((mod)->id) #define rsnd_mod_power_on(mod) clk_enable((mod)->clk) #define rsnd_mod_power_off(mod) clk_disable((mod)->clk) #define rsnd_mod_get(ip) (&(ip)->mod) @@ -385,9 +385,6 @@ int rsnd_mod_init(struct rsnd_priv *priv, struct rsnd_mod *mod, struct rsnd_mod_ops *ops, struct clk *clk, - u32* (*get_status)(struct rsnd_dai_stream *io, - struct rsnd_mod *mod, - enum rsnd_mod_type type), enum rsnd_mod_type type, int id); void rsnd_mod_quit(struct rsnd_mod *mod); @@ -396,9 +393,13 @@ struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, void rsnd_mod_interrupt(struct rsnd_mod *mod, void (*callback)(struct rsnd_mod *mod, struct rsnd_dai_stream *io)); -u32 *rsnd_mod_get_status(struct rsnd_dai_stream *io, - struct rsnd_mod *mod, +u32 *rsnd_mod_get_status(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, enum rsnd_mod_type type); +int rsnd_mod_id(struct rsnd_mod *mod); +int rsnd_mod_id_raw(struct rsnd_mod *mod); +int rsnd_mod_id_sub(struct rsnd_mod *mod); +char *rsnd_mod_name(struct rsnd_mod *mod); struct rsnd_mod *rsnd_mod_next(int *iterator, struct rsnd_dai_stream *io, enum rsnd_mod_type *array, @@ -430,8 +431,9 @@ int rsnd_runtime_channel_after_ctu_with_params(struct rsnd_dai_stream *io, rsnd_runtime_channel_for_ssi_with_params(io, NULL) int rsnd_runtime_channel_for_ssi_with_params(struct rsnd_dai_stream *io, struct snd_pcm_hw_params *params); -int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io); -int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io); +int rsnd_runtime_is_multi_ssi(struct rsnd_dai_stream *io); +int rsnd_runtime_is_tdm(struct rsnd_dai_stream *io); +int rsnd_runtime_is_tdm_split(struct rsnd_dai_stream *io); /* * DT @@ -440,6 +442,7 @@ int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io); 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_SSIU "rcar_sound,ssiu" #define RSND_NODE_SRC "rcar_sound,src" #define RSND_NODE_CTU "rcar_sound,ctu" #define RSND_NODE_MIX "rcar_sound,mix" @@ -456,8 +459,17 @@ struct rsnd_dai_stream { struct rsnd_mod *dma; struct rsnd_dai *rdai; struct device *dmac_dev; /* for IPMMU */ + u32 converted_rate; /* converted sampling rate */ + int converted_chan; /* converted channels */ u32 parent_ssi_status; + u32 flags; }; + +/* flags */ +#define RSND_STREAM_HDMI0 (1 << 0) /* for HDMI0 */ +#define RSND_STREAM_HDMI1 (1 << 1) /* for HDMI1 */ +#define RSND_STREAM_TDM_SPLIT (1 << 2) /* for TDM split mode */ + #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) @@ -472,6 +484,8 @@ struct rsnd_dai_stream { #define rsnd_io_is_play(io) (&rsnd_io_to_rdai(io)->playback == io) #define rsnd_io_to_runtime(io) ((io)->substream ? \ (io)->substream->runtime : NULL) +#define rsnd_io_converted_rate(io) ((io)->converted_rate) +#define rsnd_io_converted_chan(io) ((io)->converted_chan) int rsnd_io_is_working(struct rsnd_dai_stream *io); struct rsnd_dai { @@ -712,18 +726,9 @@ extern const char * const volume_ramp_rate[]; int rsnd_ssi_probe(struct rsnd_priv *priv); void rsnd_ssi_remove(struct rsnd_priv *priv); struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); -int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); int rsnd_ssi_use_busif(struct rsnd_dai_stream *io); -int rsnd_ssi_get_busif(struct rsnd_dai_stream *io); u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io); -#define RSND_SSI_HDMI_PORT0 0xf0 -#define RSND_SSI_HDMI_PORT1 0xf1 -int rsnd_ssi_hdmi_port(struct rsnd_dai_stream *io); -void rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv, - struct device_node *endpoint, - int dai_i); - #define rsnd_ssi_is_pin_sharing(io) \ __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io)) int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); @@ -742,6 +747,10 @@ int rsnd_ssiu_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod); int rsnd_ssiu_probe(struct rsnd_priv *priv); void rsnd_ssiu_remove(struct rsnd_priv *priv); +void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai, + struct device_node *playback, + struct device_node *capture); +#define rsnd_ssiu_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_SSIU) /* * R-Car SRC @@ -767,7 +776,6 @@ unsigned int rsnd_src_get_rate(struct rsnd_priv *priv, */ 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) rsnd_parse_of_node(priv, RSND_NODE_CTU) #define rsnd_parse_connect_ctu(rdai, playback, capture) \ diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index cd38a43b976f..50348a2c9203 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -25,7 +25,6 @@ struct rsnd_src { struct rsnd_mod *dma; struct rsnd_kctrl_cfg_s sen; /* sync convert enable */ struct rsnd_kctrl_cfg_s sync; /* sync convert */ - u32 convert_rate; /* sampling rate convert */ int irq; }; @@ -89,12 +88,12 @@ static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io, return 0; if (!rsnd_src_sync_is_enabled(mod)) - return src->convert_rate; + return rsnd_io_converted_rate(io); convert_rate = src->sync.val; if (!convert_rate) - convert_rate = src->convert_rate; + convert_rate = rsnd_io_converted_rate(io); if (!convert_rate) convert_rate = runtime->rate; @@ -135,40 +134,6 @@ unsigned int rsnd_src_get_rate(struct rsnd_priv *priv, return rate; } -static int rsnd_src_hw_params(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *fe_params) -{ - struct rsnd_src *src = rsnd_mod_to_src(mod); - struct snd_soc_pcm_runtime *fe = substream->private_data; - - /* - * SRC assumes that it is used under DPCM if user want to use - * sampling rate convert. Then, SRC should be FE. - * And then, this function will be called *after* BE settings. - * this means, each BE already has fixuped hw_params. - * see - * dpcm_fe_dai_hw_params() - * dpcm_be_dai_hw_params() - */ - src->convert_rate = 0; - if (fe->dai_link->dynamic) { - int stream = substream->stream; - struct snd_soc_dpcm *dpcm; - struct snd_pcm_hw_params *be_params; - - for_each_dpcm_be(fe, stream, dpcm) { - be_params = &dpcm->hw_params; - - if (params_rate(fe_params) != params_rate(be_params)) - src->convert_rate = params_rate(be_params); - } - } - - return 0; -} - static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { @@ -349,9 +314,8 @@ static bool rsnd_src_error_occurred(struct rsnd_mod *mod) status0 = rsnd_mod_read(mod, SCU_SYS_STATUS0); status1 = rsnd_mod_read(mod, SCU_SYS_STATUS1); if ((status0 & val0) || (status1 & val1)) { - rsnd_dbg_irq_status(dev, "%s[%d] err status : 0x%08x, 0x%08x\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), - status0, status1); + rsnd_dbg_irq_status(dev, "%s err status : 0x%08x, 0x%08x\n", + rsnd_mod_name(mod), status0, status1); ret = true; } @@ -527,16 +491,16 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, } static struct rsnd_mod_ops rsnd_src_ops = { - .name = SRC_NAME, - .dma_req = rsnd_src_dma_req, - .probe = rsnd_src_probe_, - .init = rsnd_src_init, - .quit = rsnd_src_quit, - .start = rsnd_src_start, - .stop = rsnd_src_stop, - .irq = rsnd_src_irq, - .hw_params = rsnd_src_hw_params, - .pcm_new = rsnd_src_pcm_new, + .name = SRC_NAME, + .dma_req = rsnd_src_dma_req, + .probe = rsnd_src_probe_, + .init = rsnd_src_init, + .quit = rsnd_src_quit, + .start = rsnd_src_start, + .stop = rsnd_src_stop, + .irq = rsnd_src_irq, + .pcm_new = rsnd_src_pcm_new, + .get_status = rsnd_mod_get_status, }; struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) @@ -605,8 +569,7 @@ int rsnd_src_probe(struct rsnd_priv *priv) } ret = rsnd_mod_init(priv, rsnd_mod_get(src), - &rsnd_src_ops, clk, rsnd_mod_get_status, - RSND_MOD_SRC, i); + &rsnd_src_ops, clk, RSND_MOD_SRC, i); if (ret) { of_node_put(np); goto rsnd_src_probe_done; diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 6ec78f3096dd..45ef295743ec 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -99,9 +99,7 @@ struct rsnd_ssi { /* flags */ #define RSND_SSI_CLK_PIN_SHARE (1 << 0) #define RSND_SSI_NO_BUSIF (1 << 1) /* SSI+DMA without BUSIF */ -#define RSND_SSI_HDMI0 (1 << 2) /* for HDMI0 */ -#define RSND_SSI_HDMI1 (1 << 3) /* for HDMI1 */ -#define RSND_SSI_PROBED (1 << 4) +#define RSND_SSI_PROBED (1 << 2) #define for_each_rsnd_ssi(pos, priv, i) \ for (i = 0; \ @@ -119,19 +117,7 @@ struct rsnd_ssi { (rsnd_ssi_run_mods(io) & (1 << rsnd_mod_id(mod))) #define rsnd_ssi_can_output_clk(mod) (!__rsnd_ssi_is_pin_sharing(mod)) -int rsnd_ssi_hdmi_port(struct rsnd_dai_stream *io) -{ - struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - - if (rsnd_flags_has(ssi, RSND_SSI_HDMI0)) - return RSND_SSI_HDMI_PORT0; - - if (rsnd_flags_has(ssi, RSND_SSI_HDMI1)) - return RSND_SSI_HDMI_PORT1; - - return 0; -} +static int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) { @@ -150,11 +136,6 @@ int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) return use_busif; } -int rsnd_ssi_get_busif(struct rsnd_dai_stream *io) -{ - return 0; /* BUSIF0 only for now */ -} - static void rsnd_ssi_status_clear(struct rsnd_mod *mod) { rsnd_mod_write(mod, SSISR, 0); @@ -181,8 +162,7 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod, udelay(5); } - dev_warn(dev, "%s[%d] status check failed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); + dev_warn(dev, "%s status check failed\n", rsnd_mod_name(mod)); } static u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io) @@ -224,7 +204,7 @@ static u32 rsnd_ssi_run_mods(struct rsnd_dai_stream *io) u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io) { - if (rsnd_runtime_is_ssi_multi(io)) + if (rsnd_runtime_is_multi_ssi(io)) return rsnd_ssi_multi_slaves(io); return 0; @@ -320,6 +300,9 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, return 0; } + if (rsnd_runtime_is_tdm_split(io)) + chan = rsnd_io_converted_chan(io); + main_rate = rsnd_ssi_clk_query(rdai, rate, chan, &idx); if (!main_rate) { dev_err(dev, "unsupported clock rate\n"); @@ -346,9 +329,8 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, ssi->rate = rate; ssi->chan = chan; - dev_dbg(dev, "%s[%d] outputs %u Hz\n", - rsnd_mod_name(mod), - rsnd_mod_id(mod), rate); + dev_dbg(dev, "%s outputs %d chan %u Hz\n", + rsnd_mod_name(mod), chan, rate); return 0; } @@ -379,14 +361,23 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); + struct device *dev = rsnd_priv_to_dev(priv); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); u32 cr_own = ssi->cr_own; u32 cr_mode = ssi->cr_mode; u32 wsr = ssi->wsr; - int is_tdm; + int width; + int is_tdm, is_tdm_split; + + is_tdm = rsnd_runtime_is_tdm(io); + is_tdm_split = rsnd_runtime_is_tdm_split(io); - is_tdm = rsnd_runtime_is_ssi_tdm(io); + if (is_tdm) + dev_dbg(dev, "TDM mode\n"); + if (is_tdm_split) + dev_dbg(dev, "TDM Split mode\n"); cr_own |= FORCE | rsnd_rdai_width_to_swl(rdai); @@ -405,7 +396,7 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod, * rsnd_ssiu_init_gen2() */ wsr = ssi->wsr; - if (is_tdm) { + if (is_tdm || is_tdm_split) { wsr |= WS_MODE; cr_own |= CHNL_8; } @@ -421,7 +412,18 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod, cr_own |= TRMD; cr_own &= ~DWL_MASK; - switch (snd_pcm_format_width(runtime->format)) { + width = snd_pcm_format_width(runtime->format); + if (is_tdm_split) { + /* + * The SWL and DWL bits in SSICR should be fixed at 32-bit + * setting when TDM split mode. + * see datasheet + * Operation :: TDM Format Split Function (TDM Split Mode) + */ + width = 32; + } + + switch (width) { case 8: cr_own |= DWL_8; break; @@ -431,6 +433,9 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod, case 24: cr_own |= DWL_24; break; + case 32: + cr_own |= DWL_32; + break; } if (rsnd_ssi_is_dma_mode(mod)) { @@ -494,8 +499,7 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod, return 0; if (!ssi->usrcnt) { - dev_err(dev, "%s[%d] usrcnt error\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); + dev_err(dev, "%s usrcnt error\n", rsnd_mod_name(mod)); return -EIO; } @@ -654,8 +658,8 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, /* DMA only */ if (is_dma && (status & (UIRQ | OIRQ))) { - rsnd_dbg_irq_status(dev, "%s[%d] err status : 0x%08x\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), status); + rsnd_dbg_irq_status(dev, "%s err status : 0x%08x\n", + rsnd_mod_name(mod), status); stop = true; } @@ -681,6 +685,41 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) return IRQ_HANDLED; } +static u32 *rsnd_ssi_get_status(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + enum rsnd_mod_type type) +{ + /* + * SSIP (= SSI parent) needs to be special, otherwise, + * 2nd SSI might doesn't start. see also rsnd_mod_call() + * + * We can't include parent SSI status on SSI, because we don't know + * how many SSI requests parent SSI. Thus, it is localed on "io" now. + * ex) trouble case + * Playback: SSI0 + * Capture : SSI1 (needs SSI0) + * + * 1) start Capture -> SSI0/SSI1 are started. + * 2) start Playback -> SSI0 doesn't work, because it is already + * marked as "started" on 1) + * + * OTOH, using each mod's status is good for MUX case. + * It doesn't need to start in 2nd start + * ex) + * IO-0: SRC0 -> CTU1 -+-> MUX -> DVC -> SSIU -> SSI0 + * | + * IO-1: SRC1 -> CTU2 -+ + * + * 1) start IO-0 -> start SSI0 + * 2) start IO-1 -> SSI0 doesn't need to start, because it is + * already started on 1) + */ + if (type == RSND_MOD_SSIP) + return &io->parent_ssi_status; + + return rsnd_mod_get_status(mod, io, type); +} + /* * SSI PIO */ @@ -730,7 +769,7 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod, { struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - int ret; + int ret = 0; /* * SSIP/SSIU/IRQ are not needed on @@ -744,10 +783,6 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod, * see rsnd_ssi_pcm_new() */ - ret = rsnd_ssiu_attach(io, mod); - if (ret < 0) - return ret; - /* * SSI might be called again as PIO fallback * It is easy to manual handling for IRQ request/free @@ -876,18 +911,19 @@ static int rsnd_ssi_prepare(struct rsnd_mod *mod, } static struct rsnd_mod_ops rsnd_ssi_pio_ops = { - .name = SSI_NAME, - .probe = rsnd_ssi_common_probe, - .remove = rsnd_ssi_common_remove, - .init = rsnd_ssi_pio_init, - .quit = rsnd_ssi_quit, - .start = rsnd_ssi_start, - .stop = rsnd_ssi_stop, - .irq = rsnd_ssi_irq, - .pointer = rsnd_ssi_pio_pointer, - .pcm_new = rsnd_ssi_pcm_new, - .hw_params = rsnd_ssi_hw_params, - .prepare = rsnd_ssi_prepare, + .name = SSI_NAME, + .probe = rsnd_ssi_common_probe, + .remove = rsnd_ssi_common_remove, + .init = rsnd_ssi_pio_init, + .quit = rsnd_ssi_quit, + .start = rsnd_ssi_start, + .stop = rsnd_ssi_stop, + .irq = rsnd_ssi_irq, + .pointer = rsnd_ssi_pio_pointer, + .pcm_new = rsnd_ssi_pcm_new, + .hw_params = rsnd_ssi_hw_params, + .prepare = rsnd_ssi_prepare, + .get_status = rsnd_ssi_get_status, }; static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, @@ -928,8 +964,7 @@ static int rsnd_ssi_fallback(struct rsnd_mod *mod, */ mod->ops = &rsnd_ssi_pio_ops; - dev_info(dev, "%s[%d] fallback to PIO mode\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); + dev_info(dev, "%s fallback to PIO mode\n", rsnd_mod_name(mod)); return 0; } @@ -941,6 +976,17 @@ static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io, int is_play = rsnd_io_is_play(io); char *name; + /* + * It should use "rcar_sound,ssiu" on DT. + * But, we need to keep compatibility for old version. + * + * If it has "rcar_sound.ssiu", it will be used. + * If not, "rcar_sound.ssi" will be used. + * see + * rsnd_ssiu_dma_req() + * rsnd_dma_of_path() + */ + if (rsnd_ssi_use_busif(io)) name = is_play ? "rxu" : "txu"; else @@ -951,27 +997,27 @@ static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io, } static struct rsnd_mod_ops rsnd_ssi_dma_ops = { - .name = SSI_NAME, - .dma_req = rsnd_ssi_dma_req, - .probe = rsnd_ssi_dma_probe, - .remove = rsnd_ssi_common_remove, - .init = rsnd_ssi_init, - .quit = rsnd_ssi_quit, - .start = rsnd_ssi_start, - .stop = rsnd_ssi_stop, - .irq = rsnd_ssi_irq, - .pcm_new = rsnd_ssi_pcm_new, - .fallback = rsnd_ssi_fallback, - .hw_params = rsnd_ssi_hw_params, - .prepare = rsnd_ssi_prepare, + .name = SSI_NAME, + .dma_req = rsnd_ssi_dma_req, + .probe = rsnd_ssi_dma_probe, + .remove = rsnd_ssi_common_remove, + .init = rsnd_ssi_init, + .quit = rsnd_ssi_quit, + .start = rsnd_ssi_start, + .stop = rsnd_ssi_stop, + .irq = rsnd_ssi_irq, + .pcm_new = rsnd_ssi_pcm_new, + .fallback = rsnd_ssi_fallback, + .hw_params = rsnd_ssi_hw_params, + .prepare = rsnd_ssi_prepare, + .get_status = rsnd_ssi_get_status, }; -int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod) +static int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod) { return mod->ops == &rsnd_ssi_dma_ops; } - /* * ssi mod function */ @@ -1027,54 +1073,6 @@ void rsnd_parse_connect_ssi(struct rsnd_dai *rdai, of_node_put(node); } -static void __rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv, - struct rsnd_dai_stream *io, - struct device_node *remote_ep) -{ - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); - struct rsnd_ssi *ssi; - struct device_node *remote_node = of_graph_get_port_parent(remote_ep); - - /* support Gen3 only */ - if (!rsnd_is_gen3(priv)) - return; - - if (!mod) - return; - - ssi = rsnd_mod_to_ssi(mod); - - /* HDMI0 */ - if (strstr(remote_node->full_name, "hdmi@fead0000")) { - rsnd_flags_set(ssi, RSND_SSI_HDMI0); - dev_dbg(dev, "%s[%d] connected to HDMI0\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - } - - /* HDMI1 */ - if (strstr(remote_node->full_name, "hdmi@feae0000")) { - rsnd_flags_set(ssi, RSND_SSI_HDMI1); - dev_dbg(dev, "%s[%d] connected to HDMI1\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - } -} - -void rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv, - struct device_node *endpoint, - int dai_i) -{ - struct rsnd_dai *rdai = rsnd_rdai_get(priv, dai_i); - struct device_node *remote_ep; - - remote_ep = of_graph_get_remote_endpoint(endpoint); - if (!remote_ep) - return; - - __rsnd_ssi_parse_hdmi_connection(priv, &rdai->playback, remote_ep); - __rsnd_ssi_parse_hdmi_connection(priv, &rdai->capture, remote_ep); -} - struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) { if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv))) @@ -1091,41 +1089,6 @@ int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) return !!(rsnd_flags_has(rsnd_mod_to_ssi(mod), RSND_SSI_CLK_PIN_SHARE)); } -static u32 *rsnd_ssi_get_status(struct rsnd_dai_stream *io, - struct rsnd_mod *mod, - enum rsnd_mod_type type) -{ - /* - * SSIP (= SSI parent) needs to be special, otherwise, - * 2nd SSI might doesn't start. see also rsnd_mod_call() - * - * We can't include parent SSI status on SSI, because we don't know - * how many SSI requests parent SSI. Thus, it is localed on "io" now. - * ex) trouble case - * Playback: SSI0 - * Capture : SSI1 (needs SSI0) - * - * 1) start Capture -> SSI0/SSI1 are started. - * 2) start Playback -> SSI0 doesn't work, because it is already - * marked as "started" on 1) - * - * OTOH, using each mod's status is good for MUX case. - * It doesn't need to start in 2nd start - * ex) - * IO-0: SRC0 -> CTU1 -+-> MUX -> DVC -> SSIU -> SSI0 - * | - * IO-1: SRC1 -> CTU2 -+ - * - * 1) start IO-0 -> start SSI0 - * 2) start IO-1 -> SSI0 doesn't need to start, because it is - * already started on 1) - */ - if (type == RSND_MOD_SSIP) - return &io->parent_ssi_status; - - return rsnd_mod_get_status(io, mod, type); -} - int rsnd_ssi_probe(struct rsnd_priv *priv) { struct device_node *node; @@ -1192,7 +1155,7 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) ops = &rsnd_ssi_dma_ops; ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk, - rsnd_ssi_get_status, RSND_MOD_SSI, i); + RSND_MOD_SSI, i); if (ret) { of_node_put(np); goto rsnd_ssi_probe_done; diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 39b67643b5dc..c5934adcfd01 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -12,8 +12,14 @@ struct rsnd_ssiu { struct rsnd_mod mod; u32 busif_status[8]; /* for BUSIF0 - BUSIF7 */ unsigned int usrcnt; + int id; + int id_sub; }; +/* SSI_MODE */ +#define TDM_EXT (1 << 0) +#define TDM_SPLIT (1 << 8) + #define rsnd_ssiu_nr(priv) ((priv)->ssiu_nr) #define rsnd_mod_to_ssiu(_mod) container_of((_mod), struct rsnd_ssiu, mod) #define for_each_rsnd_ssiu(pos, priv, i) \ @@ -22,6 +28,33 @@ struct rsnd_ssiu { ((pos) = ((struct rsnd_ssiu *)(priv)->ssiu + i)); \ i++) +/* + * SSI Gen2 Gen3 + * 0 BUSIF0-3 BUSIF0-7 + * 1 BUSIF0-3 BUSIF0-7 + * 2 BUSIF0-3 BUSIF0-7 + * 3 BUSIF0 BUSIF0-7 + * 4 BUSIF0 BUSIF0-7 + * 5 BUSIF0 BUSIF0 + * 6 BUSIF0 BUSIF0 + * 7 BUSIF0 BUSIF0 + * 8 BUSIF0 BUSIF0 + * 9 BUSIF0-3 BUSIF0-7 + * total 22 52 + */ +static const int gen2_id[] = { 0, 4, 8, 12, 13, 14, 15, 16, 17, 18 }; +static const int gen3_id[] = { 0, 8, 16, 24, 32, 40, 41, 42, 43, 44 }; + +static u32 *rsnd_ssiu_get_status(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + enum rsnd_mod_type type) +{ + struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod); + int busif = rsnd_mod_id_sub(mod); + + return &ssiu->busif_status[busif]; +} + static int rsnd_ssiu_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) @@ -32,6 +65,7 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod, int id = rsnd_mod_id(mod); u32 mask1, val1; u32 mask2, val2; + int i; /* clear status */ switch (id) { @@ -40,16 +74,12 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod, 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)); + for (i = 0; i < 4; i++) + rsnd_mod_write(mod, SSI_SYS_STATUS(i * 2), 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); + for (i = 0; i < 4; i++) + rsnd_mod_write(mod, SSI_SYS_STATUS((i * 2) + 1), 0xf << (id * 4)); break; } @@ -115,8 +145,9 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod, } static struct rsnd_mod_ops rsnd_ssiu_ops_gen1 = { - .name = SSIU_NAME, - .init = rsnd_ssiu_init, + .name = SSIU_NAME, + .init = rsnd_ssiu_init, + .get_status = rsnd_ssiu_get_status, }; static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, @@ -124,7 +155,8 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, struct rsnd_priv *priv) { struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod); - int hdmi = rsnd_ssi_hdmi_port(io); + u32 has_hdmi0 = rsnd_flags_has(io, RSND_STREAM_HDMI0); + u32 has_hdmi1 = rsnd_flags_has(io, RSND_STREAM_HDMI1); int ret; u32 mode = 0; @@ -134,20 +166,21 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, ssiu->usrcnt++; - if (rsnd_runtime_is_ssi_tdm(io)) { - /* - * TDM Extend Mode - * see - * rsnd_ssi_config_init() - */ - mode = 0x1; - } + /* + * TDM Extend/Split Mode + * see + * rsnd_ssi_config_init() + */ + if (rsnd_runtime_is_tdm(io)) + mode = TDM_EXT; + else if (rsnd_runtime_is_tdm_split(io)) + mode = TDM_SPLIT; rsnd_mod_write(mod, SSI_MODE, mode); if (rsnd_ssi_use_busif(io)) { int id = rsnd_mod_id(mod); - int busif = rsnd_ssi_get_busif(io); + int busif = rsnd_mod_id_sub(mod); /* * FIXME @@ -162,46 +195,18 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, id, busif); } -#define RSND_WRITE_BUSIF(i) \ - rsnd_mod_write(mod, SSI_BUSIF##i##_ADINR, \ - rsnd_get_adinr_bit(mod, io) | \ - (rsnd_io_is_play(io) ? \ - rsnd_runtime_channel_after_ctu(io) : \ - rsnd_runtime_channel_original(io))); \ - rsnd_mod_write(mod, SSI_BUSIF##i##_MODE, \ - rsnd_get_busif_shift(io, mod) | 1); \ - rsnd_mod_write(mod, SSI_BUSIF##i##_DALIGN, \ - rsnd_get_dalign(mod, io)) - - switch (busif) { - case 0: - RSND_WRITE_BUSIF(0); - break; - case 1: - RSND_WRITE_BUSIF(1); - break; - case 2: - RSND_WRITE_BUSIF(2); - break; - case 3: - RSND_WRITE_BUSIF(3); - break; - case 4: - RSND_WRITE_BUSIF(4); - break; - case 5: - RSND_WRITE_BUSIF(5); - break; - case 6: - RSND_WRITE_BUSIF(6); - break; - case 7: - RSND_WRITE_BUSIF(7); - break; - } + rsnd_mod_write(mod, SSI_BUSIF_ADINR(busif), + rsnd_get_adinr_bit(mod, io) | + (rsnd_io_is_play(io) ? + rsnd_runtime_channel_after_ctu(io) : + rsnd_runtime_channel_original(io))); + rsnd_mod_write(mod, SSI_BUSIF_MODE(busif), + rsnd_get_busif_shift(io, mod) | 1); + rsnd_mod_write(mod, SSI_BUSIF_DALIGN(busif), + rsnd_get_dalign(mod, io)); } - if (hdmi) { + if (has_hdmi0 || has_hdmi1) { enum rsnd_mod_type rsnd_ssi_array[] = { RSND_MOD_SSIM1, RSND_MOD_SSIM2, @@ -227,14 +232,10 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, rsnd_mod_id(pos) << shift; } - switch (hdmi) { - case RSND_SSI_HDMI_PORT0: + if (has_hdmi0) rsnd_mod_write(mod, HDMI0_SEL, val); - break; - case RSND_SSI_HDMI_PORT1: + if (has_hdmi1) rsnd_mod_write(mod, HDMI1_SEL, val); - break; - } } return 0; @@ -244,7 +245,7 @@ static int rsnd_ssiu_start_gen2(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - int busif = rsnd_ssi_get_busif(io); + int busif = rsnd_mod_id_sub(mod); if (!rsnd_ssi_use_busif(io)) return 0; @@ -262,7 +263,7 @@ static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod, struct rsnd_priv *priv) { struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod); - int busif = rsnd_ssi_get_busif(io); + int busif = rsnd_mod_id_sub(mod); if (!rsnd_ssi_use_busif(io)) return 0; @@ -278,11 +279,53 @@ static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod, return 0; } +static int rsnd_ssiu_id(struct rsnd_mod *mod) +{ + struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod); + + /* see rsnd_ssiu_probe() */ + return ssiu->id; +} + +static int rsnd_ssiu_id_sub(struct rsnd_mod *mod) +{ + struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod); + + /* see rsnd_ssiu_probe() */ + return ssiu->id_sub; +} + +static struct dma_chan *rsnd_ssiu_dma_req(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + int is_play = rsnd_io_is_play(io); + char *name; + + /* + * It should use "rcar_sound,ssiu" on DT. + * But, we need to keep compatibility for old version. + * + * If it has "rcar_sound.ssiu", it will be used. + * If not, "rcar_sound.ssi" will be used. + * see + * rsnd_ssi_dma_req() + * rsnd_dma_of_path() + */ + + name = is_play ? "rx" : "tx"; + + return rsnd_dma_request_channel(rsnd_ssiu_of_node(priv), + mod, name); +} + static struct rsnd_mod_ops rsnd_ssiu_ops_gen2 = { - .name = SSIU_NAME, - .init = rsnd_ssiu_init_gen2, - .start = rsnd_ssiu_start_gen2, - .stop = rsnd_ssiu_stop_gen2, + .name = SSIU_NAME, + .dma_req = rsnd_ssiu_dma_req, + .init = rsnd_ssiu_init_gen2, + .start = rsnd_ssiu_start_gen2, + .stop = rsnd_ssiu_stop_gen2, + .get_status = rsnd_ssiu_get_status, }; static struct rsnd_mod *rsnd_ssiu_mod_get(struct rsnd_priv *priv, int id) @@ -293,36 +336,85 @@ static struct rsnd_mod *rsnd_ssiu_mod_get(struct rsnd_priv *priv, int id) return rsnd_mod_get((struct rsnd_ssiu *)(priv->ssiu) + id); } -int rsnd_ssiu_attach(struct rsnd_dai_stream *io, - struct rsnd_mod *ssi_mod) +static void rsnd_parse_connect_ssiu_compatible(struct rsnd_priv *priv, + struct rsnd_dai_stream *io) { - struct rsnd_priv *priv = rsnd_io_to_priv(io); - struct rsnd_mod *mod = rsnd_ssiu_mod_get(priv, rsnd_mod_id(ssi_mod)); + struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io); + struct rsnd_mod *mod; + struct rsnd_ssiu *ssiu; + int i; - rsnd_mod_confirm_ssi(ssi_mod); + if (!ssi_mod) + return; - return rsnd_dai_connect(mod, io, mod->type); + /* select BUSIF0 */ + for_each_rsnd_ssiu(ssiu, priv, i) { + mod = rsnd_mod_get(ssiu); + + if ((rsnd_mod_id(ssi_mod) == rsnd_mod_id(mod)) && + (rsnd_mod_id_sub(mod) == 0)) { + rsnd_dai_connect(mod, io, mod->type); + return; + } + } } -static u32 *rsnd_ssiu_get_status(struct rsnd_dai_stream *io, - struct rsnd_mod *mod, - enum rsnd_mod_type type) +void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai, + struct device_node *playback, + struct device_node *capture) { - struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod); - int busif = rsnd_ssi_get_busif(io); + struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); + struct device_node *node = rsnd_ssiu_of_node(priv); + struct device_node *np; + struct rsnd_mod *mod; + struct rsnd_dai_stream *io_p = &rdai->playback; + struct rsnd_dai_stream *io_c = &rdai->capture; + int i; - return &ssiu->busif_status[busif]; + /* use rcar_sound,ssiu if exist */ + if (node) { + i = 0; + for_each_child_of_node(node, np) { + mod = rsnd_ssiu_mod_get(priv, i); + if (np == playback) + rsnd_dai_connect(mod, io_p, mod->type); + if (np == capture) + rsnd_dai_connect(mod, io_c, mod->type); + i++; + } + + of_node_put(node); + } + + /* Keep DT compatibility */ + if (!rsnd_io_to_mod_ssiu(io_p)) + rsnd_parse_connect_ssiu_compatible(priv, io_p); + if (!rsnd_io_to_mod_ssiu(io_c)) + rsnd_parse_connect_ssiu_compatible(priv, io_c); } int rsnd_ssiu_probe(struct rsnd_priv *priv) { struct device *dev = rsnd_priv_to_dev(priv); + struct device_node *node; struct rsnd_ssiu *ssiu; struct rsnd_mod_ops *ops; + const int *list = NULL; int i, nr, ret; - /* same number to SSI */ - nr = priv->ssi_nr; + /* + * Keep DT compatibility. + * if it has "rcar_sound,ssiu", use it. + * if not, use "rcar_sound,ssi" + * see + * rsnd_ssiu_bufsif_to_id() + */ + node = rsnd_ssiu_of_node(priv); + if (node) + nr = of_get_child_count(node); + else + nr = priv->ssi_nr; + ssiu = devm_kcalloc(dev, nr, sizeof(*ssiu), GFP_KERNEL); if (!ssiu) return -ENOMEM; @@ -335,10 +427,46 @@ int rsnd_ssiu_probe(struct rsnd_priv *priv) else ops = &rsnd_ssiu_ops_gen2; + /* Keep compatibility */ + nr = 0; + if ((node) && + (ops == &rsnd_ssiu_ops_gen2)) { + ops->id = rsnd_ssiu_id; + ops->id_sub = rsnd_ssiu_id_sub; + + if (rsnd_is_gen2(priv)) { + list = gen2_id; + nr = ARRAY_SIZE(gen2_id); + } else if (rsnd_is_gen3(priv)) { + list = gen3_id; + nr = ARRAY_SIZE(gen3_id); + } else { + dev_err(dev, "unknown SSIU\n"); + return -ENODEV; + } + } + for_each_rsnd_ssiu(ssiu, priv, i) { + if (node) { + int j; + + /* + * see + * rsnd_ssiu_get_id() + * rsnd_ssiu_get_id_sub() + */ + for (j = 0; j < nr; j++) { + if (list[j] > i) + break; + ssiu->id = j; + ssiu->id_sub = i - list[ssiu->id]; + } + } else { + ssiu->id = i; + } + ret = rsnd_mod_init(priv, rsnd_mod_get(ssiu), - ops, NULL, rsnd_ssiu_get_status, - RSND_MOD_SSIU, i); + ops, NULL, RSND_MOD_SSIU, i); if (ret) return ret; } diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index b29d0f65611e..0462b3ec977a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1467,7 +1467,7 @@ static int soc_link_dai_pcm_new(struct snd_soc_dai **dais, int num_dais, for (i = 0; i < num_dais; ++i) { struct snd_soc_dai_driver *drv = dais[i]->driver; - if (!rtd->dai_link->no_pcm && drv->pcm_new) + if (drv->pcm_new) ret = drv->pcm_new(rtd, dais[i]); if (ret < 0) { dev_err(dais[i]->dev, @@ -3485,12 +3485,11 @@ 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, - struct snd_soc_codec_conf *codec_conf, - struct device_node *of_node, - const char *propname) +void snd_soc_of_parse_node_prefix(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; @@ -3503,7 +3502,7 @@ 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_node_prefix); int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, const char *propname) diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c index d597eba61992..bcb35cae2a2c 100644 --- a/sound/soc/stm/stm32_sai.c +++ b/sound/soc/stm/stm32_sai.c @@ -74,14 +74,14 @@ static int stm32_sai_sync_conf_provider(struct stm32_sai_data *sai, int synco) return ret; } - dev_dbg(&sai->pdev->dev, "Set %s%s as synchro provider\n", - sai->pdev->dev.of_node->name, + dev_dbg(&sai->pdev->dev, "Set %pOFn%s as synchro provider\n", + sai->pdev->dev.of_node, synco == STM_SAI_SYNC_OUT_A ? "A" : "B"); prev_synco = FIELD_GET(SAI_GCR_SYNCOUT_MASK, readl_relaxed(sai->base)); if (prev_synco != STM_SAI_SYNC_OUT_NONE && synco != prev_synco) { - dev_err(&sai->pdev->dev, "%s%s already set as sync provider\n", - sai->pdev->dev.of_node->name, + dev_err(&sai->pdev->dev, "%pOFn%s already set as sync provider\n", + sai->pdev->dev.of_node, prev_synco == STM_SAI_SYNC_OUT_A ? "A" : "B"); clk_disable_unprepare(sai->pclk); return -EINVAL; diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 211589b0b2ef..d4825700b63f 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -336,8 +336,7 @@ static int stm32_sai_mclk_set_rate(struct clk_hw *hw, unsigned long rate, { struct stm32_sai_mclk_data *mclk = to_mclk_data(hw); struct stm32_sai_sub_data *sai = mclk->sai_data; - unsigned int div; - int ret; + int div, ret; div = stm32_sai_get_clk_div(sai, parent_rate, rate); if (div < 0) diff --git a/sound/soc/sunxi/sun50i-codec-analog.c b/sound/soc/sunxi/sun50i-codec-analog.c index 8f5f999df631..df1fed0aa001 100644 --- a/sound/soc/sunxi/sun50i-codec-analog.c +++ b/sound/soc/sunxi/sun50i-codec-analog.c @@ -274,6 +274,7 @@ static const struct snd_soc_dapm_widget sun50i_a64_codec_widgets[] = { * stream widgets at the card level. */ + SND_SOC_DAPM_REGULATOR_SUPPLY("hpvcc", 0, 0), SND_SOC_DAPM_MUX("Headphone Source Playback Route", SND_SOC_NOPM, 0, 0, sun50i_codec_hp_src), SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN50I_ADDA_HP_CTRL, @@ -361,6 +362,7 @@ static const struct snd_soc_dapm_route sun50i_a64_codec_routes[] = { { "Headphone Source Playback Route", "Mixer", "Left Mixer" }, { "Headphone Source Playback Route", "Mixer", "Right Mixer" }, { "Headphone Amp", NULL, "Headphone Source Playback Route" }, + { "Headphone Amp", NULL, "hpvcc" }, { "HP", NULL, "Headphone Amp" }, /* Microphone Routes */ diff --git a/sound/soc/ti/Kconfig b/sound/soc/ti/Kconfig new file mode 100644 index 000000000000..4bf3c15d4e51 --- /dev/null +++ b/sound/soc/ti/Kconfig @@ -0,0 +1,209 @@ +menu "Audio support for Texas Instruments SoCs" +depends on DMA_OMAP || TI_EDMA || COMPILE_TEST + +config SND_SOC_TI_EDMA_PCM + tristate + select SND_SOC_GENERIC_DMAENGINE_PCM + +config SND_SOC_TI_SDMA_PCM + tristate + select SND_SOC_GENERIC_DMAENGINE_PCM + +comment "Texas Instruments DAI support for:" +config SND_SOC_DAVINCI_ASP + tristate "daVinci Audio Serial Port (ASP) or McBSP suport" + depends on ARCH_DAVINCI || COMPILE_TEST + select SND_SOC_TI_EDMA_PCM + help + Say Y or M here if you want audio support via daVinci ASP or McBSP. + The driver only implements the ASP support which is a subset of + daVinci McBSP (w/o the multichannel support). + +config SND_SOC_DAVINCI_MCASP + tristate "Multichannel Audio Serial Port (McASP) support" + select SND_SOC_TI_EDMA_PCM if TI_EDMA + select SND_SOC_TI_SDMA_PCM if DMA_OMAP + help + Say Y or M here if you want to have support for McASP IP found in + various Texas Instruments SoCs like: + - daVinci devices + - Sitara line of SoCs (AM335x, AM438x, etc) + - DRA7x devices + - Keystone devices + +config SND_SOC_DAVINCI_VCIF + tristate "daVinci Voice Interface (VCIF) suport" + depends on ARCH_DAVINCI || COMPILE_TEST + select SND_SOC_TI_EDMA_PCM + help + Say Y or M here if you want audio support via daVinci VCIF. + +config SND_SOC_OMAP_DMIC + tristate "Digital Microphone Module (DMIC) support" + depends on ARCH_OMAP4 || SOC_OMAP5 || COMPILE_TEST + select SND_SOC_TI_SDMA_PCM + help + Say Y or M here if you want to have support for DMIC IP found in + OMAP4 and OMAP5. + +config SND_SOC_OMAP_MCBSP + tristate "Multichannel Buffered Serial Port (McBSP) support" + depends on ARCH_OMAP || ARCH_OMAP1 || COMPILE_TEST + select SND_SOC_TI_SDMA_PCM + help + Say Y or M here if you want to have support for McBSP IP found in + Texas Instruments OMAP1/2/3/4/5 SoCs. + +config SND_SOC_OMAP_MCPDM + tristate "Multichannel PDM Controller (McPDM) support" + depends on ARCH_OMAP4 || SOC_OMAP5 || COMPILE_TEST + select SND_SOC_TI_SDMA_PCM + help + Say Y or M here if you want to have support for McPDM IP found in + OMAP4 and OMAP5. + +comment "Audio support for boards with Texas Instruments SoCs" +config SND_SOC_NOKIA_N810 + tristate "SoC Audio support for Nokia N810" + depends on MACH_NOKIA_N810 && I2C + select SND_SOC_OMAP_MCBSP + select SND_SOC_TLV320AIC3X + help + Say Y or M if you want to add support for SoC audio on Nokia N810. + +config SND_SOC_NOKIA_RX51 + tristate "SoC Audio support for Nokia RX-51" + depends on ARCH_OMAP3 && I2C && GPIOLIB + select SND_SOC_OMAP_MCBSP + select SND_SOC_TLV320AIC3X + select SND_SOC_TPA6130A2 + help + Say Y or M if you want to add support for SoC audio on Nokia RX-51 + hardware. This is also known as Nokia N900 product. + +config SND_SOC_OMAP3_PANDORA + tristate "SoC Audio support for OMAP3 Pandora" + depends on ARCH_OMAP3 + depends on TWL4030_CORE + select SND_SOC_OMAP_MCBSP + select SND_SOC_TWL4030 + help + Say Y or M if you want to add support for SoC audio on the OMAP3 Pandora. + +config SND_SOC_OMAP3_TWL4030 + tristate "SoC Audio support for OMAP3 based boards with twl4030 codec" + depends on ARCH_OMAP3 || COMPILE_TEST + depends on TWL4030_CORE + select SND_SOC_OMAP_MCBSP + select SND_SOC_TWL4030 + help + Say Y or M if you want to add support for SoC audio on OMAP3 based + boards using twl4030 as codec. This driver currently supports: + - Beagleboard or Devkit8000 + - Gumstix Overo or CompuLab CM-T35/CM-T3730 + - IGEP v2 + - OMAP3EVM + - SDP3430 + - Zoom2 + +config SND_SOC_OMAP_ABE_TWL6040 + tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec" + depends on TWL6040_CORE && COMMON_CLK + depends on ARCH_OMAP4 || (SOC_OMAP5 && MFD_PALMAS) || COMPILE_TEST + select SND_SOC_OMAP_DMIC + select SND_SOC_OMAP_MCPDM + select SND_SOC_TWL6040 + help + Say Y or M if you want to add support for SoC audio on OMAP boards + using ABE and twl6040 codec. This driver currently supports: + - SDP4430/Blaze boards + - PandaBoard (4430) + - PandaBoardES (4460) + - OMAP5 uEVM + +config SND_SOC_OMAP_AMS_DELTA + tristate "SoC Audio support for Amstrad E3 (Delta) videophone" + depends on MACH_AMS_DELTA && TTY + select SND_SOC_OMAP_MCBSP + select SND_SOC_CX20442 + help + Say Y or M if you want to add support for SoC audio device + connected to a handset and a speakerphone found on Amstrad E3 (Delta) + videophone. + + Note that in order to get those devices fully supported, you have to + build the kernel with standard serial port driver included and + configured for at least 4 ports. Then, from userspace, you must load + a line discipline #19 on the modem (ttyS3) serial line. The simplest + way to achieve this is to install util-linux-ng and use the included + ldattach utility. This can be started automatically from udev, + a simple rule like this one should do the trick (it does for me): + ACTION=="add", KERNEL=="controlC0", \ + RUN+="/usr/sbin/ldattach 19 /dev/ttyS3" + +config SND_SOC_OMAP_HDMI + tristate "OMAP4/5 HDMI audio support" + depends on OMAP4_DSS_HDMI || OMAP5_DSS_HDMI || COMPILE_TEST + select SND_SOC_TI_SDMA_PCM + help + For HDMI audio to work OMAPDSS HDMI support should be + enabled. + The hdmi audio driver implements cpu-dai component using the + callbacks provided by OMAPDSS and registers the component + under DSS HDMI device. Omap-pcm is registered for platform + component also under DSS HDMI device. Dummy codec is used as + as codec component. The hdmi audio driver implements also + the card and registers it under its own platform device. + The device for the driver is registered by OMAPDSS hdmi + driver. + +config SND_SOC_OMAP_OSK5912 + tristate "SoC Audio support for omap osk5912" + depends on MACH_OMAP_OSK && I2C + select SND_SOC_OMAP_MCBSP + select SND_SOC_TLV320AIC23_I2C + help + Say Y or M if you want to add support for SoC audio on osk5912. + +config SND_SOC_DAVINCI_EVM + tristate "SoC Audio support for DaVinci EVMs" + depends on ARCH_DAVINCI && I2C + select SND_SOC_DAVINCI_ASP if MACH_DAVINCI_DM355_EVM + select SND_SOC_DAVINCI_ASP if SND_SOC_DM365_AIC3X_CODEC + select SND_SOC_DAVINCI_VCIF if SND_SOC_DM365_VOICE_CODEC + select SND_SOC_DAVINCI_ASP if MACH_DAVINCI_EVM # DM6446 + select SND_SOC_DAVINCI_MCASP if MACH_DAVINCI_DM6467_EVM + select SND_SOC_SPDIF if MACH_DAVINCI_DM6467_EVM + select SND_SOC_DAVINCI_MCASP if MACH_DAVINCI_DA830_EVM + select SND_SOC_DAVINCI_MCASP if MACH_DAVINCI_DA850_EVM + select SND_SOC_TLV320AIC3X + help + Say Y if you want to add support for SoC audio on the following TI + DaVinci EVM platforms: + - DM355 + - DM365 + - DM6446 + - DM6447 + - DM830 + - DM850 + +choice + prompt "DM365 codec select" + depends on SND_SOC_DAVINCI_EVM + depends on MACH_DAVINCI_DM365_EVM + +config SND_SOC_DM365_AIC3X_CODEC + bool "Audio Codec - AIC3101" + help + Say Y if you want to add support for AIC3101 audio codec + +config SND_SOC_DM365_VOICE_CODEC + bool "Voice Codec - CQ93VC" + select MFD_DAVINCI_VOICECODEC + select SND_SOC_CQ0093VC + help + Say Y if you want to add support for SoC On-chip voice codec +endchoice + +endmenu + diff --git a/sound/soc/ti/Makefile b/sound/soc/ti/Makefile new file mode 100644 index 000000000000..08c44d56ef3e --- /dev/null +++ b/sound/soc/ti/Makefile @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: GPL-2.0 + +# Platform drivers +snd-soc-ti-edma-objs := edma-pcm.o +snd-soc-ti-sdma-objs := sdma-pcm.o + +obj-$(CONFIG_SND_SOC_TI_EDMA_PCM) += snd-soc-ti-edma.o +obj-$(CONFIG_SND_SOC_TI_SDMA_PCM) += snd-soc-ti-sdma.o + +# CPU DAI drivers +snd-soc-davinci-asp-objs := davinci-i2s.o +snd-soc-davinci-mcasp-objs := davinci-mcasp.o +snd-soc-davinci-vcif-objs := davinci-vcif.o +snd-soc-omap-dmic-objs := omap-dmic.o +snd-soc-omap-mcbsp-objs := omap-mcbsp.o omap-mcbsp-st.o +snd-soc-omap-mcpdm-objs := omap-mcpdm.o + +obj-$(CONFIG_SND_SOC_DAVINCI_ASP) += snd-soc-davinci-asp.o +obj-$(CONFIG_SND_SOC_DAVINCI_MCASP) += snd-soc-davinci-mcasp.o +obj-$(CONFIG_SND_SOC_DAVINCI_VCIF) += snd-soc-davinci-vcif.o +obj-$(CONFIG_SND_SOC_OMAP_DMIC) += snd-soc-omap-dmic.o +obj-$(CONFIG_SND_SOC_OMAP_MCBSP) += snd-soc-omap-mcbsp.o +obj-$(CONFIG_SND_SOC_OMAP_MCPDM) += snd-soc-omap-mcpdm.o + +# Machine drivers +snd-soc-davinci-evm-objs := davinci-evm.o +snd-soc-n810-objs := n810.o +snd-soc-rx51-objs := rx51.o +snd-soc-omap3pandora-objs := omap3pandora.o +snd-soc-omap-twl4030-objs := omap-twl4030.o +snd-soc-omap-abe-twl6040-objs := omap-abe-twl6040.o +snd-soc-ams-delta-objs := ams-delta.o +snd-soc-omap-hdmi-objs := omap-hdmi.o +snd-soc-osk5912-objs := osk5912.o + +obj-$(CONFIG_SND_SOC_DAVINCI_EVM) += snd-soc-davinci-evm.o +obj-$(CONFIG_SND_SOC_NOKIA_N810) += snd-soc-n810.o +obj-$(CONFIG_SND_SOC_NOKIA_RX51) += snd-soc-rx51.o +obj-$(CONFIG_SND_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o +obj-$(CONFIG_SND_SOC_OMAP3_TWL4030) += snd-soc-omap-twl4030.o +obj-$(CONFIG_SND_SOC_OMAP_ABE_TWL6040) += snd-soc-omap-abe-twl6040.o +obj-$(CONFIG_SND_SOC_OMAP_AMS_DELTA) += snd-soc-ams-delta.o +obj-$(CONFIG_SND_SOC_OMAP_HDMI) += snd-soc-omap-hdmi.o +obj-$(CONFIG_SND_SOC_OMAP_OSK5912) += snd-soc-osk5912.o diff --git a/sound/soc/omap/ams-delta.c b/sound/soc/ti/ams-delta.c index 4dce494dfbd3..4dce494dfbd3 100644 --- a/sound/soc/omap/ams-delta.c +++ b/sound/soc/ti/ams-delta.c diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/ti/davinci-evm.c index 7a369e0f2093..4869d6311510 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/ti/davinci-evm.c @@ -170,7 +170,7 @@ static struct snd_soc_dai_link dm355_evm_dai = { }; static struct snd_soc_dai_link dm365_evm_dai = { -#ifdef CONFIG_SND_DM365_AIC3X_CODEC +#ifdef CONFIG_SND_SOC_DM365_AIC3X_CODEC .name = "TLV320AIC3X", .stream_name = "AIC3X", .cpu_dai_name = "davinci-mcbsp", @@ -181,7 +181,7 @@ static struct snd_soc_dai_link dm365_evm_dai = { .ops = &evm_ops, .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_NF, -#elif defined(CONFIG_SND_DM365_VOICE_CODEC) +#elif defined(CONFIG_SND_SOC_DM365_VOICE_CODEC) .name = "Voice Codec - CQ93VC", .stream_name = "CQ93", .cpu_dai_name = "davinci-vcif", diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/ti/davinci-i2s.c index a3206e65e5e5..a3206e65e5e5 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/ti/davinci-i2s.c diff --git a/sound/soc/davinci/davinci-i2s.h b/sound/soc/ti/davinci-i2s.h index 48dac3e2521a..48dac3e2521a 100644 --- a/sound/soc/davinci/davinci-i2s.h +++ b/sound/soc/ti/davinci-i2s.h diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index 267aee776b2d..eeda6d5565bc 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -28,6 +28,7 @@ #include <linux/of_device.h> #include <linux/platform_data/davinci_asp.h> #include <linux/math64.h> +#include <linux/bitmap.h> #include <sound/asoundef.h> #include <sound/core.h> @@ -38,7 +39,7 @@ #include <sound/dmaengine_pcm.h> #include "edma-pcm.h" -#include "../omap/sdma-pcm.h" +#include "sdma-pcm.h" #include "davinci-mcasp.h" #define MCASP_MAX_AFIFO_DEPTH 64 @@ -84,6 +85,7 @@ struct davinci_mcasp { u32 tdm_mask[2]; int slot_width; u8 op_mode; + u8 dismod; u8 num_serializer; u8 *serial_dir; u8 version; @@ -95,6 +97,8 @@ struct davinci_mcasp { int sysclk_freq; bool bclk_master; + unsigned long pdir; /* Pin direction bitfield */ + /* McASP FIFO related */ u8 txnumevt; u8 rxnumevt; @@ -169,6 +173,30 @@ static bool mcasp_is_synchronous(struct davinci_mcasp *mcasp) return !(aclkxctl & TX_ASYNC) && rxfmctl & AFSRE; } +static inline void mcasp_set_clk_pdir(struct davinci_mcasp *mcasp, bool enable) +{ + u32 bit = PIN_BIT_AMUTE; + + for_each_set_bit_from(bit, &mcasp->pdir, PIN_BIT_AFSR + 1) { + if (enable) + mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit)); + else + mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit)); + } +} + +static inline void mcasp_set_axr_pdir(struct davinci_mcasp *mcasp, bool enable) +{ + u32 bit; + + for_each_set_bit(bit, &mcasp->pdir, PIN_BIT_AFSR) { + if (enable) + mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit)); + else + mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit)); + } +} + static void mcasp_start_rx(struct davinci_mcasp *mcasp) { if (mcasp->rxnumevt) { /* enable FIFO */ @@ -192,6 +220,7 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp) } /* Activate serializer(s) */ + mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSERCLR); /* Release RX state machine */ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST); @@ -219,7 +248,10 @@ static void mcasp_start_tx(struct davinci_mcasp *mcasp) /* Start clocks */ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST); mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST); + mcasp_set_clk_pdir(mcasp, true); + /* Activate serializer(s) */ + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF); mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSERCLR); /* wait for XDATA to be cleared */ @@ -228,6 +260,8 @@ static void mcasp_start_tx(struct davinci_mcasp *mcasp) (cnt < 100000)) cnt++; + mcasp_set_axr_pdir(mcasp, true); + /* Release TX state machine */ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSMRST); /* Release Frame Sync generator */ @@ -258,8 +292,10 @@ static void mcasp_stop_rx(struct davinci_mcasp *mcasp) * In synchronous mode stop the TX clocks if no other stream is * running */ - if (mcasp_is_synchronous(mcasp) && !mcasp->streams) + if (mcasp_is_synchronous(mcasp) && !mcasp->streams) { + mcasp_set_clk_pdir(mcasp, false); mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, 0); + } mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, 0); mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); @@ -285,6 +321,9 @@ static void mcasp_stop_tx(struct davinci_mcasp *mcasp) */ if (mcasp_is_synchronous(mcasp) && mcasp->streams) val = TXHCLKRST | TXCLKRST | TXFSRST; + else + mcasp_set_clk_pdir(mcasp, false); + mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, val); mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF); @@ -294,6 +333,8 @@ static void mcasp_stop_tx(struct davinci_mcasp *mcasp) mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); } + + mcasp_set_axr_pdir(mcasp, false); } static void davinci_mcasp_stop(struct davinci_mcasp *mcasp, int stream) @@ -444,8 +485,13 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE); - mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, ACLKX | ACLKR); - mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AFSX | AFSR); + /* BCLK */ + set_bit(PIN_BIT_ACLKX, &mcasp->pdir); + set_bit(PIN_BIT_ACLKR, &mcasp->pdir); + /* Frame Sync */ + set_bit(PIN_BIT_AFSX, &mcasp->pdir); + set_bit(PIN_BIT_AFSR, &mcasp->pdir); + mcasp->bclk_master = 1; break; case SND_SOC_DAIFMT_CBS_CFM: @@ -456,8 +502,13 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE); - mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, ACLKX | ACLKR); - mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AFSX | AFSR); + /* BCLK */ + set_bit(PIN_BIT_ACLKX, &mcasp->pdir); + set_bit(PIN_BIT_ACLKR, &mcasp->pdir); + /* Frame Sync */ + clear_bit(PIN_BIT_AFSX, &mcasp->pdir); + clear_bit(PIN_BIT_AFSR, &mcasp->pdir); + mcasp->bclk_master = 1; break; case SND_SOC_DAIFMT_CBM_CFS: @@ -468,8 +519,13 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE); - mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, ACLKX | ACLKR); - mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AFSX | AFSR); + /* BCLK */ + clear_bit(PIN_BIT_ACLKX, &mcasp->pdir); + clear_bit(PIN_BIT_ACLKR, &mcasp->pdir); + /* Frame Sync */ + set_bit(PIN_BIT_AFSX, &mcasp->pdir); + set_bit(PIN_BIT_AFSR, &mcasp->pdir); + mcasp->bclk_master = 0; break; case SND_SOC_DAIFMT_CBM_CFM: @@ -480,8 +536,13 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE); - mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, - ACLKX | AFSX | ACLKR | AHCLKR | AFSR); + /* BCLK */ + clear_bit(PIN_BIT_ACLKX, &mcasp->pdir); + clear_bit(PIN_BIT_ACLKR, &mcasp->pdir); + /* Frame Sync */ + clear_bit(PIN_BIT_AFSX, &mcasp->pdir); + clear_bit(PIN_BIT_AFSR, &mcasp->pdir); + mcasp->bclk_master = 0; break; default: @@ -596,11 +657,11 @@ static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id, if (dir == SND_SOC_CLOCK_OUT) { mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE); mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE); - mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AHCLKX); + set_bit(PIN_BIT_AHCLKX, &mcasp->pdir); } else { mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE); mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE); - mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AHCLKX); + clear_bit(PIN_BIT_AHCLKX, &mcasp->pdir); } mcasp->sysclk_freq = freq; @@ -773,17 +834,23 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream, mcasp->serial_dir[i]); if (mcasp->serial_dir[i] == TX_MODE && tx_ser < max_active_serializers) { - mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AXR(i)); mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i), - DISMOD_LOW, DISMOD_MASK); + mcasp->dismod, DISMOD_MASK); + set_bit(PIN_BIT_AXR(i), &mcasp->pdir); tx_ser++; } else if (mcasp->serial_dir[i] == RX_MODE && rx_ser < max_active_serializers) { - mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AXR(i)); + clear_bit(PIN_BIT_AXR(i), &mcasp->pdir); rx_ser++; } else if (mcasp->serial_dir[i] == INACTIVE_MODE) { mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i), SRMOD_INACTIVE, SRMOD_MASK); + clear_bit(PIN_BIT_AXR(i), &mcasp->pdir); + } else if (mcasp->serial_dir[i] == TX_MODE) { + /* Unused TX pins, clear PDIR */ + mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i), + mcasp->dismod, DISMOD_MASK); + clear_bit(PIN_BIT_AXR(i), &mcasp->pdir); } } @@ -1645,6 +1712,7 @@ static struct davinci_mcasp_pdata *davinci_mcasp_set_pdata_from_of( if (pdev->dev.platform_data) { pdata = pdev->dev.platform_data; + pdata->dismod = DISMOD_LOW; return pdata; } else if (match) { pdata = devm_kmemdup(&pdev->dev, match->data, sizeof(*pdata), @@ -1734,6 +1802,18 @@ static struct davinci_mcasp_pdata *davinci_mcasp_set_pdata_from_of( if (ret >= 0) pdata->sram_size_capture = val; + ret = of_property_read_u32(np, "dismod", &val); + if (ret >= 0) { + if (val == 0 || val == 2 || val == 3) { + pdata->dismod = DISMOD_VAL(val); + } else { + dev_warn(&pdev->dev, "Invalid dismod value: %u\n", val); + pdata->dismod = DISMOD_LOW; + } + } else { + pdata->dismod = DISMOD_LOW; + } + return pdata; nodata: @@ -1909,6 +1989,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) mcasp->version = pdata->version; mcasp->txnumevt = pdata->txnumevt; mcasp->rxnumevt = pdata->rxnumevt; + mcasp->dismod = pdata->dismod; mcasp->dev = &pdev->dev; @@ -2068,9 +2149,9 @@ static int davinci_mcasp_probe(struct platform_device *pdev) ret = davinci_mcasp_get_dma_type(mcasp); switch (ret) { case PCM_EDMA: -#if IS_BUILTIN(CONFIG_SND_EDMA_SOC) || \ - (IS_MODULE(CONFIG_SND_DAVINCI_SOC_MCASP) && \ - IS_MODULE(CONFIG_SND_EDMA_SOC)) +#if IS_BUILTIN(CONFIG_SND_SOC_TI_EDMA_PCM) || \ + (IS_MODULE(CONFIG_SND_SOC_DAVINCI_MCASP) && \ + IS_MODULE(CONFIG_SND_SOC_TI_EDMA_PCM)) ret = edma_pcm_platform_register(&pdev->dev); #else dev_err(&pdev->dev, "Missing SND_EDMA_SOC\n"); @@ -2079,9 +2160,9 @@ static int davinci_mcasp_probe(struct platform_device *pdev) #endif break; case PCM_SDMA: -#if IS_BUILTIN(CONFIG_SND_SDMA_SOC) || \ - (IS_MODULE(CONFIG_SND_DAVINCI_SOC_MCASP) && \ - IS_MODULE(CONFIG_SND_SDMA_SOC)) +#if IS_BUILTIN(CONFIG_SND_SOC_TI_SDMA_PCM) || \ + (IS_MODULE(CONFIG_SND_SOC_DAVINCI_MCASP) && \ + IS_MODULE(CONFIG_SND_SOC_TI_SDMA_PCM)) ret = sdma_pcm_platform_register(&pdev->dev, NULL, NULL); #else dev_err(&pdev->dev, "Missing SND_SDMA_SOC\n"); diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/ti/davinci-mcasp.h index afddc8010c54..5e4060d8fe56 100644 --- a/sound/soc/davinci/davinci-mcasp.h +++ b/sound/soc/ti/davinci-mcasp.h @@ -108,27 +108,18 @@ /* * DAVINCI_MCASP_PFUNC_REG - Pin Function / GPIO Enable Register Bits - */ -#define AXR(n) (1<<n) -#define PFUNC_AMUTE BIT(25) -#define ACLKX BIT(26) -#define AHCLKX BIT(27) -#define AFSX BIT(28) -#define ACLKR BIT(29) -#define AHCLKR BIT(30) -#define AFSR BIT(31) - -/* * DAVINCI_MCASP_PDIR_REG - Pin Direction Register Bits + * DAVINCI_MCASP_PDOUT_REG - Pin output in GPIO mode + * DAVINCI_MCASP_PDSET_REG - Pin input in GPIO mode */ -#define AXR(n) (1<<n) -#define PDIR_AMUTE BIT(25) -#define ACLKX BIT(26) -#define AHCLKX BIT(27) -#define AFSX BIT(28) -#define ACLKR BIT(29) -#define AHCLKR BIT(30) -#define AFSR BIT(31) +#define PIN_BIT_AXR(n) (n) +#define PIN_BIT_AMUTE 25 +#define PIN_BIT_ACLKX 26 +#define PIN_BIT_AHCLKX 27 +#define PIN_BIT_AFSX 28 +#define PIN_BIT_ACLKR 29 +#define PIN_BIT_AHCLKR 30 +#define PIN_BIT_AFSR 31 /* * DAVINCI_MCASP_TXDITCTL_REG - Transmit DIT Control Register Bits @@ -218,6 +209,7 @@ #define DISMOD_3STATE (0x0) #define DISMOD_LOW (0x2 << 2) #define DISMOD_HIGH (0x3 << 2) +#define DISMOD_VAL(x) ((x) << 2) #define DISMOD_MASK DISMOD_HIGH #define TXSTATE BIT(4) #define RXSTATE BIT(5) diff --git a/sound/soc/davinci/davinci-vcif.c b/sound/soc/ti/davinci-vcif.c index 5415b72393fa..5415b72393fa 100644 --- a/sound/soc/davinci/davinci-vcif.c +++ b/sound/soc/ti/davinci-vcif.c diff --git a/sound/soc/davinci/edma-pcm.c b/sound/soc/ti/edma-pcm.c index 59e588abe54b..59e588abe54b 100644 --- a/sound/soc/davinci/edma-pcm.c +++ b/sound/soc/ti/edma-pcm.c diff --git a/sound/soc/davinci/edma-pcm.h b/sound/soc/ti/edma-pcm.h index b0957744851c..8058bdb0f032 100644 --- a/sound/soc/davinci/edma-pcm.h +++ b/sound/soc/ti/edma-pcm.h @@ -20,13 +20,13 @@ #ifndef __EDMA_PCM_H__ #define __EDMA_PCM_H__ -#if IS_ENABLED(CONFIG_SND_EDMA_SOC) +#if IS_ENABLED(CONFIG_SND_SOC_TI_EDMA_PCM) int edma_pcm_platform_register(struct device *dev); #else static inline int edma_pcm_platform_register(struct device *dev) { return 0; } -#endif /* CONFIG_SND_EDMA_SOC */ +#endif /* CONFIG_SND_SOC_TI_EDMA_PCM */ #endif /* __EDMA_PCM_H__ */ diff --git a/sound/soc/omap/n810.c b/sound/soc/ti/n810.c index 9cfefe44a75f..9cfefe44a75f 100644 --- a/sound/soc/omap/n810.c +++ b/sound/soc/ti/n810.c diff --git a/sound/soc/omap/omap-abe-twl6040.c b/sound/soc/ti/omap-abe-twl6040.c index fed45b41f9d3..fed45b41f9d3 100644 --- a/sound/soc/omap/omap-abe-twl6040.c +++ b/sound/soc/ti/omap-abe-twl6040.c diff --git a/sound/soc/omap/omap-dmic.c b/sound/soc/ti/omap-dmic.c index cba9645b6487..cba9645b6487 100644 --- a/sound/soc/omap/omap-dmic.c +++ b/sound/soc/ti/omap-dmic.c diff --git a/sound/soc/omap/omap-dmic.h b/sound/soc/ti/omap-dmic.h index 231e728bff0e..231e728bff0e 100644 --- a/sound/soc/omap/omap-dmic.h +++ b/sound/soc/ti/omap-dmic.h diff --git a/sound/soc/omap/omap-hdmi-audio.c b/sound/soc/ti/omap-hdmi.c index 673a9eb153b2..673a9eb153b2 100644 --- a/sound/soc/omap/omap-hdmi-audio.c +++ b/sound/soc/ti/omap-hdmi.c diff --git a/sound/soc/omap/mcbsp.h b/sound/soc/ti/omap-mcbsp-priv.h index 46ae1269a698..7865cda4bf0a 100644 --- a/sound/soc/omap/mcbsp.h +++ b/sound/soc/ti/omap-mcbsp-priv.h @@ -1,28 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * sound/soc/omap/mcbsp.h - * * OMAP Multi-Channel Buffered Serial Port * * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com> * Peter Ujfalusi <peter.ujfalusi@ti.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. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ -#ifndef __ASOC_MCBSP_H -#define __ASOC_MCBSP_H + +#ifndef __OMAP_MCBSP_PRIV_H__ +#define __OMAP_MCBSP_PRIV_H__ + +#include <linux/platform_data/asoc-ti-mcbsp.h> #ifdef CONFIG_ARCH_OMAP1 #define mcbsp_omap1() 1 @@ -30,8 +17,6 @@ #define mcbsp_omap1() 0 #endif -#include <sound/dmaengine_pcm.h> - /* McBSP register numbers. Register address offset = num * reg_step */ enum { /* Common registers */ @@ -85,15 +70,6 @@ enum { OMAP_MCBSP_REG_SSELCR, }; -/* OMAP3 sidetone control registers */ -#define OMAP_ST_REG_REV 0x00 -#define OMAP_ST_REG_SYSCONFIG 0x10 -#define OMAP_ST_REG_IRQSTATUS 0x18 -#define OMAP_ST_REG_IRQENABLE 0x1C -#define OMAP_ST_REG_SGAINCR 0x24 -#define OMAP_ST_REG_SFIRCR 0x28 -#define OMAP_ST_REG_SSELCR 0x2C - /************************** McBSP SPCR1 bit definitions ***********************/ #define RRST BIT(0) #define RRDY BIT(1) @@ -202,24 +178,6 @@ enum { #define SIDLEMODE(value) (((value) & 0x3) << 3) #define CLOCKACTIVITY(value) (((value) & 0x3) << 8) -/********************** McBSP SSELCR bit definitions ***********************/ -#define SIDETONEEN BIT(10) - -/********************** McBSP Sidetone SYSCONFIG bit definitions ***********/ -#define ST_AUTOIDLE BIT(0) - -/********************** McBSP Sidetone SGAINCR bit definitions *************/ -#define ST_CH0GAIN(value) ((value) & 0xffff) /* Bits 0:15 */ -#define ST_CH1GAIN(value) (((value) & 0xffff) << 16) /* Bits 16:31 */ - -/********************** McBSP Sidetone SFIRCR bit definitions **************/ -#define ST_FIRCOEFF(value) ((value) & 0xffff) /* Bits 0:15 */ - -/********************** McBSP Sidetone SSELCR bit definitions **************/ -#define ST_SIDETONEEN BIT(0) -#define ST_COEFFWREN BIT(1) -#define ST_COEFFWRDONE BIT(2) - /********************** McBSP DMA operating modes **************************/ #define MCBSP_DMA_MODE_ELEMENT 0 #define MCBSP_DMA_MODE_THRESHOLD 1 @@ -278,16 +236,7 @@ struct omap_mcbsp_reg_cfg { u16 rccr; }; -struct omap_mcbsp_st_data { - void __iomem *io_base_st; - struct clk *mcbsp_iclk; - bool running; - bool enabled; - s16 taps[128]; /* Sidetone filter coefficients */ - int nr_taps; /* Number of filter coefficients in use */ - s16 ch0gain; - s16 ch1gain; -}; +struct omap_mcbsp_st_data; struct omap_mcbsp { struct device *dev; @@ -330,29 +279,46 @@ struct omap_mcbsp { struct pm_qos_request pm_qos_req; }; -void omap_mcbsp_config(struct omap_mcbsp *mcbsp, - const struct omap_mcbsp_reg_cfg *config); -void omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold); -void omap_mcbsp_set_rx_threshold(struct omap_mcbsp *mcbsp, u16 threshold); -u16 omap_mcbsp_get_tx_delay(struct omap_mcbsp *mcbsp); -u16 omap_mcbsp_get_rx_delay(struct omap_mcbsp *mcbsp); -int omap_mcbsp_get_dma_op_mode(struct omap_mcbsp *mcbsp); -int omap_mcbsp_request(struct omap_mcbsp *mcbsp); -void omap_mcbsp_free(struct omap_mcbsp *mcbsp); -void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int tx, int rx); -void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int tx, int rx); - -/* McBSP functional clock source changing function */ -int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id); +static inline void omap_mcbsp_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val) +{ + void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step; + + if (mcbsp->pdata->reg_size == 2) { + ((u16 *)mcbsp->reg_cache)[reg] = (u16)val; + writew_relaxed((u16)val, addr); + } else { + ((u32 *)mcbsp->reg_cache)[reg] = val; + writel_relaxed(val, addr); + } +} + +static inline int omap_mcbsp_read(struct omap_mcbsp *mcbsp, u16 reg, + bool from_cache) +{ + void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step; + + if (mcbsp->pdata->reg_size == 2) { + return !from_cache ? readw_relaxed(addr) : + ((u16 *)mcbsp->reg_cache)[reg]; + } else { + return !from_cache ? readl_relaxed(addr) : + ((u32 *)mcbsp->reg_cache)[reg]; + } +} + +#define MCBSP_READ(mcbsp, reg) \ + omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 0) +#define MCBSP_WRITE(mcbsp, reg, val) \ + omap_mcbsp_write(mcbsp, OMAP_MCBSP_REG_##reg, val) +#define MCBSP_READ_CACHE(mcbsp, reg) \ + omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 1) + /* Sidetone specific API */ -int omap_st_set_chgain(struct omap_mcbsp *mcbsp, int channel, s16 chgain); -int omap_st_get_chgain(struct omap_mcbsp *mcbsp, int channel, s16 *chgain); -int omap_st_enable(struct omap_mcbsp *mcbsp); -int omap_st_disable(struct omap_mcbsp *mcbsp); -int omap_st_is_enabled(struct omap_mcbsp *mcbsp); +int omap_mcbsp_st_init(struct platform_device *pdev); +void omap_mcbsp_st_cleanup(struct platform_device *pdev); -int omap_mcbsp_init(struct platform_device *pdev); -void omap_mcbsp_cleanup(struct omap_mcbsp *mcbsp); +int omap_mcbsp_st_start(struct omap_mcbsp *mcbsp); +int omap_mcbsp_st_stop(struct omap_mcbsp *mcbsp); -#endif /* __ASOC_MCBSP_H */ +#endif /* __OMAP_MCBSP_PRIV_H__ */ diff --git a/sound/soc/ti/omap-mcbsp-st.c b/sound/soc/ti/omap-mcbsp-st.c new file mode 100644 index 000000000000..1a3fe854e856 --- /dev/null +++ b/sound/soc/ti/omap-mcbsp-st.c @@ -0,0 +1,516 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * McBSP Sidetone support + * + * Copyright (C) 2004 Nokia Corporation + * Author: Samuel Ortiz <samuel.ortiz@nokia.com> + * + * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com> + * Peter Ujfalusi <peter.ujfalusi@ti.com> + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/pm_runtime.h> + +#include "omap-mcbsp.h" +#include "omap-mcbsp-priv.h" + +/* OMAP3 sidetone control registers */ +#define OMAP_ST_REG_REV 0x00 +#define OMAP_ST_REG_SYSCONFIG 0x10 +#define OMAP_ST_REG_IRQSTATUS 0x18 +#define OMAP_ST_REG_IRQENABLE 0x1C +#define OMAP_ST_REG_SGAINCR 0x24 +#define OMAP_ST_REG_SFIRCR 0x28 +#define OMAP_ST_REG_SSELCR 0x2C + +/********************** McBSP SSELCR bit definitions ***********************/ +#define SIDETONEEN BIT(10) + +/********************** McBSP Sidetone SYSCONFIG bit definitions ***********/ +#define ST_AUTOIDLE BIT(0) + +/********************** McBSP Sidetone SGAINCR bit definitions *************/ +#define ST_CH0GAIN(value) ((value) & 0xffff) /* Bits 0:15 */ +#define ST_CH1GAIN(value) (((value) & 0xffff) << 16) /* Bits 16:31 */ + +/********************** McBSP Sidetone SFIRCR bit definitions **************/ +#define ST_FIRCOEFF(value) ((value) & 0xffff) /* Bits 0:15 */ + +/********************** McBSP Sidetone SSELCR bit definitions **************/ +#define ST_SIDETONEEN BIT(0) +#define ST_COEFFWREN BIT(1) +#define ST_COEFFWRDONE BIT(2) + +struct omap_mcbsp_st_data { + void __iomem *io_base_st; + struct clk *mcbsp_iclk; + bool running; + bool enabled; + s16 taps[128]; /* Sidetone filter coefficients */ + int nr_taps; /* Number of filter coefficients in use */ + s16 ch0gain; + s16 ch1gain; +}; + +static void omap_mcbsp_st_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val) +{ + writel_relaxed(val, mcbsp->st_data->io_base_st + reg); +} + +static int omap_mcbsp_st_read(struct omap_mcbsp *mcbsp, u16 reg) +{ + return readl_relaxed(mcbsp->st_data->io_base_st + reg); +} + +#define MCBSP_ST_READ(mcbsp, reg) omap_mcbsp_st_read(mcbsp, OMAP_ST_REG_##reg) +#define MCBSP_ST_WRITE(mcbsp, reg, val) \ + omap_mcbsp_st_write(mcbsp, OMAP_ST_REG_##reg, val) + +static void omap_mcbsp_st_on(struct omap_mcbsp *mcbsp) +{ + unsigned int w; + + if (mcbsp->pdata->force_ick_on) + mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, true); + + /* Disable Sidetone clock auto-gating for normal operation */ + w = MCBSP_ST_READ(mcbsp, SYSCONFIG); + MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w & ~(ST_AUTOIDLE)); + + /* Enable McBSP Sidetone */ + w = MCBSP_READ(mcbsp, SSELCR); + MCBSP_WRITE(mcbsp, SSELCR, w | SIDETONEEN); + + /* Enable Sidetone from Sidetone Core */ + w = MCBSP_ST_READ(mcbsp, SSELCR); + MCBSP_ST_WRITE(mcbsp, SSELCR, w | ST_SIDETONEEN); +} + +static void omap_mcbsp_st_off(struct omap_mcbsp *mcbsp) +{ + unsigned int w; + + w = MCBSP_ST_READ(mcbsp, SSELCR); + MCBSP_ST_WRITE(mcbsp, SSELCR, w & ~(ST_SIDETONEEN)); + + w = MCBSP_READ(mcbsp, SSELCR); + MCBSP_WRITE(mcbsp, SSELCR, w & ~(SIDETONEEN)); + + /* Enable Sidetone clock auto-gating to reduce power consumption */ + w = MCBSP_ST_READ(mcbsp, SYSCONFIG); + MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w | ST_AUTOIDLE); + + if (mcbsp->pdata->force_ick_on) + mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, false); +} + +static void omap_mcbsp_st_fir_write(struct omap_mcbsp *mcbsp, s16 *fir) +{ + u16 val, i; + + val = MCBSP_ST_READ(mcbsp, SSELCR); + + if (val & ST_COEFFWREN) + MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN)); + + MCBSP_ST_WRITE(mcbsp, SSELCR, val | ST_COEFFWREN); + + for (i = 0; i < 128; i++) + MCBSP_ST_WRITE(mcbsp, SFIRCR, fir[i]); + + i = 0; + + val = MCBSP_ST_READ(mcbsp, SSELCR); + while (!(val & ST_COEFFWRDONE) && (++i < 1000)) + val = MCBSP_ST_READ(mcbsp, SSELCR); + + MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN)); + + if (i == 1000) + dev_err(mcbsp->dev, "McBSP FIR load error!\n"); +} + +static void omap_mcbsp_st_chgain(struct omap_mcbsp *mcbsp) +{ + u16 w; + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + + w = MCBSP_ST_READ(mcbsp, SSELCR); + + MCBSP_ST_WRITE(mcbsp, SGAINCR, ST_CH0GAIN(st_data->ch0gain) | + ST_CH1GAIN(st_data->ch1gain)); +} + +static int omap_mcbsp_st_set_chgain(struct omap_mcbsp *mcbsp, int channel, + s16 chgain) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + int ret = 0; + + if (!st_data) + return -ENOENT; + + spin_lock_irq(&mcbsp->lock); + if (channel == 0) + st_data->ch0gain = chgain; + else if (channel == 1) + st_data->ch1gain = chgain; + else + ret = -EINVAL; + + if (st_data->enabled) + omap_mcbsp_st_chgain(mcbsp); + spin_unlock_irq(&mcbsp->lock); + + return ret; +} + +static int omap_mcbsp_st_get_chgain(struct omap_mcbsp *mcbsp, int channel, + s16 *chgain) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + int ret = 0; + + if (!st_data) + return -ENOENT; + + spin_lock_irq(&mcbsp->lock); + if (channel == 0) + *chgain = st_data->ch0gain; + else if (channel == 1) + *chgain = st_data->ch1gain; + else + ret = -EINVAL; + spin_unlock_irq(&mcbsp->lock); + + return ret; +} + +static int omap_mcbsp_st_enable(struct omap_mcbsp *mcbsp) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + + if (!st_data) + return -ENODEV; + + spin_lock_irq(&mcbsp->lock); + st_data->enabled = 1; + omap_mcbsp_st_start(mcbsp); + spin_unlock_irq(&mcbsp->lock); + + return 0; +} + +static int omap_mcbsp_st_disable(struct omap_mcbsp *mcbsp) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + int ret = 0; + + if (!st_data) + return -ENODEV; + + spin_lock_irq(&mcbsp->lock); + omap_mcbsp_st_stop(mcbsp); + st_data->enabled = 0; + spin_unlock_irq(&mcbsp->lock); + + return ret; +} + +static int omap_mcbsp_st_is_enabled(struct omap_mcbsp *mcbsp) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + + if (!st_data) + return -ENODEV; + + return st_data->enabled; +} + +static ssize_t st_taps_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + ssize_t status = 0; + int i; + + spin_lock_irq(&mcbsp->lock); + for (i = 0; i < st_data->nr_taps; i++) + status += sprintf(&buf[status], (i ? ", %d" : "%d"), + st_data->taps[i]); + if (i) + status += sprintf(&buf[status], "\n"); + spin_unlock_irq(&mcbsp->lock); + + return status; +} + +static ssize_t st_taps_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + int val, tmp, status, i = 0; + + spin_lock_irq(&mcbsp->lock); + memset(st_data->taps, 0, sizeof(st_data->taps)); + st_data->nr_taps = 0; + + do { + status = sscanf(buf, "%d%n", &val, &tmp); + if (status < 0 || status == 0) { + size = -EINVAL; + goto out; + } + if (val < -32768 || val > 32767) { + size = -EINVAL; + goto out; + } + st_data->taps[i++] = val; + buf += tmp; + if (*buf != ',') + break; + buf++; + } while (1); + + st_data->nr_taps = i; + +out: + spin_unlock_irq(&mcbsp->lock); + + return size; +} + +static DEVICE_ATTR_RW(st_taps); + +static const struct attribute *sidetone_attrs[] = { + &dev_attr_st_taps.attr, + NULL, +}; + +static const struct attribute_group sidetone_attr_group = { + .attrs = (struct attribute **)sidetone_attrs, +}; + +int omap_mcbsp_st_start(struct omap_mcbsp *mcbsp) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + + if (st_data->enabled && !st_data->running) { + omap_mcbsp_st_fir_write(mcbsp, st_data->taps); + omap_mcbsp_st_chgain(mcbsp); + + if (!mcbsp->free) { + omap_mcbsp_st_on(mcbsp); + st_data->running = 1; + } + } + + return 0; +} + +int omap_mcbsp_st_stop(struct omap_mcbsp *mcbsp) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + + if (st_data->running) { + if (!mcbsp->free) { + omap_mcbsp_st_off(mcbsp); + st_data->running = 0; + } + } + + return 0; +} + +int omap_mcbsp_st_init(struct platform_device *pdev) +{ + struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); + struct omap_mcbsp_st_data *st_data; + struct resource *res; + int ret; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sidetone"); + if (!res) + return 0; + + st_data = devm_kzalloc(mcbsp->dev, sizeof(*mcbsp->st_data), GFP_KERNEL); + if (!st_data) + return -ENOMEM; + + st_data->mcbsp_iclk = clk_get(mcbsp->dev, "ick"); + if (IS_ERR(st_data->mcbsp_iclk)) { + dev_warn(mcbsp->dev, + "Failed to get ick, sidetone might be broken\n"); + st_data->mcbsp_iclk = NULL; + } + + st_data->io_base_st = devm_ioremap(mcbsp->dev, res->start, + resource_size(res)); + if (!st_data->io_base_st) + return -ENOMEM; + + ret = sysfs_create_group(&mcbsp->dev->kobj, &sidetone_attr_group); + if (ret) + return ret; + + mcbsp->st_data = st_data; + + return 0; +} + +void omap_mcbsp_st_cleanup(struct platform_device *pdev) +{ + struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); + + if (mcbsp->st_data) { + sysfs_remove_group(&mcbsp->dev->kobj, &sidetone_attr_group); + clk_put(mcbsp->st_data->mcbsp_iclk); + } +} + +static int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int max = mc->max; + int min = mc->min; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = min; + uinfo->value.integer.max = max; + return 0; +} + +#define OMAP_MCBSP_ST_CHANNEL_VOLUME(channel) \ +static int \ +omap_mcbsp_set_st_ch##channel##_volume(struct snd_kcontrol *kc, \ + struct snd_ctl_elem_value *uc) \ +{ \ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \ + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \ + struct soc_mixer_control *mc = \ + (struct soc_mixer_control *)kc->private_value; \ + int max = mc->max; \ + int min = mc->min; \ + int val = uc->value.integer.value[0]; \ + \ + if (val < min || val > max) \ + return -EINVAL; \ + \ + /* OMAP McBSP implementation uses index values 0..4 */ \ + return omap_mcbsp_st_set_chgain(mcbsp, channel, val); \ +} \ + \ +static int \ +omap_mcbsp_get_st_ch##channel##_volume(struct snd_kcontrol *kc, \ + struct snd_ctl_elem_value *uc) \ +{ \ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \ + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \ + s16 chgain; \ + \ + if (omap_mcbsp_st_get_chgain(mcbsp, channel, &chgain)) \ + return -EAGAIN; \ + \ + uc->value.integer.value[0] = chgain; \ + return 0; \ +} + +OMAP_MCBSP_ST_CHANNEL_VOLUME(0) +OMAP_MCBSP_ST_CHANNEL_VOLUME(1) + +static int omap_mcbsp_st_put_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); + u8 value = ucontrol->value.integer.value[0]; + + if (value == omap_mcbsp_st_is_enabled(mcbsp)) + return 0; + + if (value) + omap_mcbsp_st_enable(mcbsp); + else + omap_mcbsp_st_disable(mcbsp); + + return 1; +} + +static int omap_mcbsp_st_get_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); + + ucontrol->value.integer.value[0] = omap_mcbsp_st_is_enabled(mcbsp); + return 0; +} + +#define OMAP_MCBSP_SOC_SINGLE_S16_EXT(xname, xmin, xmax, \ + xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = omap_mcbsp_st_info_volsw, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(struct soc_mixer_control) \ + {.min = xmin, .max = xmax} } + +#define OMAP_MCBSP_ST_CONTROLS(port) \ +static const struct snd_kcontrol_new omap_mcbsp##port##_st_controls[] = { \ +SOC_SINGLE_EXT("McBSP" #port " Sidetone Switch", 1, 0, 1, 0, \ + omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode), \ +OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 0 Volume", \ + -32768, 32767, \ + omap_mcbsp_get_st_ch0_volume, \ + omap_mcbsp_set_st_ch0_volume), \ +OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 1 Volume", \ + -32768, 32767, \ + omap_mcbsp_get_st_ch1_volume, \ + omap_mcbsp_set_st_ch1_volume), \ +} + +OMAP_MCBSP_ST_CONTROLS(2); +OMAP_MCBSP_ST_CONTROLS(3); + +int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); + + if (!mcbsp->st_data) { + dev_warn(mcbsp->dev, "No sidetone data for port\n"); + return 0; + } + + switch (port_id) { + case 2: /* McBSP 2 */ + return snd_soc_add_dai_controls(cpu_dai, + omap_mcbsp2_st_controls, + ARRAY_SIZE(omap_mcbsp2_st_controls)); + case 3: /* McBSP 3 */ + return snd_soc_add_dai_controls(cpu_dai, + omap_mcbsp3_st_controls, + ARRAY_SIZE(omap_mcbsp3_st_controls)); + default: + dev_err(mcbsp->dev, "Port %d not supported\n", port_id); + break; + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(omap_mcbsp_st_add_controls); diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/ti/omap-mcbsp.c index 2d6decbfc99e..a395598f1f20 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/ti/omap-mcbsp.c @@ -35,21 +35,12 @@ #include <sound/soc.h> #include <sound/dmaengine_pcm.h> -#include <linux/platform_data/asoc-ti-mcbsp.h> -#include "mcbsp.h" +#include "omap-mcbsp-priv.h" #include "omap-mcbsp.h" #include "sdma-pcm.h" #define OMAP_MCBSP_RATES (SNDRV_PCM_RATE_8000_96000) -#define OMAP_MCBSP_SOC_SINGLE_S16_EXT(xname, xmin, xmax, \ - xhandler_get, xhandler_put) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .info = omap_mcbsp_st_info_volsw, \ - .get = xhandler_get, .put = xhandler_put, \ - .private_value = (unsigned long) &(struct soc_mixer_control) \ - {.min = xmin, .max = xmax} } - enum { OMAP_MCBSP_WORD_8 = 0, OMAP_MCBSP_WORD_12, @@ -59,6 +50,699 @@ enum { OMAP_MCBSP_WORD_32, }; +static void omap_mcbsp_dump_reg(struct omap_mcbsp *mcbsp) +{ + dev_dbg(mcbsp->dev, "**** McBSP%d regs ****\n", mcbsp->id); + dev_dbg(mcbsp->dev, "DRR2: 0x%04x\n", MCBSP_READ(mcbsp, DRR2)); + dev_dbg(mcbsp->dev, "DRR1: 0x%04x\n", MCBSP_READ(mcbsp, DRR1)); + dev_dbg(mcbsp->dev, "DXR2: 0x%04x\n", MCBSP_READ(mcbsp, DXR2)); + dev_dbg(mcbsp->dev, "DXR1: 0x%04x\n", MCBSP_READ(mcbsp, DXR1)); + dev_dbg(mcbsp->dev, "SPCR2: 0x%04x\n", MCBSP_READ(mcbsp, SPCR2)); + dev_dbg(mcbsp->dev, "SPCR1: 0x%04x\n", MCBSP_READ(mcbsp, SPCR1)); + dev_dbg(mcbsp->dev, "RCR2: 0x%04x\n", MCBSP_READ(mcbsp, RCR2)); + dev_dbg(mcbsp->dev, "RCR1: 0x%04x\n", MCBSP_READ(mcbsp, RCR1)); + dev_dbg(mcbsp->dev, "XCR2: 0x%04x\n", MCBSP_READ(mcbsp, XCR2)); + dev_dbg(mcbsp->dev, "XCR1: 0x%04x\n", MCBSP_READ(mcbsp, XCR1)); + dev_dbg(mcbsp->dev, "SRGR2: 0x%04x\n", MCBSP_READ(mcbsp, SRGR2)); + dev_dbg(mcbsp->dev, "SRGR1: 0x%04x\n", MCBSP_READ(mcbsp, SRGR1)); + dev_dbg(mcbsp->dev, "PCR0: 0x%04x\n", MCBSP_READ(mcbsp, PCR0)); + dev_dbg(mcbsp->dev, "***********************\n"); +} + +static int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id) +{ + struct clk *fck_src; + const char *src; + int r; + + if (fck_src_id == MCBSP_CLKS_PAD_SRC) + src = "pad_fck"; + else if (fck_src_id == MCBSP_CLKS_PRCM_SRC) + src = "prcm_fck"; + else + return -EINVAL; + + fck_src = clk_get(mcbsp->dev, src); + if (IS_ERR(fck_src)) { + dev_err(mcbsp->dev, "CLKS: could not clk_get() %s\n", src); + return -EINVAL; + } + + pm_runtime_put_sync(mcbsp->dev); + + r = clk_set_parent(mcbsp->fclk, fck_src); + if (r) { + dev_err(mcbsp->dev, "CLKS: could not clk_set_parent() to %s\n", + src); + clk_put(fck_src); + return r; + } + + pm_runtime_get_sync(mcbsp->dev); + + clk_put(fck_src); + + return 0; +} + +static irqreturn_t omap_mcbsp_irq_handler(int irq, void *data) +{ + struct omap_mcbsp *mcbsp = data; + u16 irqst; + + irqst = MCBSP_READ(mcbsp, IRQST); + dev_dbg(mcbsp->dev, "IRQ callback : 0x%x\n", irqst); + + if (irqst & RSYNCERREN) + dev_err(mcbsp->dev, "RX Frame Sync Error!\n"); + if (irqst & RFSREN) + dev_dbg(mcbsp->dev, "RX Frame Sync\n"); + if (irqst & REOFEN) + dev_dbg(mcbsp->dev, "RX End Of Frame\n"); + if (irqst & RRDYEN) + dev_dbg(mcbsp->dev, "RX Buffer Threshold Reached\n"); + if (irqst & RUNDFLEN) + dev_err(mcbsp->dev, "RX Buffer Underflow!\n"); + if (irqst & ROVFLEN) + dev_err(mcbsp->dev, "RX Buffer Overflow!\n"); + + if (irqst & XSYNCERREN) + dev_err(mcbsp->dev, "TX Frame Sync Error!\n"); + if (irqst & XFSXEN) + dev_dbg(mcbsp->dev, "TX Frame Sync\n"); + if (irqst & XEOFEN) + dev_dbg(mcbsp->dev, "TX End Of Frame\n"); + if (irqst & XRDYEN) + dev_dbg(mcbsp->dev, "TX Buffer threshold Reached\n"); + if (irqst & XUNDFLEN) + dev_err(mcbsp->dev, "TX Buffer Underflow!\n"); + if (irqst & XOVFLEN) + dev_err(mcbsp->dev, "TX Buffer Overflow!\n"); + if (irqst & XEMPTYEOFEN) + dev_dbg(mcbsp->dev, "TX Buffer empty at end of frame\n"); + + MCBSP_WRITE(mcbsp, IRQST, irqst); + + return IRQ_HANDLED; +} + +static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *data) +{ + struct omap_mcbsp *mcbsp = data; + u16 irqst_spcr2; + + irqst_spcr2 = MCBSP_READ(mcbsp, SPCR2); + dev_dbg(mcbsp->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2); + + if (irqst_spcr2 & XSYNC_ERR) { + dev_err(mcbsp->dev, "TX Frame Sync Error! : 0x%x\n", + irqst_spcr2); + /* Writing zero to XSYNC_ERR clears the IRQ */ + MCBSP_WRITE(mcbsp, SPCR2, MCBSP_READ_CACHE(mcbsp, SPCR2)); + } + + return IRQ_HANDLED; +} + +static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *data) +{ + struct omap_mcbsp *mcbsp = data; + u16 irqst_spcr1; + + irqst_spcr1 = MCBSP_READ(mcbsp, SPCR1); + dev_dbg(mcbsp->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1); + + if (irqst_spcr1 & RSYNC_ERR) { + dev_err(mcbsp->dev, "RX Frame Sync Error! : 0x%x\n", + irqst_spcr1); + /* Writing zero to RSYNC_ERR clears the IRQ */ + MCBSP_WRITE(mcbsp, SPCR1, MCBSP_READ_CACHE(mcbsp, SPCR1)); + } + + return IRQ_HANDLED; +} + +/* + * omap_mcbsp_config simply write a config to the + * appropriate McBSP. + * You either call this function or set the McBSP registers + * by yourself before calling omap_mcbsp_start(). + */ +static void omap_mcbsp_config(struct omap_mcbsp *mcbsp, + const struct omap_mcbsp_reg_cfg *config) +{ + dev_dbg(mcbsp->dev, "Configuring McBSP%d phys_base: 0x%08lx\n", + mcbsp->id, mcbsp->phys_base); + + /* We write the given config */ + MCBSP_WRITE(mcbsp, SPCR2, config->spcr2); + MCBSP_WRITE(mcbsp, SPCR1, config->spcr1); + MCBSP_WRITE(mcbsp, RCR2, config->rcr2); + MCBSP_WRITE(mcbsp, RCR1, config->rcr1); + MCBSP_WRITE(mcbsp, XCR2, config->xcr2); + MCBSP_WRITE(mcbsp, XCR1, config->xcr1); + MCBSP_WRITE(mcbsp, SRGR2, config->srgr2); + MCBSP_WRITE(mcbsp, SRGR1, config->srgr1); + MCBSP_WRITE(mcbsp, MCR2, config->mcr2); + MCBSP_WRITE(mcbsp, MCR1, config->mcr1); + MCBSP_WRITE(mcbsp, PCR0, config->pcr0); + if (mcbsp->pdata->has_ccr) { + MCBSP_WRITE(mcbsp, XCCR, config->xccr); + MCBSP_WRITE(mcbsp, RCCR, config->rccr); + } + /* Enable wakeup behavior */ + if (mcbsp->pdata->has_wakeup) + MCBSP_WRITE(mcbsp, WAKEUPEN, XRDYEN | RRDYEN); + + /* Enable TX/RX sync error interrupts by default */ + if (mcbsp->irq) + MCBSP_WRITE(mcbsp, IRQEN, RSYNCERREN | XSYNCERREN | + RUNDFLEN | ROVFLEN | XUNDFLEN | XOVFLEN); +} + +/** + * omap_mcbsp_dma_reg_params - returns the address of mcbsp data register + * @mcbsp: omap_mcbsp struct for the McBSP instance + * @stream: Stream direction (playback/capture) + * + * Returns the address of mcbsp data transmit register or data receive register + * to be used by DMA for transferring/receiving data + */ +static int omap_mcbsp_dma_reg_params(struct omap_mcbsp *mcbsp, + unsigned int stream) +{ + int data_reg; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (mcbsp->pdata->reg_size == 2) + data_reg = OMAP_MCBSP_REG_DXR1; + else + data_reg = OMAP_MCBSP_REG_DXR; + } else { + if (mcbsp->pdata->reg_size == 2) + data_reg = OMAP_MCBSP_REG_DRR1; + else + data_reg = OMAP_MCBSP_REG_DRR; + } + + return mcbsp->phys_dma_base + data_reg * mcbsp->pdata->reg_step; +} + +/* + * omap_mcbsp_set_rx_threshold configures the transmit threshold in words. + * The threshold parameter is 1 based, and it is converted (threshold - 1) + * for the THRSH2 register. + */ +static void omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) +{ + if (threshold && threshold <= mcbsp->max_tx_thres) + MCBSP_WRITE(mcbsp, THRSH2, threshold - 1); +} + +/* + * omap_mcbsp_set_rx_threshold configures the receive threshold in words. + * The threshold parameter is 1 based, and it is converted (threshold - 1) + * for the THRSH1 register. + */ +static void omap_mcbsp_set_rx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) +{ + if (threshold && threshold <= mcbsp->max_rx_thres) + MCBSP_WRITE(mcbsp, THRSH1, threshold - 1); +} + +/* + * omap_mcbsp_get_tx_delay returns the number of used slots in the McBSP FIFO + */ +static u16 omap_mcbsp_get_tx_delay(struct omap_mcbsp *mcbsp) +{ + u16 buffstat; + + /* Returns the number of free locations in the buffer */ + buffstat = MCBSP_READ(mcbsp, XBUFFSTAT); + + /* Number of slots are different in McBSP ports */ + return mcbsp->pdata->buffer_size - buffstat; +} + +/* + * omap_mcbsp_get_rx_delay returns the number of free slots in the McBSP FIFO + * to reach the threshold value (when the DMA will be triggered to read it) + */ +static u16 omap_mcbsp_get_rx_delay(struct omap_mcbsp *mcbsp) +{ + u16 buffstat, threshold; + + /* Returns the number of used locations in the buffer */ + buffstat = MCBSP_READ(mcbsp, RBUFFSTAT); + /* RX threshold */ + threshold = MCBSP_READ(mcbsp, THRSH1); + + /* Return the number of location till we reach the threshold limit */ + if (threshold <= buffstat) + return 0; + else + return threshold - buffstat; +} + +static int omap_mcbsp_request(struct omap_mcbsp *mcbsp) +{ + void *reg_cache; + int err; + + reg_cache = kzalloc(mcbsp->reg_cache_size, GFP_KERNEL); + if (!reg_cache) + return -ENOMEM; + + spin_lock(&mcbsp->lock); + if (!mcbsp->free) { + dev_err(mcbsp->dev, "McBSP%d is currently in use\n", mcbsp->id); + err = -EBUSY; + goto err_kfree; + } + + mcbsp->free = false; + mcbsp->reg_cache = reg_cache; + spin_unlock(&mcbsp->lock); + + if(mcbsp->pdata->ops && mcbsp->pdata->ops->request) + mcbsp->pdata->ops->request(mcbsp->id - 1); + + /* + * Make sure that transmitter, receiver and sample-rate generator are + * not running before activating IRQs. + */ + MCBSP_WRITE(mcbsp, SPCR1, 0); + MCBSP_WRITE(mcbsp, SPCR2, 0); + + if (mcbsp->irq) { + err = request_irq(mcbsp->irq, omap_mcbsp_irq_handler, 0, + "McBSP", (void *)mcbsp); + if (err != 0) { + dev_err(mcbsp->dev, "Unable to request IRQ\n"); + goto err_clk_disable; + } + } else { + err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler, 0, + "McBSP TX", (void *)mcbsp); + if (err != 0) { + dev_err(mcbsp->dev, "Unable to request TX IRQ\n"); + goto err_clk_disable; + } + + err = request_irq(mcbsp->rx_irq, omap_mcbsp_rx_irq_handler, 0, + "McBSP RX", (void *)mcbsp); + if (err != 0) { + dev_err(mcbsp->dev, "Unable to request RX IRQ\n"); + goto err_free_irq; + } + } + + return 0; +err_free_irq: + free_irq(mcbsp->tx_irq, (void *)mcbsp); +err_clk_disable: + if(mcbsp->pdata->ops && mcbsp->pdata->ops->free) + mcbsp->pdata->ops->free(mcbsp->id - 1); + + /* Disable wakeup behavior */ + if (mcbsp->pdata->has_wakeup) + MCBSP_WRITE(mcbsp, WAKEUPEN, 0); + + spin_lock(&mcbsp->lock); + mcbsp->free = true; + mcbsp->reg_cache = NULL; +err_kfree: + spin_unlock(&mcbsp->lock); + kfree(reg_cache); + + return err; +} + +static void omap_mcbsp_free(struct omap_mcbsp *mcbsp) +{ + void *reg_cache; + + if(mcbsp->pdata->ops && mcbsp->pdata->ops->free) + mcbsp->pdata->ops->free(mcbsp->id - 1); + + /* Disable wakeup behavior */ + if (mcbsp->pdata->has_wakeup) + MCBSP_WRITE(mcbsp, WAKEUPEN, 0); + + /* Disable interrupt requests */ + if (mcbsp->irq) + MCBSP_WRITE(mcbsp, IRQEN, 0); + + if (mcbsp->irq) { + free_irq(mcbsp->irq, (void *)mcbsp); + } else { + free_irq(mcbsp->rx_irq, (void *)mcbsp); + free_irq(mcbsp->tx_irq, (void *)mcbsp); + } + + reg_cache = mcbsp->reg_cache; + + /* + * Select CLKS source from internal source unconditionally before + * marking the McBSP port as free. + * If the external clock source via MCBSP_CLKS pin has been selected the + * system will refuse to enter idle if the CLKS pin source is not reset + * back to internal source. + */ + if (!mcbsp_omap1()) + omap2_mcbsp_set_clks_src(mcbsp, MCBSP_CLKS_PRCM_SRC); + + spin_lock(&mcbsp->lock); + if (mcbsp->free) + dev_err(mcbsp->dev, "McBSP%d was not reserved\n", mcbsp->id); + else + mcbsp->free = true; + mcbsp->reg_cache = NULL; + spin_unlock(&mcbsp->lock); + + kfree(reg_cache); +} + +/* + * Here we start the McBSP, by enabling transmitter, receiver or both. + * If no transmitter or receiver is active prior calling, then sample-rate + * generator and frame sync are started. + */ +static void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int stream) +{ + int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK); + int rx = !tx; + int enable_srg = 0; + u16 w; + + if (mcbsp->st_data) + omap_mcbsp_st_start(mcbsp); + + /* Only enable SRG, if McBSP is master */ + w = MCBSP_READ_CACHE(mcbsp, PCR0); + if (w & (FSXM | FSRM | CLKXM | CLKRM)) + enable_srg = !((MCBSP_READ_CACHE(mcbsp, SPCR2) | + MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1); + + if (enable_srg) { + /* Start the sample generator */ + w = MCBSP_READ_CACHE(mcbsp, SPCR2); + MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 6)); + } + + /* Enable transmitter and receiver */ + tx &= 1; + w = MCBSP_READ_CACHE(mcbsp, SPCR2); + MCBSP_WRITE(mcbsp, SPCR2, w | tx); + + rx &= 1; + w = MCBSP_READ_CACHE(mcbsp, SPCR1); + MCBSP_WRITE(mcbsp, SPCR1, w | rx); + + /* + * Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec + * REVISIT: 100us may give enough time for two CLKSRG, however + * due to some unknown PM related, clock gating etc. reason it + * is now at 500us. + */ + udelay(500); + + if (enable_srg) { + /* Start frame sync */ + w = MCBSP_READ_CACHE(mcbsp, SPCR2); + MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 7)); + } + + if (mcbsp->pdata->has_ccr) { + /* Release the transmitter and receiver */ + w = MCBSP_READ_CACHE(mcbsp, XCCR); + w &= ~(tx ? XDISABLE : 0); + MCBSP_WRITE(mcbsp, XCCR, w); + w = MCBSP_READ_CACHE(mcbsp, RCCR); + w &= ~(rx ? RDISABLE : 0); + MCBSP_WRITE(mcbsp, RCCR, w); + } + + /* Dump McBSP Regs */ + omap_mcbsp_dump_reg(mcbsp); +} + +static void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int stream) +{ + int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK); + int rx = !tx; + int idle; + u16 w; + + /* Reset transmitter */ + tx &= 1; + if (mcbsp->pdata->has_ccr) { + w = MCBSP_READ_CACHE(mcbsp, XCCR); + w |= (tx ? XDISABLE : 0); + MCBSP_WRITE(mcbsp, XCCR, w); + } + w = MCBSP_READ_CACHE(mcbsp, SPCR2); + MCBSP_WRITE(mcbsp, SPCR2, w & ~tx); + + /* Reset receiver */ + rx &= 1; + if (mcbsp->pdata->has_ccr) { + w = MCBSP_READ_CACHE(mcbsp, RCCR); + w |= (rx ? RDISABLE : 0); + MCBSP_WRITE(mcbsp, RCCR, w); + } + w = MCBSP_READ_CACHE(mcbsp, SPCR1); + MCBSP_WRITE(mcbsp, SPCR1, w & ~rx); + + idle = !((MCBSP_READ_CACHE(mcbsp, SPCR2) | + MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1); + + if (idle) { + /* Reset the sample rate generator */ + w = MCBSP_READ_CACHE(mcbsp, SPCR2); + MCBSP_WRITE(mcbsp, SPCR2, w & ~(1 << 6)); + } + + if (mcbsp->st_data) + omap_mcbsp_st_stop(mcbsp); +} + +#define max_thres(m) (mcbsp->pdata->buffer_size) +#define valid_threshold(m, val) ((val) <= max_thres(m)) +#define THRESHOLD_PROP_BUILDER(prop) \ +static ssize_t prop##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \ + \ + return sprintf(buf, "%u\n", mcbsp->prop); \ +} \ + \ +static ssize_t prop##_store(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t size) \ +{ \ + struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \ + unsigned long val; \ + int status; \ + \ + status = kstrtoul(buf, 0, &val); \ + if (status) \ + return status; \ + \ + if (!valid_threshold(mcbsp, val)) \ + return -EDOM; \ + \ + mcbsp->prop = val; \ + return size; \ +} \ + \ +static DEVICE_ATTR(prop, 0644, prop##_show, prop##_store) + +THRESHOLD_PROP_BUILDER(max_tx_thres); +THRESHOLD_PROP_BUILDER(max_rx_thres); + +static const char * const dma_op_modes[] = { + "element", "threshold", +}; + +static ssize_t dma_op_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); + int dma_op_mode, i = 0; + ssize_t len = 0; + const char * const *s; + + dma_op_mode = mcbsp->dma_op_mode; + + for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) { + if (dma_op_mode == i) + len += sprintf(buf + len, "[%s] ", *s); + else + len += sprintf(buf + len, "%s ", *s); + } + len += sprintf(buf + len, "\n"); + + return len; +} + +static ssize_t dma_op_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t size) +{ + struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); + int i; + + i = sysfs_match_string(dma_op_modes, buf); + if (i < 0) + return i; + + spin_lock_irq(&mcbsp->lock); + if (!mcbsp->free) { + size = -EBUSY; + goto unlock; + } + mcbsp->dma_op_mode = i; + +unlock: + spin_unlock_irq(&mcbsp->lock); + + return size; +} + +static DEVICE_ATTR_RW(dma_op_mode); + +static const struct attribute *additional_attrs[] = { + &dev_attr_max_tx_thres.attr, + &dev_attr_max_rx_thres.attr, + &dev_attr_dma_op_mode.attr, + NULL, +}; + +static const struct attribute_group additional_attr_group = { + .attrs = (struct attribute **)additional_attrs, +}; + +/* + * McBSP1 and McBSP3 are directly mapped on 1610 and 1510. + * 730 has only 2 McBSP, and both of them are MPU peripherals. + */ +static int omap_mcbsp_init(struct platform_device *pdev) +{ + struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); + struct resource *res; + int ret = 0; + + spin_lock_init(&mcbsp->lock); + mcbsp->free = true; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); + if (!res) + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + mcbsp->io_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mcbsp->io_base)) + return PTR_ERR(mcbsp->io_base); + + mcbsp->phys_base = res->start; + mcbsp->reg_cache_size = resource_size(res); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); + if (!res) + mcbsp->phys_dma_base = mcbsp->phys_base; + else + mcbsp->phys_dma_base = res->start; + + /* + * OMAP1, 2 uses two interrupt lines: TX, RX + * OMAP2430, OMAP3 SoC have combined IRQ line as well. + * OMAP4 and newer SoC only have the combined IRQ line. + * Use the combined IRQ if available since it gives better debugging + * possibilities. + */ + mcbsp->irq = platform_get_irq_byname(pdev, "common"); + if (mcbsp->irq == -ENXIO) { + mcbsp->tx_irq = platform_get_irq_byname(pdev, "tx"); + + if (mcbsp->tx_irq == -ENXIO) { + mcbsp->irq = platform_get_irq(pdev, 0); + mcbsp->tx_irq = 0; + } else { + mcbsp->rx_irq = platform_get_irq_byname(pdev, "rx"); + mcbsp->irq = 0; + } + } + + if (!pdev->dev.of_node) { + res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); + if (!res) { + dev_err(&pdev->dev, "invalid tx DMA channel\n"); + return -ENODEV; + } + mcbsp->dma_req[0] = res->start; + mcbsp->dma_data[0].filter_data = &mcbsp->dma_req[0]; + + res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); + if (!res) { + dev_err(&pdev->dev, "invalid rx DMA channel\n"); + return -ENODEV; + } + mcbsp->dma_req[1] = res->start; + mcbsp->dma_data[1].filter_data = &mcbsp->dma_req[1]; + } else { + mcbsp->dma_data[0].filter_data = "tx"; + mcbsp->dma_data[1].filter_data = "rx"; + } + + mcbsp->dma_data[0].addr = omap_mcbsp_dma_reg_params(mcbsp, + SNDRV_PCM_STREAM_PLAYBACK); + mcbsp->dma_data[1].addr = omap_mcbsp_dma_reg_params(mcbsp, + SNDRV_PCM_STREAM_CAPTURE); + + mcbsp->fclk = clk_get(&pdev->dev, "fck"); + if (IS_ERR(mcbsp->fclk)) { + ret = PTR_ERR(mcbsp->fclk); + dev_err(mcbsp->dev, "unable to get fck: %d\n", ret); + return ret; + } + + mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT; + if (mcbsp->pdata->buffer_size) { + /* + * Initially configure the maximum thresholds to a safe value. + * The McBSP FIFO usage with these values should not go under + * 16 locations. + * If the whole FIFO without safety buffer is used, than there + * is a possibility that the DMA will be not able to push the + * new data on time, causing channel shifts in runtime. + */ + mcbsp->max_tx_thres = max_thres(mcbsp) - 0x10; + mcbsp->max_rx_thres = max_thres(mcbsp) - 0x10; + + ret = sysfs_create_group(&mcbsp->dev->kobj, + &additional_attr_group); + if (ret) { + dev_err(mcbsp->dev, + "Unable to create additional controls\n"); + goto err_thres; + } + } + + ret = omap_mcbsp_st_init(pdev); + if (ret) + goto err_st; + + return 0; + +err_st: + if (mcbsp->pdata->buffer_size) + sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group); +err_thres: + clk_put(mcbsp->fclk); + return ret; +} + /* * Stream DMA parameters. DMA request line and port address are set runtime * since they are different between OMAP1 and later OMAPs @@ -71,6 +755,10 @@ static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream, struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); int words; + /* No need to proceed further if McBSP does not have FIFO */ + if (mcbsp->pdata->buffer_size == 0) + return; + /* * Configure McBSP threshold based on either: * packet_size, when the sDMA is in packet mode, or based on the @@ -201,27 +889,26 @@ static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *cpu_dai) { struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); - int err = 0, play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: mcbsp->active++; - omap_mcbsp_start(mcbsp, play, !play); + omap_mcbsp_start(mcbsp, substream->stream); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - omap_mcbsp_stop(mcbsp, play, !play); + omap_mcbsp_stop(mcbsp, substream->stream); mcbsp->active--; break; default: - err = -EINVAL; + return -EINVAL; } - return err; + return 0; } static snd_pcm_sframes_t omap_mcbsp_dai_delay( @@ -234,6 +921,10 @@ static snd_pcm_sframes_t omap_mcbsp_dai_delay( u16 fifo_use; snd_pcm_sframes_t delay; + /* No need to proceed further if McBSP does not have FIFO */ + if (mcbsp->pdata->buffer_size == 0) + return 0; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) fifo_use = omap_mcbsp_get_tx_delay(mcbsp); else @@ -649,132 +1340,6 @@ static const struct snd_soc_component_driver omap_mcbsp_component = { .name = "omap-mcbsp", }; -static int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int max = mc->max; - int min = mc->min; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = min; - uinfo->value.integer.max = max; - return 0; -} - -#define OMAP_MCBSP_ST_CHANNEL_VOLUME(channel) \ -static int \ -omap_mcbsp_set_st_ch##channel##_volume(struct snd_kcontrol *kc, \ - struct snd_ctl_elem_value *uc) \ -{ \ - struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \ - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \ - struct soc_mixer_control *mc = \ - (struct soc_mixer_control *)kc->private_value; \ - int max = mc->max; \ - int min = mc->min; \ - int val = uc->value.integer.value[0]; \ - \ - if (val < min || val > max) \ - return -EINVAL; \ - \ - /* OMAP McBSP implementation uses index values 0..4 */ \ - return omap_st_set_chgain(mcbsp, channel, val); \ -} \ - \ -static int \ -omap_mcbsp_get_st_ch##channel##_volume(struct snd_kcontrol *kc, \ - struct snd_ctl_elem_value *uc) \ -{ \ - struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \ - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \ - s16 chgain; \ - \ - if (omap_st_get_chgain(mcbsp, channel, &chgain)) \ - return -EAGAIN; \ - \ - uc->value.integer.value[0] = chgain; \ - return 0; \ -} - -OMAP_MCBSP_ST_CHANNEL_VOLUME(0) -OMAP_MCBSP_ST_CHANNEL_VOLUME(1) - -static int omap_mcbsp_st_put_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); - u8 value = ucontrol->value.integer.value[0]; - - if (value == omap_st_is_enabled(mcbsp)) - return 0; - - if (value) - omap_st_enable(mcbsp); - else - omap_st_disable(mcbsp); - - return 1; -} - -static int omap_mcbsp_st_get_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); - - ucontrol->value.integer.value[0] = omap_st_is_enabled(mcbsp); - return 0; -} - -#define OMAP_MCBSP_ST_CONTROLS(port) \ -static const struct snd_kcontrol_new omap_mcbsp##port##_st_controls[] = { \ -SOC_SINGLE_EXT("McBSP" #port " Sidetone Switch", 1, 0, 1, 0, \ - omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode), \ -OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 0 Volume", \ - -32768, 32767, \ - omap_mcbsp_get_st_ch0_volume, \ - omap_mcbsp_set_st_ch0_volume), \ -OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 1 Volume", \ - -32768, 32767, \ - omap_mcbsp_get_st_ch1_volume, \ - omap_mcbsp_set_st_ch1_volume), \ -} - -OMAP_MCBSP_ST_CONTROLS(2); -OMAP_MCBSP_ST_CONTROLS(3); - -int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id) -{ - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); - - if (!mcbsp->st_data) { - dev_warn(mcbsp->dev, "No sidetone data for port\n"); - return 0; - } - - switch (port_id) { - case 2: /* McBSP 2 */ - return snd_soc_add_dai_controls(cpu_dai, - omap_mcbsp2_st_controls, - ARRAY_SIZE(omap_mcbsp2_st_controls)); - case 3: /* McBSP 3 */ - return snd_soc_add_dai_controls(cpu_dai, - omap_mcbsp3_st_controls, - ARRAY_SIZE(omap_mcbsp3_st_controls)); - default: - dev_err(mcbsp->dev, "Port %d not supported\n", port_id); - break; - } - - return -EINVAL; -} -EXPORT_SYMBOL_GPL(omap_mcbsp_st_add_controls); - static struct omap_mcbsp_platform_data omap2420_pdata = { .reg_step = 4, .reg_size = 2, @@ -862,6 +1427,11 @@ static int asoc_mcbsp_probe(struct platform_device *pdev) if (ret) return ret; + if (mcbsp->pdata->reg_size == 2) { + omap_mcbsp_dai.playback.formats = SNDRV_PCM_FMTBIT_S16_LE; + omap_mcbsp_dai.capture.formats = SNDRV_PCM_FMTBIT_S16_LE; + } + ret = devm_snd_soc_register_component(&pdev->dev, &omap_mcbsp_component, &omap_mcbsp_dai, 1); @@ -881,7 +1451,10 @@ static int asoc_mcbsp_remove(struct platform_device *pdev) if (pm_qos_request_active(&mcbsp->pm_qos_req)) pm_qos_remove_request(&mcbsp->pm_qos_req); - omap_mcbsp_cleanup(mcbsp); + if (mcbsp->pdata->buffer_size) + sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group); + + omap_mcbsp_st_cleanup(pdev); clk_put(mcbsp->fclk); diff --git a/sound/soc/omap/omap-mcbsp.h b/sound/soc/ti/omap-mcbsp.h index 2e3369c27be3..7911d24898c9 100644 --- a/sound/soc/omap/omap-mcbsp.h +++ b/sound/soc/ti/omap-mcbsp.h @@ -22,8 +22,10 @@ * */ -#ifndef __OMAP_I2S_H__ -#define __OMAP_I2S_H__ +#ifndef __OMAP_MCBSP_H__ +#define __OMAP_MCBSP_H__ + +#include <sound/dmaengine_pcm.h> /* Source clocks for McBSP sample rate generator */ enum omap_mcbsp_clksrg_clk { @@ -41,4 +43,4 @@ enum omap_mcbsp_div { int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id); -#endif +#endif /* __OMAP_MCBSP_H__ */ diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/ti/omap-mcpdm.c index 7d5bdc5a2890..7d5bdc5a2890 100644 --- a/sound/soc/omap/omap-mcpdm.c +++ b/sound/soc/ti/omap-mcpdm.c diff --git a/sound/soc/omap/omap-mcpdm.h b/sound/soc/ti/omap-mcpdm.h index de8cf26595b1..de8cf26595b1 100644 --- a/sound/soc/omap/omap-mcpdm.h +++ b/sound/soc/ti/omap-mcpdm.h diff --git a/sound/soc/omap/omap-twl4030.c b/sound/soc/ti/omap-twl4030.c index cccc316743fa..cccc316743fa 100644 --- a/sound/soc/omap/omap-twl4030.c +++ b/sound/soc/ti/omap-twl4030.c diff --git a/sound/soc/omap/omap3pandora.c b/sound/soc/ti/omap3pandora.c index 4e3de712159c..4e3de712159c 100644 --- a/sound/soc/omap/omap3pandora.c +++ b/sound/soc/ti/omap3pandora.c diff --git a/sound/soc/omap/osk5912.c b/sound/soc/ti/osk5912.c index e4096779ca05..e4096779ca05 100644 --- a/sound/soc/omap/osk5912.c +++ b/sound/soc/ti/osk5912.c diff --git a/sound/soc/omap/rx51.c b/sound/soc/ti/rx51.c index 57448bd5ad77..57448bd5ad77 100644 --- a/sound/soc/omap/rx51.c +++ b/sound/soc/ti/rx51.c diff --git a/sound/soc/omap/sdma-pcm.c b/sound/soc/ti/sdma-pcm.c index 21a9c2499d48..21a9c2499d48 100644 --- a/sound/soc/omap/sdma-pcm.c +++ b/sound/soc/ti/sdma-pcm.c diff --git a/sound/soc/omap/sdma-pcm.h b/sound/soc/ti/sdma-pcm.h index 34a7f90b2587..cb0627c8dd34 100644 --- a/sound/soc/omap/sdma-pcm.h +++ b/sound/soc/ti/sdma-pcm.h @@ -7,7 +7,7 @@ #ifndef __SDMA_PCM_H__ #define __SDMA_PCM_H__ -#if IS_ENABLED(CONFIG_SND_SDMA_SOC) +#if IS_ENABLED(CONFIG_SND_SOC_TI_SDMA_PCM) int sdma_pcm_platform_register(struct device *dev, char *txdmachan, char *rxdmachan); #else @@ -16,6 +16,6 @@ static inline int sdma_pcm_platform_register(struct device *dev, { return -ENODEV; } -#endif /* CONFIG_SND_SDMA_SOC */ +#endif /* CONFIG_SND_SOC_TI_SDMA_PCM */ #endif /* __SDMA_PCM_H__ */ diff --git a/sound/soc/xilinx/Kconfig b/sound/soc/xilinx/Kconfig new file mode 100644 index 000000000000..25e287feb58c --- /dev/null +++ b/sound/soc/xilinx/Kconfig @@ -0,0 +1,8 @@ +config SND_SOC_XILINX_I2S + tristate "Audio support for the the Xilinx I2S" + help + Select this option to enable Xilinx I2S Audio. This enables + I2S playback and capture using xilinx soft IP. In transmitter + mode, IP receives audio in AES format, extracts PCM and sends + PCM data. In receiver mode, IP receives PCM audio and + encapsulates PCM in AES format and sends AES data. diff --git a/sound/soc/xilinx/Makefile b/sound/soc/xilinx/Makefile new file mode 100644 index 000000000000..6c1209b9ee75 --- /dev/null +++ b/sound/soc/xilinx/Makefile @@ -0,0 +1,2 @@ +snd-soc-xlnx-i2s-objs := xlnx_i2s.o +obj-$(CONFIG_SND_SOC_XILINX_I2S) += snd-soc-xlnx-i2s.o diff --git a/sound/soc/xilinx/xlnx_i2s.c b/sound/soc/xilinx/xlnx_i2s.c new file mode 100644 index 000000000000..d4ae9eff41ce --- /dev/null +++ b/sound/soc/xilinx/xlnx_i2s.c @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx ASoC I2S audio support + * + * Copyright (C) 2018 Xilinx, Inc. + * + * Author: Praveen Vuppala <praveenv@xilinx.com> + * Author: Maruthi Srinivas Bayyavarapu <maruthis@xilinx.com> + */ + +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#define DRV_NAME "xlnx_i2s" + +#define I2S_CORE_CTRL_OFFSET 0x08 +#define I2S_I2STIM_OFFSET 0x20 +#define I2S_CH0_OFFSET 0x30 +#define I2S_I2STIM_VALID_MASK GENMASK(7, 0) + +static int xlnx_i2s_set_sclkout_div(struct snd_soc_dai *cpu_dai, + int div_id, int div) +{ + void __iomem *base = snd_soc_dai_get_drvdata(cpu_dai); + + if (!div || (div & ~I2S_I2STIM_VALID_MASK)) + return -EINVAL; + + writel(div, base + I2S_I2STIM_OFFSET); + + return 0; +} + +static int xlnx_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *i2s_dai) +{ + u32 reg_off, chan_id; + void __iomem *base = snd_soc_dai_get_drvdata(i2s_dai); + + chan_id = params_channels(params) / 2; + + while (chan_id > 0) { + reg_off = I2S_CH0_OFFSET + ((chan_id - 1) * 4); + writel(chan_id, base + reg_off); + chan_id--; + } + + return 0; +} + +static int xlnx_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *i2s_dai) +{ + void __iomem *base = snd_soc_dai_get_drvdata(i2s_dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + writel(1, base + I2S_CORE_CTRL_OFFSET); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + writel(0, base + I2S_CORE_CTRL_OFFSET); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dai_ops xlnx_i2s_dai_ops = { + .trigger = xlnx_i2s_trigger, + .set_clkdiv = xlnx_i2s_set_sclkout_div, + .hw_params = xlnx_i2s_hw_params +}; + +static const struct snd_soc_component_driver xlnx_i2s_component = { + .name = DRV_NAME, +}; + +static const struct of_device_id xlnx_i2s_of_match[] = { + { .compatible = "xlnx,i2s-transmitter-1.0", }, + { .compatible = "xlnx,i2s-receiver-1.0", }, + {}, +}; +MODULE_DEVICE_TABLE(of, xlnx_i2s_of_match); + +static int xlnx_i2s_probe(struct platform_device *pdev) +{ + struct resource *res; + void __iomem *base; + struct snd_soc_dai_driver *dai_drv; + int ret; + u32 ch, format, data_width; + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + + dai_drv = devm_kzalloc(&pdev->dev, sizeof(*dai_drv), GFP_KERNEL); + if (!dai_drv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + ret = of_property_read_u32(node, "xlnx,num-channels", &ch); + if (ret < 0) { + dev_err(dev, "cannot get supported channels\n"); + return ret; + } + ch = ch * 2; + + ret = of_property_read_u32(node, "xlnx,dwidth", &data_width); + if (ret < 0) { + dev_err(dev, "cannot get data width\n"); + return ret; + } + switch (data_width) { + case 16: + format = SNDRV_PCM_FMTBIT_S16_LE; + break; + case 24: + format = SNDRV_PCM_FMTBIT_S24_LE; + break; + default: + return -EINVAL; + } + + if (of_device_is_compatible(node, "xlnx,i2s-transmitter-1.0")) { + dai_drv->name = "xlnx_i2s_playback"; + dai_drv->playback.stream_name = "Playback"; + dai_drv->playback.formats = format; + dai_drv->playback.channels_min = ch; + dai_drv->playback.channels_max = ch; + dai_drv->playback.rates = SNDRV_PCM_RATE_8000_192000; + dai_drv->ops = &xlnx_i2s_dai_ops; + } else if (of_device_is_compatible(node, "xlnx,i2s-receiver-1.0")) { + dai_drv->name = "xlnx_i2s_capture"; + dai_drv->capture.stream_name = "Capture"; + dai_drv->capture.formats = format; + dai_drv->capture.channels_min = ch; + dai_drv->capture.channels_max = ch; + dai_drv->capture.rates = SNDRV_PCM_RATE_8000_192000; + dai_drv->ops = &xlnx_i2s_dai_ops; + } else { + return -ENODEV; + } + + dev_set_drvdata(&pdev->dev, base); + + ret = devm_snd_soc_register_component(&pdev->dev, &xlnx_i2s_component, + dai_drv, 1); + if (ret) { + dev_err(&pdev->dev, "i2s component registration failed\n"); + return ret; + } + + dev_info(&pdev->dev, "%s DAI registered\n", dai_drv->name); + + return ret; +} + +static struct platform_driver xlnx_i2s_aud_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = xlnx_i2s_of_match, + }, + .probe = xlnx_i2s_probe, +}; + +module_platform_driver(xlnx_i2s_aud_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Praveen Vuppala <praveenv@xilinx.com>"); +MODULE_AUTHOR("Maruthi Srinivas Bayyavarapu <maruthis@xilinx.com>"); diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c index 079063d8038d..883678ee971c 100644 --- a/sound/sparc/cs4231.c +++ b/sound/sparc/cs4231.c @@ -2071,12 +2071,12 @@ static int cs4231_ebus_probe(struct platform_device *op) static int cs4231_probe(struct platform_device *op) { #ifdef EBUS_SUPPORT - if (!strcmp(op->dev.of_node->parent->name, "ebus")) + if (of_node_name_eq(op->dev.of_node->parent, "ebus")) return cs4231_ebus_probe(op); #endif #ifdef SBUS_SUPPORT - if (!strcmp(op->dev.of_node->parent->name, "sbus") || - !strcmp(op->dev.of_node->parent->name, "sbi")) + if (of_node_name_eq(op->dev.of_node->parent, "sbus") || + of_node_name_eq(op->dev.of_node->parent, "sbi")) return cs4231_sbus_probe(op); #endif return -ENODEV; diff --git a/sound/synth/emux/emux_hwdep.c b/sound/synth/emux/emux_hwdep.c index e557946718a9..d9fcae071b47 100644 --- a/sound/synth/emux/emux_hwdep.c +++ b/sound/synth/emux/emux_hwdep.c @@ -22,9 +22,9 @@ #include <sound/core.h> #include <sound/hwdep.h> #include <linux/uaccess.h> +#include <linux/nospec.h> #include "emux_voice.h" - #define TMP_CLIENT_ID 0x1001 /* @@ -66,13 +66,16 @@ snd_emux_hwdep_misc_mode(struct snd_emux *emu, void __user *arg) return -EFAULT; if (info.mode < 0 || info.mode >= EMUX_MD_END) return -EINVAL; + info.mode = array_index_nospec(info.mode, EMUX_MD_END); if (info.port < 0) { for (i = 0; i < emu->num_ports; i++) emu->portptrs[i]->ctrls[info.mode] = info.value; } else { - if (info.port < emu->num_ports) + if (info.port < emu->num_ports) { + info.port = array_index_nospec(info.port, emu->num_ports); emu->portptrs[info.port]->ctrls[info.mode] = info.value; + } } return 0; } diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 6623cafc94f2..96340f23f86d 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -19,6 +19,7 @@ #include <linux/usb.h> #include <linux/usb/audio.h> #include <linux/usb/midi.h> +#include <linux/bits.h> #include <sound/control.h> #include <sound/core.h> @@ -668,15 +669,133 @@ static int snd_usb_cm106_boot_quirk(struct usb_device *dev) } /* - * C-Media CM6206 is based on CM106 with two additional - * registers that are not documented in the data sheet. - * Values here are chosen based on sniffing USB traffic - * under Windows. + * CM6206 registers from the CM6206 datasheet rev 2.1 */ +#define CM6206_REG0_DMA_MASTER BIT(15) +#define CM6206_REG0_SPDIFO_RATE_48K (2 << 12) +#define CM6206_REG0_SPDIFO_RATE_96K (7 << 12) +/* Bit 4 thru 11 is the S/PDIF category code */ +#define CM6206_REG0_SPDIFO_CAT_CODE_GENERAL (0 << 4) +#define CM6206_REG0_SPDIFO_EMPHASIS_CD BIT(3) +#define CM6206_REG0_SPDIFO_COPYRIGHT_NA BIT(2) +#define CM6206_REG0_SPDIFO_NON_AUDIO BIT(1) +#define CM6206_REG0_SPDIFO_PRO_FORMAT BIT(0) + +#define CM6206_REG1_TEST_SEL_CLK BIT(14) +#define CM6206_REG1_PLLBIN_EN BIT(13) +#define CM6206_REG1_SOFT_MUTE_EN BIT(12) +#define CM6206_REG1_GPIO4_OUT BIT(11) +#define CM6206_REG1_GPIO4_OE BIT(10) +#define CM6206_REG1_GPIO3_OUT BIT(9) +#define CM6206_REG1_GPIO3_OE BIT(8) +#define CM6206_REG1_GPIO2_OUT BIT(7) +#define CM6206_REG1_GPIO2_OE BIT(6) +#define CM6206_REG1_GPIO1_OUT BIT(5) +#define CM6206_REG1_GPIO1_OE BIT(4) +#define CM6206_REG1_SPDIFO_INVALID BIT(3) +#define CM6206_REG1_SPDIF_LOOP_EN BIT(2) +#define CM6206_REG1_SPDIFO_DIS BIT(1) +#define CM6206_REG1_SPDIFI_MIX BIT(0) + +#define CM6206_REG2_DRIVER_ON BIT(15) +#define CM6206_REG2_HEADP_SEL_SIDE_CHANNELS (0 << 13) +#define CM6206_REG2_HEADP_SEL_SURROUND_CHANNELS (1 << 13) +#define CM6206_REG2_HEADP_SEL_CENTER_SUBW (2 << 13) +#define CM6206_REG2_HEADP_SEL_FRONT_CHANNELS (3 << 13) +#define CM6206_REG2_MUTE_HEADPHONE_RIGHT BIT(12) +#define CM6206_REG2_MUTE_HEADPHONE_LEFT BIT(11) +#define CM6206_REG2_MUTE_REAR_SURROUND_RIGHT BIT(10) +#define CM6206_REG2_MUTE_REAR_SURROUND_LEFT BIT(9) +#define CM6206_REG2_MUTE_SIDE_SURROUND_RIGHT BIT(8) +#define CM6206_REG2_MUTE_SIDE_SURROUND_LEFT BIT(7) +#define CM6206_REG2_MUTE_SUBWOOFER BIT(6) +#define CM6206_REG2_MUTE_CENTER BIT(5) +#define CM6206_REG2_MUTE_RIGHT_FRONT BIT(3) +#define CM6206_REG2_MUTE_LEFT_FRONT BIT(3) +#define CM6206_REG2_EN_BTL BIT(2) +#define CM6206_REG2_MCUCLKSEL_1_5_MHZ (0) +#define CM6206_REG2_MCUCLKSEL_3_MHZ (1) +#define CM6206_REG2_MCUCLKSEL_6_MHZ (2) +#define CM6206_REG2_MCUCLKSEL_12_MHZ (3) + +/* Bit 11..13 sets the sensitivity to FLY tuner volume control VP/VD signal */ +#define CM6206_REG3_FLYSPEED_DEFAULT (2 << 11) +#define CM6206_REG3_VRAP25EN BIT(10) +#define CM6206_REG3_MSEL1 BIT(9) +#define CM6206_REG3_SPDIFI_RATE_44_1K BIT(0 << 7) +#define CM6206_REG3_SPDIFI_RATE_48K BIT(2 << 7) +#define CM6206_REG3_SPDIFI_RATE_32K BIT(3 << 7) +#define CM6206_REG3_PINSEL BIT(6) +#define CM6206_REG3_FOE BIT(5) +#define CM6206_REG3_ROE BIT(4) +#define CM6206_REG3_CBOE BIT(3) +#define CM6206_REG3_LOSE BIT(2) +#define CM6206_REG3_HPOE BIT(1) +#define CM6206_REG3_SPDIFI_CANREC BIT(0) + +#define CM6206_REG5_DA_RSTN BIT(13) +#define CM6206_REG5_AD_RSTN BIT(12) +#define CM6206_REG5_SPDIFO_AD2SPDO BIT(12) +#define CM6206_REG5_SPDIFO_SEL_FRONT (0 << 9) +#define CM6206_REG5_SPDIFO_SEL_SIDE_SUR (1 << 9) +#define CM6206_REG5_SPDIFO_SEL_CEN_LFE (2 << 9) +#define CM6206_REG5_SPDIFO_SEL_REAR_SUR (3 << 9) +#define CM6206_REG5_CODECM BIT(8) +#define CM6206_REG5_EN_HPF BIT(7) +#define CM6206_REG5_T_SEL_DSDA4 BIT(6) +#define CM6206_REG5_T_SEL_DSDA3 BIT(5) +#define CM6206_REG5_T_SEL_DSDA2 BIT(4) +#define CM6206_REG5_T_SEL_DSDA1 BIT(3) +#define CM6206_REG5_T_SEL_DSDAD_NORMAL 0 +#define CM6206_REG5_T_SEL_DSDAD_FRONT 4 +#define CM6206_REG5_T_SEL_DSDAD_S_SURROUND 5 +#define CM6206_REG5_T_SEL_DSDAD_CEN_LFE 6 +#define CM6206_REG5_T_SEL_DSDAD_R_SURROUND 7 + static int snd_usb_cm6206_boot_quirk(struct usb_device *dev) { int err = 0, reg; - int val[] = {0x2004, 0x3000, 0xf800, 0x143f, 0x0000, 0x3000}; + int val[] = { + /* + * Values here are chosen based on sniffing USB traffic + * under Windows. + * + * REG0: DAC is master, sample rate 48kHz, no copyright + */ + CM6206_REG0_SPDIFO_RATE_48K | + CM6206_REG0_SPDIFO_COPYRIGHT_NA, + /* + * REG1: PLL binary search enable, soft mute enable. + */ + CM6206_REG1_PLLBIN_EN | + CM6206_REG1_SOFT_MUTE_EN | + /* + * REG2: enable output drivers, + * select front channels to the headphone output, + * then mute the headphone channels, run the MCU + * at 1.5 MHz. + */ + CM6206_REG2_DRIVER_ON | + CM6206_REG2_HEADP_SEL_FRONT_CHANNELS | + CM6206_REG2_MUTE_HEADPHONE_RIGHT | + CM6206_REG2_MUTE_HEADPHONE_LEFT, + /* + * REG3: default flyspeed, set 2.5V mic bias + * enable all line out ports and enable SPDIF + */ + CM6206_REG3_FLYSPEED_DEFAULT | + CM6206_REG3_VRAP25EN | + CM6206_REG3_FOE | + CM6206_REG3_ROE | + CM6206_REG3_CBOE | + CM6206_REG3_LOSE | + CM6206_REG3_HPOE | + CM6206_REG3_SPDIFI_CANREC, + /* REG4 is just a bunch of GPIO lines */ + 0x0000, + /* REG5: de-assert AD/DA reset signals */ + CM6206_REG5_DA_RSTN | + CM6206_REG5_AD_RSTN }; for (reg = 0; reg < ARRAY_SIZE(val); reg++) { err = snd_usb_cm106_write_int_reg(dev, reg, val[reg]); diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c index 83d76c345940..00c92eb854ce 100644 --- a/sound/x86/intel_hdmi_audio.c +++ b/sound/x86/intel_hdmi_audio.c @@ -1648,7 +1648,7 @@ static int had_create_jack(struct snd_intelhad *ctx, * PM callbacks */ -static int hdmi_lpe_audio_runtime_suspend(struct device *dev) +static int __maybe_unused hdmi_lpe_audio_suspend(struct device *dev) { struct snd_intelhad_card *card_ctx = dev_get_drvdata(dev); int port; @@ -1664,23 +1664,8 @@ static int hdmi_lpe_audio_runtime_suspend(struct device *dev) } } - return 0; -} - -static int __maybe_unused hdmi_lpe_audio_suspend(struct device *dev) -{ - struct snd_intelhad_card *card_ctx = dev_get_drvdata(dev); - int err; + snd_power_change_state(card_ctx->card, SNDRV_CTL_POWER_D3hot); - err = hdmi_lpe_audio_runtime_suspend(dev); - if (!err) - snd_power_change_state(card_ctx->card, SNDRV_CTL_POWER_D3hot); - return err; -} - -static int hdmi_lpe_audio_runtime_resume(struct device *dev) -{ - pm_runtime_mark_last_busy(dev); return 0; } @@ -1688,8 +1673,10 @@ static int __maybe_unused hdmi_lpe_audio_resume(struct device *dev) { struct snd_intelhad_card *card_ctx = dev_get_drvdata(dev); - hdmi_lpe_audio_runtime_resume(dev); + pm_runtime_mark_last_busy(dev); + snd_power_change_state(card_ctx->card, SNDRV_CTL_POWER_D0); + return 0; } @@ -1877,7 +1864,6 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev) pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_mark_last_busy(&pdev->dev); - pm_runtime_set_active(&pdev->dev); dev_dbg(&pdev->dev, "%s: handle pending notification\n", __func__); for_each_port(card_ctx, port) { @@ -1908,8 +1894,6 @@ static int hdmi_lpe_audio_remove(struct platform_device *pdev) static const struct dev_pm_ops hdmi_lpe_audio_pm = { SET_SYSTEM_SLEEP_PM_OPS(hdmi_lpe_audio_suspend, hdmi_lpe_audio_resume) - SET_RUNTIME_PM_OPS(hdmi_lpe_audio_runtime_suspend, - hdmi_lpe_audio_runtime_resume, NULL) }; static struct platform_driver hdmi_lpe_audio_driver = { diff --git a/sound/xen/Kconfig b/sound/xen/Kconfig index 4f1fceea82d2..e4d7beb4df1c 100644 --- a/sound/xen/Kconfig +++ b/sound/xen/Kconfig @@ -5,6 +5,7 @@ config SND_XEN_FRONTEND depends on XEN select SND_PCM select XEN_XENBUS_FRONTEND + select XEN_FRONT_PGDIR_SHBUF help Choose this option if you want to enable a para-virtualized frontend sound driver for Xen guest OSes. diff --git a/sound/xen/Makefile b/sound/xen/Makefile index 1e6470ecc2f2..24031775b715 100644 --- a/sound/xen/Makefile +++ b/sound/xen/Makefile @@ -3,7 +3,6 @@ snd_xen_front-objs := xen_snd_front.o \ xen_snd_front_cfg.o \ xen_snd_front_evtchnl.o \ - xen_snd_front_shbuf.o \ xen_snd_front_alsa.o obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o diff --git a/sound/xen/xen_snd_front.c b/sound/xen/xen_snd_front.c index b089b13b5160..a9e5c2cd7698 100644 --- a/sound/xen/xen_snd_front.c +++ b/sound/xen/xen_snd_front.c @@ -16,12 +16,12 @@ #include <xen/xen.h> #include <xen/xenbus.h> +#include <xen/xen-front-pgdir-shbuf.h> #include <xen/interface/io/sndif.h> #include "xen_snd_front.h" #include "xen_snd_front_alsa.h" #include "xen_snd_front_evtchnl.h" -#include "xen_snd_front_shbuf.h" static struct xensnd_req * be_stream_prepare_req(struct xen_snd_front_evtchnl *evtchnl, u8 operation) @@ -82,7 +82,7 @@ int xen_snd_front_stream_query_hw_param(struct xen_snd_front_evtchnl *evtchnl, } int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl *evtchnl, - struct xen_snd_front_shbuf *sh_buf, + struct xen_front_pgdir_shbuf *shbuf, u8 format, unsigned int channels, unsigned int rate, u32 buffer_sz, u32 period_sz) @@ -99,7 +99,8 @@ int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl *evtchnl, req->op.open.pcm_rate = rate; req->op.open.buffer_sz = buffer_sz; req->op.open.period_sz = period_sz; - req->op.open.gref_directory = xen_snd_front_shbuf_get_dir_start(sh_buf); + req->op.open.gref_directory = + xen_front_pgdir_shbuf_get_dir_start(shbuf); mutex_unlock(&evtchnl->ring_io_lock); ret = be_stream_do_io(evtchnl); diff --git a/sound/xen/xen_snd_front.h b/sound/xen/xen_snd_front.h index a2ea2463bcc5..05611f113b94 100644 --- a/sound/xen/xen_snd_front.h +++ b/sound/xen/xen_snd_front.h @@ -16,7 +16,7 @@ struct xen_snd_front_card_info; struct xen_snd_front_evtchnl; struct xen_snd_front_evtchnl_pair; -struct xen_snd_front_shbuf; +struct xen_front_pgdir_shbuf; struct xensnd_query_hw_param; struct xen_snd_front_info { @@ -35,7 +35,7 @@ int xen_snd_front_stream_query_hw_param(struct xen_snd_front_evtchnl *evtchnl, struct xensnd_query_hw_param *hw_param_resp); int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl *evtchnl, - struct xen_snd_front_shbuf *sh_buf, + struct xen_front_pgdir_shbuf *shbuf, u8 format, unsigned int channels, unsigned int rate, u32 buffer_sz, u32 period_sz); diff --git a/sound/xen/xen_snd_front_alsa.c b/sound/xen/xen_snd_front_alsa.c index 2cbd9679aca1..a7f413cb704d 100644 --- a/sound/xen/xen_snd_front_alsa.c +++ b/sound/xen/xen_snd_front_alsa.c @@ -15,17 +15,24 @@ #include <sound/pcm_params.h> #include <xen/xenbus.h> +#include <xen/xen-front-pgdir-shbuf.h> #include "xen_snd_front.h" #include "xen_snd_front_alsa.h" #include "xen_snd_front_cfg.h" #include "xen_snd_front_evtchnl.h" -#include "xen_snd_front_shbuf.h" struct xen_snd_front_pcm_stream_info { struct xen_snd_front_info *front_info; struct xen_snd_front_evtchnl_pair *evt_pair; - struct xen_snd_front_shbuf sh_buf; + + /* This is the shared buffer with its backing storage. */ + struct xen_front_pgdir_shbuf shbuf; + u8 *buffer; + size_t buffer_sz; + int num_pages; + struct page **pages; + int index; bool is_open; @@ -214,12 +221,20 @@ static void stream_clear(struct xen_snd_front_pcm_stream_info *stream) stream->out_frames = 0; atomic_set(&stream->hw_ptr, 0); xen_snd_front_evtchnl_pair_clear(stream->evt_pair); - xen_snd_front_shbuf_clear(&stream->sh_buf); + memset(&stream->shbuf, 0, sizeof(stream->shbuf)); + stream->buffer = NULL; + stream->buffer_sz = 0; + stream->pages = NULL; + stream->num_pages = 0; } static void stream_free(struct xen_snd_front_pcm_stream_info *stream) { - xen_snd_front_shbuf_free(&stream->sh_buf); + xen_front_pgdir_shbuf_unmap(&stream->shbuf); + xen_front_pgdir_shbuf_free(&stream->shbuf); + if (stream->buffer) + free_pages_exact(stream->buffer, stream->buffer_sz); + kfree(stream->pages); stream_clear(stream); } @@ -421,10 +436,34 @@ static int alsa_close(struct snd_pcm_substream *substream) return 0; } +static int shbuf_setup_backstore(struct xen_snd_front_pcm_stream_info *stream, + size_t buffer_sz) +{ + int i; + + stream->buffer = alloc_pages_exact(stream->buffer_sz, GFP_KERNEL); + if (!stream->buffer) + return -ENOMEM; + + stream->buffer_sz = buffer_sz; + stream->num_pages = DIV_ROUND_UP(stream->buffer_sz, PAGE_SIZE); + stream->pages = kcalloc(stream->num_pages, sizeof(struct page *), + GFP_KERNEL); + if (!stream->pages) + return -ENOMEM; + + for (i = 0; i < stream->num_pages; i++) + stream->pages[i] = virt_to_page(stream->buffer + i * PAGE_SIZE); + + return 0; +} + static int alsa_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); + struct xen_snd_front_info *front_info = stream->front_info; + struct xen_front_pgdir_shbuf_cfg buf_cfg; int ret; /* @@ -432,19 +471,32 @@ static int alsa_hw_params(struct snd_pcm_substream *substream, * so free the previously allocated shared buffer if any. */ stream_free(stream); + ret = shbuf_setup_backstore(stream, params_buffer_bytes(params)); + if (ret < 0) + goto fail; - ret = xen_snd_front_shbuf_alloc(stream->front_info->xb_dev, - &stream->sh_buf, - params_buffer_bytes(params)); - if (ret < 0) { - stream_free(stream); - dev_err(&stream->front_info->xb_dev->dev, - "Failed to allocate buffers for stream with index %d\n", - stream->index); - return ret; - } + memset(&buf_cfg, 0, sizeof(buf_cfg)); + buf_cfg.xb_dev = front_info->xb_dev; + buf_cfg.pgdir = &stream->shbuf; + buf_cfg.num_pages = stream->num_pages; + buf_cfg.pages = stream->pages; + + ret = xen_front_pgdir_shbuf_alloc(&buf_cfg); + if (ret < 0) + goto fail; + + ret = xen_front_pgdir_shbuf_map(&stream->shbuf); + if (ret < 0) + goto fail; return 0; + +fail: + stream_free(stream); + dev_err(&front_info->xb_dev->dev, + "Failed to allocate buffers for stream with index %d\n", + stream->index); + return ret; } static int alsa_hw_free(struct snd_pcm_substream *substream) @@ -476,7 +528,7 @@ static int alsa_prepare(struct snd_pcm_substream *substream) sndif_format = ret; ret = xen_snd_front_stream_prepare(&stream->evt_pair->req, - &stream->sh_buf, + &stream->shbuf, sndif_format, runtime->channels, runtime->rate, @@ -556,10 +608,10 @@ static int alsa_pb_copy_user(struct snd_pcm_substream *substream, { struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); - if (unlikely(pos + count > stream->sh_buf.buffer_sz)) + if (unlikely(pos + count > stream->buffer_sz)) return -EINVAL; - if (copy_from_user(stream->sh_buf.buffer + pos, src, count)) + if (copy_from_user(stream->buffer + pos, src, count)) return -EFAULT; return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count); @@ -571,10 +623,10 @@ static int alsa_pb_copy_kernel(struct snd_pcm_substream *substream, { struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); - if (unlikely(pos + count > stream->sh_buf.buffer_sz)) + if (unlikely(pos + count > stream->buffer_sz)) return -EINVAL; - memcpy(stream->sh_buf.buffer + pos, src, count); + memcpy(stream->buffer + pos, src, count); return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count); } @@ -586,14 +638,14 @@ static int alsa_cap_copy_user(struct snd_pcm_substream *substream, struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); int ret; - if (unlikely(pos + count > stream->sh_buf.buffer_sz)) + if (unlikely(pos + count > stream->buffer_sz)) return -EINVAL; ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, count); if (ret < 0) return ret; - return copy_to_user(dst, stream->sh_buf.buffer + pos, count) ? + return copy_to_user(dst, stream->buffer + pos, count) ? -EFAULT : 0; } @@ -604,14 +656,14 @@ static int alsa_cap_copy_kernel(struct snd_pcm_substream *substream, struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); int ret; - if (unlikely(pos + count > stream->sh_buf.buffer_sz)) + if (unlikely(pos + count > stream->buffer_sz)) return -EINVAL; ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, count); if (ret < 0) return ret; - memcpy(dst, stream->sh_buf.buffer + pos, count); + memcpy(dst, stream->buffer + pos, count); return 0; } @@ -622,10 +674,10 @@ static int alsa_pb_fill_silence(struct snd_pcm_substream *substream, { struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); - if (unlikely(pos + count > stream->sh_buf.buffer_sz)) + if (unlikely(pos + count > stream->buffer_sz)) return -EINVAL; - memset(stream->sh_buf.buffer + pos, 0, count); + memset(stream->buffer + pos, 0, count); return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count); } diff --git a/sound/xen/xen_snd_front_shbuf.c b/sound/xen/xen_snd_front_shbuf.c deleted file mode 100644 index 07ac176a41ba..000000000000 --- a/sound/xen/xen_snd_front_shbuf.c +++ /dev/null @@ -1,194 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 OR MIT - -/* - * Xen para-virtual sound device - * - * Copyright (C) 2016-2018 EPAM Systems Inc. - * - * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> - */ - -#include <linux/kernel.h> -#include <xen/xen.h> -#include <xen/xenbus.h> - -#include "xen_snd_front_shbuf.h" - -grant_ref_t xen_snd_front_shbuf_get_dir_start(struct xen_snd_front_shbuf *buf) -{ - if (!buf->grefs) - return GRANT_INVALID_REF; - - return buf->grefs[0]; -} - -void xen_snd_front_shbuf_clear(struct xen_snd_front_shbuf *buf) -{ - memset(buf, 0, sizeof(*buf)); -} - -void xen_snd_front_shbuf_free(struct xen_snd_front_shbuf *buf) -{ - int i; - - if (buf->grefs) { - for (i = 0; i < buf->num_grefs; i++) - if (buf->grefs[i] != GRANT_INVALID_REF) - gnttab_end_foreign_access(buf->grefs[i], - 0, 0UL); - kfree(buf->grefs); - } - kfree(buf->directory); - free_pages_exact(buf->buffer, buf->buffer_sz); - xen_snd_front_shbuf_clear(buf); -} - -/* - * number of grant references a page can hold with respect to the - * xensnd_page_directory header - */ -#define XENSND_NUM_GREFS_PER_PAGE ((XEN_PAGE_SIZE - \ - offsetof(struct xensnd_page_directory, gref)) / \ - sizeof(grant_ref_t)) - -static void fill_page_dir(struct xen_snd_front_shbuf *buf, - int num_pages_dir) -{ - struct xensnd_page_directory *page_dir; - unsigned char *ptr; - int i, cur_gref, grefs_left, to_copy; - - ptr = buf->directory; - grefs_left = buf->num_grefs - num_pages_dir; - /* - * skip grant references at the beginning, they are for pages granted - * for the page directory itself - */ - cur_gref = num_pages_dir; - for (i = 0; i < num_pages_dir; i++) { - page_dir = (struct xensnd_page_directory *)ptr; - if (grefs_left <= XENSND_NUM_GREFS_PER_PAGE) { - to_copy = grefs_left; - page_dir->gref_dir_next_page = GRANT_INVALID_REF; - } else { - to_copy = XENSND_NUM_GREFS_PER_PAGE; - page_dir->gref_dir_next_page = buf->grefs[i + 1]; - } - - memcpy(&page_dir->gref, &buf->grefs[cur_gref], - to_copy * sizeof(grant_ref_t)); - - ptr += XEN_PAGE_SIZE; - grefs_left -= to_copy; - cur_gref += to_copy; - } -} - -static int grant_references(struct xenbus_device *xb_dev, - struct xen_snd_front_shbuf *buf, - int num_pages_dir, int num_pages_buffer, - int num_grefs) -{ - grant_ref_t priv_gref_head; - unsigned long frame; - int ret, i, j, cur_ref; - int otherend_id; - - ret = gnttab_alloc_grant_references(num_grefs, &priv_gref_head); - if (ret) - return ret; - - buf->num_grefs = num_grefs; - otherend_id = xb_dev->otherend_id; - j = 0; - - for (i = 0; i < num_pages_dir; i++) { - cur_ref = gnttab_claim_grant_reference(&priv_gref_head); - if (cur_ref < 0) { - ret = cur_ref; - goto fail; - } - - frame = xen_page_to_gfn(virt_to_page(buf->directory + - XEN_PAGE_SIZE * i)); - gnttab_grant_foreign_access_ref(cur_ref, otherend_id, frame, 0); - buf->grefs[j++] = cur_ref; - } - - for (i = 0; i < num_pages_buffer; i++) { - cur_ref = gnttab_claim_grant_reference(&priv_gref_head); - if (cur_ref < 0) { - ret = cur_ref; - goto fail; - } - - frame = xen_page_to_gfn(virt_to_page(buf->buffer + - XEN_PAGE_SIZE * i)); - gnttab_grant_foreign_access_ref(cur_ref, otherend_id, frame, 0); - buf->grefs[j++] = cur_ref; - } - - gnttab_free_grant_references(priv_gref_head); - fill_page_dir(buf, num_pages_dir); - return 0; - -fail: - gnttab_free_grant_references(priv_gref_head); - return ret; -} - -static int alloc_int_buffers(struct xen_snd_front_shbuf *buf, - int num_pages_dir, int num_pages_buffer, - int num_grefs) -{ - buf->grefs = kcalloc(num_grefs, sizeof(*buf->grefs), GFP_KERNEL); - if (!buf->grefs) - return -ENOMEM; - - buf->directory = kcalloc(num_pages_dir, XEN_PAGE_SIZE, GFP_KERNEL); - if (!buf->directory) - goto fail; - - buf->buffer_sz = num_pages_buffer * XEN_PAGE_SIZE; - buf->buffer = alloc_pages_exact(buf->buffer_sz, GFP_KERNEL); - if (!buf->buffer) - goto fail; - - return 0; - -fail: - kfree(buf->grefs); - buf->grefs = NULL; - kfree(buf->directory); - buf->directory = NULL; - return -ENOMEM; -} - -int xen_snd_front_shbuf_alloc(struct xenbus_device *xb_dev, - struct xen_snd_front_shbuf *buf, - unsigned int buffer_sz) -{ - int num_pages_buffer, num_pages_dir, num_grefs; - int ret; - - xen_snd_front_shbuf_clear(buf); - - num_pages_buffer = DIV_ROUND_UP(buffer_sz, XEN_PAGE_SIZE); - /* number of pages the page directory consumes itself */ - num_pages_dir = DIV_ROUND_UP(num_pages_buffer, - XENSND_NUM_GREFS_PER_PAGE); - num_grefs = num_pages_buffer + num_pages_dir; - - ret = alloc_int_buffers(buf, num_pages_dir, - num_pages_buffer, num_grefs); - if (ret < 0) - return ret; - - ret = grant_references(xb_dev, buf, num_pages_dir, num_pages_buffer, - num_grefs); - if (ret < 0) - return ret; - - fill_page_dir(buf, num_pages_dir); - return 0; -} diff --git a/sound/xen/xen_snd_front_shbuf.h b/sound/xen/xen_snd_front_shbuf.h deleted file mode 100644 index d28e97c47b2c..000000000000 --- a/sound/xen/xen_snd_front_shbuf.h +++ /dev/null @@ -1,36 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 OR MIT */ - -/* - * Xen para-virtual sound device - * - * Copyright (C) 2016-2018 EPAM Systems Inc. - * - * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> - */ - -#ifndef __XEN_SND_FRONT_SHBUF_H -#define __XEN_SND_FRONT_SHBUF_H - -#include <xen/grant_table.h> - -#include "xen_snd_front_evtchnl.h" - -struct xen_snd_front_shbuf { - int num_grefs; - grant_ref_t *grefs; - u8 *directory; - u8 *buffer; - size_t buffer_sz; -}; - -grant_ref_t xen_snd_front_shbuf_get_dir_start(struct xen_snd_front_shbuf *buf); - -int xen_snd_front_shbuf_alloc(struct xenbus_device *xb_dev, - struct xen_snd_front_shbuf *buf, - unsigned int buffer_sz); - -void xen_snd_front_shbuf_clear(struct xen_snd_front_shbuf *buf); - -void xen_snd_front_shbuf_free(struct xen_snd_front_shbuf *buf); - -#endif /* __XEN_SND_FRONT_SHBUF_H */ |