1
0

Merge tag 'sound-5.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound

Pull sound updates from Takashi Iwai:
 "The most significant changes at this cycle are the Sound Open Firmware
  support from Intel for the common DSP framework along with its support
  for Intel platforms. It's a door opened to a real "free" firmware (in
  the sense of FOSS), and other parties show interests in it.

  In addition to SOF, we've got a bunch of updates and fixes as usual.
  Some highlights are below.

  ALSA core:
   - Cleanups and fixes in ALSA timer code to cover some races spotted
     by syzkaller
   - Cleanups and fixes in ALSA sequencer code to cover some races,
     again unsurprisingly, spotted by syzkaller
   - Optimize the common page allocation helper with alloc_pages_exact()

  ASoC:
   - Add SOF core support, as well as Intel SOF platform support
   - Generic card driver improvements: support for MCLK/sample rate
     ratio and pin switches
   - A big set of improvements to TLV320AIC32x4 drivers
   - New drivers for Freescale audio mixers, several Intel machines,
     several Mediatek machines, Meson G12A, Spreadtrum compressed audio
     and DMA devices

  HD-audio:
   - A few Realtek codec fixes for reducing pop noises
   - Quirks for Chromebooks
   - Workaround for faulty connection report on AMD/Nvidia HDMI

  Others:
   - A quirk for Focusrite Scarlett Solo USB-audio
   - Add support for MOTU 8pre FireWire
   - 24bit sample format support in aloop
   - GUS patch format support (finally, over a decade) in native emux
     synth code"

* tag 'sound-5.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (375 commits)
  ASoC: SOF: Fix unused variable warnings
  ALSA: line6: toneport: Fix broken usage of timer for delayed execution
  ALSA: aica: Fix a long-time build breakage
  ALSA: hda/realtek - Support low power consumption for ALC256
  ASoC: stm32: i2s: update pcm hardware constraints
  ASoC: codec: hdac_hdmi: no checking monitor in hw_params
  ASoC: mediatek: mt6358: save PGA for mixer control
  ASoC: mediatek: mt6358: save output volume for mixer controls
  ASoC: mediatek: mt6358: initialize setting when ramping volume
  ASoC: SOF: core: fix undefined nocodec reference
  ASoC: SOF: xtensa: fix undefined references
  ASoC: SOF: Propagate sof_get_ctrl_copy_params() error properly
  ALSA: hdea/realtek - Headset fixup for System76 Gazelle (gaze14)
  ALSA: hda/intel: add CometLake PCI IDs
  ALSA: hda/realtek - Support low power consumption for ALC295
  ASoC: rockchip: Fix an uninitialized variable compile warning
  ASoC: SOF: Fix a compile warning with CONFIG_PCI=n
  ASoC: da7219: Fix a compile warning at CONFIG_COMMON_CLK=n
  ASoC: sound/soc/sof/: fix kconfig dependency warning
  ASoC: stm32: spdifrx: change trace level on iec control
  ...
Este cometimento está contido em:
Linus Torvalds
2019-05-09 08:26:55 -07:00
ascendente a2d635decb ed97c988bd
cometimento e57ccca1ba
321 ficheiros modificados com 30105 adições e 3213 eliminações

Ver ficheiro

@@ -49,8 +49,7 @@ static const struct file_operations snd_shutdown_f_ops;
/* locked for registering/using */
static DECLARE_BITMAP(snd_cards_lock, SNDRV_CARDS);
struct snd_card *snd_cards[SNDRV_CARDS];
EXPORT_SYMBOL(snd_cards);
static struct snd_card *snd_cards[SNDRV_CARDS];
static DEFINE_MUTEX(snd_card_mutex);
@@ -268,6 +267,26 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
}
EXPORT_SYMBOL(snd_card_new);
/**
* snd_card_ref - Get the card object from the index
* @idx: the card index
*
* Returns a card object corresponding to the given index or NULL if not found.
* Release the object via snd_card_unref().
*/
struct snd_card *snd_card_ref(int idx)
{
struct snd_card *card;
mutex_lock(&snd_card_mutex);
card = snd_cards[idx];
if (card)
get_device(&card->card_dev);
mutex_unlock(&snd_card_mutex);
return card;
}
EXPORT_SYMBOL_GPL(snd_card_ref);
/* return non-zero if a card is already locked */
int snd_card_locked(int card)
{

Ver ficheiro

@@ -30,53 +30,6 @@
#endif
#include <sound/memalloc.h>
/*
*
* Generic memory allocators
*
*/
/**
* snd_malloc_pages - allocate pages with the given size
* @size: the size to allocate in bytes
* @gfp_flags: the allocation conditions, GFP_XXX
*
* Allocates the physically contiguous pages with the given size.
*
* Return: The pointer of the buffer, or %NULL if no enough memory.
*/
void *snd_malloc_pages(size_t size, gfp_t gfp_flags)
{
int pg;
if (WARN_ON(!size))
return NULL;
if (WARN_ON(!gfp_flags))
return NULL;
gfp_flags |= __GFP_COMP; /* compound page lets parts be mapped */
pg = get_order(size);
return (void *) __get_free_pages(gfp_flags, pg);
}
EXPORT_SYMBOL(snd_malloc_pages);
/**
* snd_free_pages - release the pages
* @ptr: the buffer pointer to release
* @size: the allocated buffer size
*
* Releases the buffer allocated via snd_malloc_pages().
*/
void snd_free_pages(void *ptr, size_t size)
{
int pg;
if (ptr == NULL)
return;
pg = get_order(size);
free_pages((unsigned long) ptr, pg);
}
EXPORT_SYMBOL(snd_free_pages);
/*
*
* Bus-specific memory allocators
@@ -190,8 +143,8 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size,
dmab->bytes = 0;
switch (type) {
case SNDRV_DMA_TYPE_CONTINUOUS:
dmab->area = snd_malloc_pages(size,
(__force gfp_t)(unsigned long)device);
dmab->area = alloc_pages_exact(size,
(__force gfp_t)(unsigned long)device);
dmab->addr = 0;
break;
#ifdef CONFIG_HAS_DMA
@@ -275,7 +228,7 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab)
{
switch (dmab->dev.type) {
case SNDRV_DMA_TYPE_CONTINUOUS:
snd_free_pages(dmab->area, dmab->bytes);
free_pages_exact(dmab->area, dmab->bytes);
break;
#ifdef CONFIG_HAS_DMA
#ifdef CONFIG_GENERIC_ALLOCATOR

Ver ficheiro

@@ -1403,24 +1403,32 @@ static int snd_mixer_oss_notify_handler(struct snd_card *card, int cmd)
static int __init alsa_mixer_oss_init(void)
{
struct snd_card *card;
int idx;
snd_mixer_oss_notify_callback = snd_mixer_oss_notify_handler;
for (idx = 0; idx < SNDRV_CARDS; idx++) {
if (snd_cards[idx])
snd_mixer_oss_notify_handler(snd_cards[idx], SND_MIXER_OSS_NOTIFY_REGISTER);
card = snd_card_ref(idx);
if (card) {
snd_mixer_oss_notify_handler(card, SND_MIXER_OSS_NOTIFY_REGISTER);
snd_card_unref(card);
}
}
return 0;
}
static void __exit alsa_mixer_oss_exit(void)
{
struct snd_card *card;
int idx;
snd_mixer_oss_notify_callback = NULL;
for (idx = 0; idx < SNDRV_CARDS; idx++) {
if (snd_cards[idx])
snd_mixer_oss_notify_handler(snd_cards[idx], SND_MIXER_OSS_NOTIFY_FREE);
card = snd_card_ref(idx);
if (card) {
snd_mixer_oss_notify_handler(card, SND_MIXER_OSS_NOTIFY_FREE);
snd_card_unref(card);
}
}
}

Ver ficheiro

@@ -959,22 +959,22 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
return -ENOMEM;
size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status));
runtime->status = snd_malloc_pages(size, GFP_KERNEL);
runtime->status = alloc_pages_exact(size, GFP_KERNEL);
if (runtime->status == NULL) {
kfree(runtime);
return -ENOMEM;
}
memset((void*)runtime->status, 0, size);
memset(runtime->status, 0, size);
size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control));
runtime->control = snd_malloc_pages(size, GFP_KERNEL);
runtime->control = alloc_pages_exact(size, GFP_KERNEL);
if (runtime->control == NULL) {
snd_free_pages((void*)runtime->status,
free_pages_exact(runtime->status,
PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)));
kfree(runtime);
return -ENOMEM;
}
memset((void*)runtime->control, 0, size);
memset(runtime->control, 0, size);
init_waitqueue_head(&runtime->sleep);
init_waitqueue_head(&runtime->tsleep);
@@ -1000,9 +1000,9 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
runtime = substream->runtime;
if (runtime->private_free != NULL)
runtime->private_free(runtime);
snd_free_pages((void*)runtime->status,
free_pages_exact(runtime->status,
PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)));
snd_free_pages((void*)runtime->control,
free_pages_exact(runtime->control,
PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)));
kfree(runtime->hw_constraints.rules);
/* Avoid concurrent access to runtime via PCM timer interface */

Ver ficheiro

@@ -30,6 +30,7 @@
#include <sound/rawmidi.h>
#include <sound/seq_kernel.h>
#include <sound/info.h>
#include "../seq_clientmgr.h"
/* max. applications */
#define SNDRV_SEQ_OSS_MAX_CLIENTS 16
@@ -150,11 +151,16 @@ snd_seq_oss_dispatch(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, int a
return snd_seq_kernel_client_dispatch(dp->cseq, ev, atomic, hop);
}
/* ioctl */
/* ioctl for writeq */
static inline int
snd_seq_oss_control(struct seq_oss_devinfo *dp, unsigned int type, void *arg)
{
return snd_seq_kernel_client_ctl(dp->cseq, type, arg);
int err;
snd_seq_client_ioctl_lock(dp->cseq);
err = snd_seq_kernel_client_ctl(dp->cseq, type, arg);
snd_seq_client_ioctl_unlock(dp->cseq);
return err;
}
/* fill the addresses in header */

Ver ficheiro

@@ -180,14 +180,11 @@ insert_queue(struct seq_oss_devinfo *dp, union evrec *rec, struct file *opt)
return 0; /* invalid event - no need to insert queue */
event.time.tick = snd_seq_oss_timer_cur_tick(dp->timer);
if (dp->timer->realtime || !dp->timer->running) {
if (dp->timer->realtime || !dp->timer->running)
snd_seq_oss_dispatch(dp, &event, 0, 0);
} else {
if (is_nonblock_mode(dp->file_mode))
rc = snd_seq_kernel_client_enqueue(dp->cseq, &event, 0, 0);
else
rc = snd_seq_kernel_client_enqueue_blocking(dp->cseq, &event, opt, 0, 0);
}
else
rc = snd_seq_kernel_client_enqueue(dp->cseq, &event, opt,
!is_nonblock_mode(dp->file_mode));
return rc;
}

Ver ficheiro

@@ -116,7 +116,7 @@ snd_seq_oss_writeq_sync(struct seq_oss_writeq *q)
rec->t.code = SEQ_SYNCTIMER;
rec->t.time = time;
q->sync_event_put = 1;
snd_seq_kernel_client_enqueue_blocking(dp->cseq, &ev, NULL, 0, 0);
snd_seq_kernel_client_enqueue(dp->cseq, &ev, NULL, true);
}
wait_event_interruptible_timeout(q->sync_sleep, ! q->sync_event_put, HZ);

Ver ficheiro

@@ -179,6 +179,41 @@ struct snd_seq_client *snd_seq_client_use_ptr(int clientid)
return client;
}
/* Take refcount and perform ioctl_mutex lock on the given client;
* used only for OSS sequencer
* Unlock via snd_seq_client_ioctl_unlock() below
*/
bool snd_seq_client_ioctl_lock(int clientid)
{
struct snd_seq_client *client;
client = snd_seq_client_use_ptr(clientid);
if (!client)
return false;
mutex_lock(&client->ioctl_mutex);
/* The client isn't unrefed here; see snd_seq_client_ioctl_unlock() */
return true;
}
EXPORT_SYMBOL_GPL(snd_seq_client_ioctl_lock);
/* Unlock and unref the given client; for OSS sequencer use only */
void snd_seq_client_ioctl_unlock(int clientid)
{
struct snd_seq_client *client;
client = snd_seq_client_use_ptr(clientid);
if (WARN_ON(!client))
return;
mutex_unlock(&client->ioctl_mutex);
/* The doubly unrefs below are intentional; the first one releases the
* leftover from snd_seq_client_ioctl_lock() above, and the second one
* is for releasing snd_seq_client_use_ptr() in this function
*/
snd_seq_client_unlock(client);
snd_seq_client_unlock(client);
}
EXPORT_SYMBOL_GPL(snd_seq_client_ioctl_unlock);
static void usage_alloc(struct snd_seq_usage *res, int num)
{
res->cur += num;
@@ -203,7 +238,6 @@ int __init client_init_data(void)
static struct snd_seq_client *seq_create_client1(int client_index, int poolsize)
{
unsigned long flags;
int c;
struct snd_seq_client *client;
@@ -224,7 +258,7 @@ static struct snd_seq_client *seq_create_client1(int client_index, int poolsize)
mutex_init(&client->ioctl_mutex);
/* find free slot in the client table */
spin_lock_irqsave(&clients_lock, flags);
spin_lock_irq(&clients_lock);
if (client_index < 0) {
for (c = SNDRV_SEQ_DYNAMIC_CLIENTS_BEGIN;
c < SNDRV_SEQ_MAX_CLIENTS;
@@ -232,17 +266,17 @@ static struct snd_seq_client *seq_create_client1(int client_index, int poolsize)
if (clienttab[c] || clienttablock[c])
continue;
clienttab[client->number = c] = client;
spin_unlock_irqrestore(&clients_lock, flags);
spin_unlock_irq(&clients_lock);
return client;
}
} else {
if (clienttab[client_index] == NULL && !clienttablock[client_index]) {
clienttab[client->number = client_index] = client;
spin_unlock_irqrestore(&clients_lock, flags);
spin_unlock_irq(&clients_lock);
return client;
}
}
spin_unlock_irqrestore(&clients_lock, flags);
spin_unlock_irq(&clients_lock);
snd_seq_pool_delete(&client->pool);
kfree(client);
return NULL; /* no free slot found or busy, return failure code */
@@ -251,23 +285,21 @@ static struct snd_seq_client *seq_create_client1(int client_index, int poolsize)
static int seq_free_client1(struct snd_seq_client *client)
{
unsigned long flags;
if (!client)
return 0;
spin_lock_irqsave(&clients_lock, flags);
spin_lock_irq(&clients_lock);
clienttablock[client->number] = 1;
clienttab[client->number] = NULL;
spin_unlock_irqrestore(&clients_lock, flags);
spin_unlock_irq(&clients_lock);
snd_seq_delete_all_ports(client);
snd_seq_queue_client_leave(client->number);
snd_use_lock_sync(&client->use_lock);
snd_seq_queue_client_termination(client->number);
if (client->pool)
snd_seq_pool_delete(&client->pool);
spin_lock_irqsave(&clients_lock, flags);
spin_lock_irq(&clients_lock);
clienttablock[client->number] = 0;
spin_unlock_irqrestore(&clients_lock, flags);
spin_unlock_irq(&clients_lock);
return 0;
}
@@ -1900,20 +1932,14 @@ static int snd_seq_ioctl_get_subscription(struct snd_seq_client *client,
int result;
struct snd_seq_client *sender = NULL;
struct snd_seq_client_port *sport = NULL;
struct snd_seq_subscribers *p;
result = -EINVAL;
if ((sender = snd_seq_client_use_ptr(subs->sender.client)) == NULL)
goto __end;
if ((sport = snd_seq_port_use_ptr(sender, subs->sender.port)) == NULL)
goto __end;
p = snd_seq_port_get_subscription(&sport->c_src, &subs->dest);
if (p) {
result = 0;
*subs = p->info;
} else
result = -ENOENT;
result = snd_seq_port_get_subscription(&sport->c_src, &subs->dest,
subs);
__end:
if (sport)
snd_seq_port_unlock(sport);
@@ -2227,12 +2253,13 @@ int snd_seq_delete_kernel_client(int client)
}
EXPORT_SYMBOL(snd_seq_delete_kernel_client);
/* skeleton to enqueue event, called from snd_seq_kernel_client_enqueue
* and snd_seq_kernel_client_enqueue_blocking
/*
* exported, called by kernel clients to enqueue events (w/o blocking)
*
* RETURN VALUE: zero if succeed, negative if error
*/
static int kernel_client_enqueue(int client, struct snd_seq_event *ev,
struct file *file, int blocking,
int atomic, int hop)
int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event *ev,
struct file *file, bool blocking)
{
struct snd_seq_client *cptr;
int result;
@@ -2255,41 +2282,21 @@ static int kernel_client_enqueue(int client, struct snd_seq_event *ev,
if (cptr == NULL)
return -EINVAL;
if (! cptr->accept_output)
if (!cptr->accept_output) {
result = -EPERM;
else /* send it */
} else { /* send it */
mutex_lock(&cptr->ioctl_mutex);
result = snd_seq_client_enqueue_event(cptr, ev, file, blocking,
atomic, hop, NULL);
false, 0,
&cptr->ioctl_mutex);
mutex_unlock(&cptr->ioctl_mutex);
}
snd_seq_client_unlock(cptr);
return result;
}
/*
* exported, called by kernel clients to enqueue events (w/o blocking)
*
* RETURN VALUE: zero if succeed, negative if error
*/
int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event * ev,
int atomic, int hop)
{
return kernel_client_enqueue(client, ev, NULL, 0, atomic, hop);
}
EXPORT_SYMBOL(snd_seq_kernel_client_enqueue);
/*
* exported, called by kernel clients to enqueue events (with blocking)
*
* RETURN VALUE: zero if succeed, negative if error
*/
int snd_seq_kernel_client_enqueue_blocking(int client, struct snd_seq_event * ev,
struct file *file,
int atomic, int hop)
{
return kernel_client_enqueue(client, ev, file, 1, atomic, hop);
}
EXPORT_SYMBOL(snd_seq_kernel_client_enqueue_blocking);
/*
* exported, called by kernel clients to dispatch events directly to other
* clients, bypassing the queues. Event time-stamp will be updated.

Ver ficheiro

@@ -93,14 +93,14 @@ struct snd_seq_client *snd_seq_client_use_ptr(int clientid);
/* dispatch event to client(s) */
int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop);
/* exported to other modules */
int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event *ev, int atomic, int hop);
int snd_seq_kernel_client_enqueue_blocking(int client, struct snd_seq_event * ev,
struct file *file, int atomic, int hop);
int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait);
int snd_seq_client_notify_subscription(int client, int port,
struct snd_seq_port_subscribe *info, int evtype);
/* only for OSS sequencer */
bool snd_seq_client_ioctl_lock(int clientid);
void snd_seq_client_ioctl_unlock(int clientid);
extern int seq_client_load[15];
#endif

Ver ficheiro

@@ -98,18 +98,17 @@ static struct snd_seq_event_cell *fifo_cell_out(struct snd_seq_fifo *f);
void snd_seq_fifo_clear(struct snd_seq_fifo *f)
{
struct snd_seq_event_cell *cell;
unsigned long flags;
/* clear overflow flag */
atomic_set(&f->overflow, 0);
snd_use_lock_sync(&f->use_lock);
spin_lock_irqsave(&f->lock, flags);
spin_lock_irq(&f->lock);
/* drain the fifo */
while ((cell = fifo_cell_out(f)) != NULL) {
snd_seq_cell_free(cell);
}
spin_unlock_irqrestore(&f->lock, flags);
spin_unlock_irq(&f->lock);
}
@@ -195,9 +194,9 @@ int snd_seq_fifo_cell_out(struct snd_seq_fifo *f,
}
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&f->input_sleep, &wait);
spin_unlock_irq(&f->lock);
spin_unlock_irqrestore(&f->lock, flags);
schedule();
spin_lock_irq(&f->lock);
spin_lock_irqsave(&f->lock, flags);
remove_wait_queue(&f->input_sleep, &wait);
if (signal_pending(current)) {
spin_unlock_irqrestore(&f->lock, flags);
@@ -239,7 +238,6 @@ int snd_seq_fifo_poll_wait(struct snd_seq_fifo *f, struct file *file,
/* change the size of pool; all old events are removed */
int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize)
{
unsigned long flags;
struct snd_seq_pool *newpool, *oldpool;
struct snd_seq_event_cell *cell, *next, *oldhead;
@@ -255,7 +253,7 @@ int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize)
return -ENOMEM;
}
spin_lock_irqsave(&f->lock, flags);
spin_lock_irq(&f->lock);
/* remember old pool */
oldpool = f->pool;
oldhead = f->head;
@@ -265,7 +263,7 @@ int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize)
f->tail = NULL;
f->cells = 0;
/* NOTE: overflow flag is not cleared */
spin_unlock_irqrestore(&f->lock, flags);
spin_unlock_irq(&f->lock);
/* close the old pool and wait until all users are gone */
snd_seq_pool_mark_closing(oldpool);

Ver ficheiro

@@ -24,7 +24,7 @@
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/sched/signal.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <sound/core.h>
#include <sound/seq_kernel.h>
@@ -244,13 +244,13 @@ static int snd_seq_cell_alloc(struct snd_seq_pool *pool,
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&pool->output_sleep, &wait);
spin_unlock_irq(&pool->lock);
spin_unlock_irqrestore(&pool->lock, flags);
if (mutexp)
mutex_unlock(mutexp);
schedule();
if (mutexp)
mutex_lock(mutexp);
spin_lock_irq(&pool->lock);
spin_lock_irqsave(&pool->lock, flags);
remove_wait_queue(&pool->output_sleep, &wait);
/* interrupted? */
if (signal_pending(current)) {
@@ -384,21 +384,20 @@ int snd_seq_pool_init(struct snd_seq_pool *pool)
{
int cell;
struct snd_seq_event_cell *cellptr;
unsigned long flags;
if (snd_BUG_ON(!pool))
return -EINVAL;
cellptr = vmalloc(array_size(sizeof(struct snd_seq_event_cell),
pool->size));
cellptr = kvmalloc_array(sizeof(struct snd_seq_event_cell), pool->size,
GFP_KERNEL);
if (!cellptr)
return -ENOMEM;
/* add new cells to the free cell list */
spin_lock_irqsave(&pool->lock, flags);
spin_lock_irq(&pool->lock);
if (pool->ptr) {
spin_unlock_irqrestore(&pool->lock, flags);
vfree(cellptr);
spin_unlock_irq(&pool->lock);
kvfree(cellptr);
return 0;
}
@@ -416,7 +415,7 @@ int snd_seq_pool_init(struct snd_seq_pool *pool)
/* init statistics */
pool->max_used = 0;
pool->total_elements = pool->size;
spin_unlock_irqrestore(&pool->lock, flags);
spin_unlock_irq(&pool->lock);
return 0;
}
@@ -435,7 +434,6 @@ void snd_seq_pool_mark_closing(struct snd_seq_pool *pool)
/* remove events */
int snd_seq_pool_done(struct snd_seq_pool *pool)
{
unsigned long flags;
struct snd_seq_event_cell *ptr;
if (snd_BUG_ON(!pool))
@@ -449,18 +447,18 @@ int snd_seq_pool_done(struct snd_seq_pool *pool)
schedule_timeout_uninterruptible(1);
/* release all resources */
spin_lock_irqsave(&pool->lock, flags);
spin_lock_irq(&pool->lock);
ptr = pool->ptr;
pool->ptr = NULL;
pool->free = NULL;
pool->total_elements = 0;
spin_unlock_irqrestore(&pool->lock, flags);
spin_unlock_irq(&pool->lock);
vfree(ptr);
kvfree(ptr);
spin_lock_irqsave(&pool->lock, flags);
spin_lock_irq(&pool->lock);
pool->closing = 0;
spin_unlock_irqrestore(&pool->lock, flags);
spin_unlock_irq(&pool->lock);
return 0;
}

Ver ficheiro

@@ -128,7 +128,6 @@ static void port_subs_info_init(struct snd_seq_port_subs_info *grp)
struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
int port)
{
unsigned long flags;
struct snd_seq_client_port *new_port, *p;
int num = -1;
@@ -157,7 +156,7 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
num = port >= 0 ? port : 0;
mutex_lock(&client->ports_mutex);
write_lock_irqsave(&client->ports_lock, flags);
write_lock_irq(&client->ports_lock);
list_for_each_entry(p, &client->ports_list_head, list) {
if (p->addr.port > num)
break;
@@ -169,7 +168,7 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
client->num_ports++;
new_port->addr.port = num; /* store the port number in the port */
sprintf(new_port->name, "port-%d", num);
write_unlock_irqrestore(&client->ports_lock, flags);
write_unlock_irq(&client->ports_lock);
mutex_unlock(&client->ports_mutex);
return new_port;
@@ -283,11 +282,10 @@ static int port_delete(struct snd_seq_client *client,
/* delete a port with the given port id */
int snd_seq_delete_port(struct snd_seq_client *client, int port)
{
unsigned long flags;
struct snd_seq_client_port *found = NULL, *p;
mutex_lock(&client->ports_mutex);
write_lock_irqsave(&client->ports_lock, flags);
write_lock_irq(&client->ports_lock);
list_for_each_entry(p, &client->ports_list_head, list) {
if (p->addr.port == port) {
/* ok found. delete from the list at first */
@@ -297,7 +295,7 @@ int snd_seq_delete_port(struct snd_seq_client *client, int port)
break;
}
}
write_unlock_irqrestore(&client->ports_lock, flags);
write_unlock_irq(&client->ports_lock);
mutex_unlock(&client->ports_mutex);
if (found)
return port_delete(client, found);
@@ -308,7 +306,6 @@ int snd_seq_delete_port(struct snd_seq_client *client, int port)
/* delete the all ports belonging to the given client */
int snd_seq_delete_all_ports(struct snd_seq_client *client)
{
unsigned long flags;
struct list_head deleted_list;
struct snd_seq_client_port *port, *tmp;
@@ -316,7 +313,7 @@ int snd_seq_delete_all_ports(struct snd_seq_client *client)
* clear the port list in the client data.
*/
mutex_lock(&client->ports_mutex);
write_lock_irqsave(&client->ports_lock, flags);
write_lock_irq(&client->ports_lock);
if (! list_empty(&client->ports_list_head)) {
list_add(&deleted_list, &client->ports_list_head);
list_del_init(&client->ports_list_head);
@@ -324,7 +321,7 @@ int snd_seq_delete_all_ports(struct snd_seq_client *client)
INIT_LIST_HEAD(&deleted_list);
}
client->num_ports = 0;
write_unlock_irqrestore(&client->ports_lock, flags);
write_unlock_irq(&client->ports_lock);
/* remove each port in deleted_list */
list_for_each_entry_safe(port, tmp, &deleted_list, list) {
@@ -550,10 +547,10 @@ static void delete_and_unsubscribe_port(struct snd_seq_client *client,
list_del_init(list);
grp->exclusive = 0;
write_unlock_irq(&grp->list_lock);
up_write(&grp->list_mutex);
if (!empty)
unsubscribe_port(client, port, grp, &subs->info, ack);
up_write(&grp->list_mutex);
}
/* connect two ports */
@@ -635,20 +632,23 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector,
/* get matched subscriber */
struct snd_seq_subscribers *snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp,
struct snd_seq_addr *dest_addr)
int snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp,
struct snd_seq_addr *dest_addr,
struct snd_seq_port_subscribe *subs)
{
struct snd_seq_subscribers *s, *found = NULL;
struct snd_seq_subscribers *s;
int err = -ENOENT;
down_read(&src_grp->list_mutex);
list_for_each_entry(s, &src_grp->list_head, src_list) {
if (addr_match(dest_addr, &s->info.dest)) {
found = s;
*subs = s->info;
err = 0;
break;
}
}
up_read(&src_grp->list_mutex);
return found;
return err;
}
/*

Ver ficheiro

@@ -135,7 +135,8 @@ int snd_seq_port_subscribe(struct snd_seq_client_port *port,
struct snd_seq_port_subscribe *info);
/* get matched subscriber */
struct snd_seq_subscribers *snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp,
struct snd_seq_addr *dest_addr);
int snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp,
struct snd_seq_addr *dest_addr,
struct snd_seq_port_subscribe *subs);
#endif

Ver ficheiro

@@ -134,8 +134,11 @@ static struct snd_minor *autoload_device(unsigned int minor)
if (dev == SNDRV_MINOR_CONTROL) {
/* /dev/aloadC? */
int card = SNDRV_MINOR_CARD(minor);
if (snd_cards[card] == NULL)
struct snd_card *ref = snd_card_ref(card);
if (!ref)
snd_request_card(card);
else
snd_card_unref(ref);
} else if (dev == SNDRV_MINOR_GLOBAL) {
/* /dev/aloadSEQ */
snd_request_other(minor);

Ver ficheiro

@@ -38,6 +38,7 @@
/* internal flags */
#define SNDRV_TIMER_IFLG_PAUSED 0x00010000
#define SNDRV_TIMER_IFLG_DEAD 0x00020000
#if IS_ENABLED(CONFIG_SND_HRTIMER)
#define DEFAULT_TIMER_LIMIT 4
@@ -254,19 +255,20 @@ int snd_timer_open(struct snd_timer_instance **ti,
struct snd_timer_instance *timeri = NULL;
int err;
mutex_lock(&register_mutex);
if (tid->dev_class == SNDRV_TIMER_CLASS_SLAVE) {
/* open a slave instance */
if (tid->dev_sclass <= SNDRV_TIMER_SCLASS_NONE ||
tid->dev_sclass > SNDRV_TIMER_SCLASS_OSS_SEQUENCER) {
pr_debug("ALSA: timer: invalid slave class %i\n",
tid->dev_sclass);
return -EINVAL;
err = -EINVAL;
goto unlock;
}
mutex_lock(&register_mutex);
timeri = snd_timer_instance_new(owner, NULL);
if (!timeri) {
mutex_unlock(&register_mutex);
return -ENOMEM;
err = -ENOMEM;
goto unlock;
}
timeri->slave_class = tid->dev_sclass;
timeri->slave_id = tid->device;
@@ -277,13 +279,10 @@ int snd_timer_open(struct snd_timer_instance **ti,
snd_timer_close_locked(timeri);
timeri = NULL;
}
mutex_unlock(&register_mutex);
*ti = timeri;
return err;
goto unlock;
}
/* open a master instance */
mutex_lock(&register_mutex);
timer = snd_timer_find(tid);
#ifdef CONFIG_MODULES
if (!timer) {
@@ -294,25 +293,26 @@ int snd_timer_open(struct snd_timer_instance **ti,
}
#endif
if (!timer) {
mutex_unlock(&register_mutex);
return -ENODEV;
err = -ENODEV;
goto unlock;
}
if (!list_empty(&timer->open_list_head)) {
timeri = list_entry(timer->open_list_head.next,
struct snd_timer_instance, open_list);
if (timeri->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) {
mutex_unlock(&register_mutex);
return -EBUSY;
err = -EBUSY;
timeri = NULL;
goto unlock;
}
}
if (timer->num_instances >= timer->max_instances) {
mutex_unlock(&register_mutex);
return -EBUSY;
err = -EBUSY;
goto unlock;
}
timeri = snd_timer_instance_new(owner, timer);
if (!timeri) {
mutex_unlock(&register_mutex);
return -ENOMEM;
err = -ENOMEM;
goto unlock;
}
/* take a card refcount for safe disconnection */
if (timer->card)
@@ -321,16 +321,16 @@ int snd_timer_open(struct snd_timer_instance **ti,
timeri->slave_id = slave_id;
if (list_empty(&timer->open_list_head) && timer->hw.open) {
int err = timer->hw.open(timer);
err = timer->hw.open(timer);
if (err) {
kfree(timeri->owner);
kfree(timeri);
timeri = NULL;
if (timer->card)
put_device(&timer->card->card_dev);
module_put(timer->module);
mutex_unlock(&register_mutex);
return err;
goto unlock;
}
}
@@ -341,6 +341,8 @@ int snd_timer_open(struct snd_timer_instance **ti,
snd_timer_close_locked(timeri);
timeri = NULL;
}
unlock:
mutex_unlock(&register_mutex);
*ti = timeri;
return err;
@@ -353,15 +355,20 @@ EXPORT_SYMBOL(snd_timer_open);
*/
static int snd_timer_close_locked(struct snd_timer_instance *timeri)
{
struct snd_timer *timer = NULL;
struct snd_timer *timer = timeri->timer;
struct snd_timer_instance *slave, *tmp;
if (timer) {
spin_lock_irq(&timer->lock);
timeri->flags |= SNDRV_TIMER_IFLG_DEAD;
spin_unlock_irq(&timer->lock);
}
list_del(&timeri->open_list);
/* force to stop the timer */
snd_timer_stop(timeri);
timer = timeri->timer;
if (timer) {
timer->num_instances--;
/* wait, until the active callback is finished */
@@ -497,6 +504,10 @@ static int snd_timer_start1(struct snd_timer_instance *timeri,
return -EINVAL;
spin_lock_irqsave(&timer->lock, flags);
if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) {
result = -EINVAL;
goto unlock;
}
if (timer->card && timer->card->shutdown) {
result = -ENODEV;
goto unlock;
@@ -541,11 +552,16 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri,
bool start)
{
unsigned long flags;
int err;
spin_lock_irqsave(&slave_active_lock, flags);
if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) {
err = -EINVAL;
goto unlock;
}
if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) {
spin_unlock_irqrestore(&slave_active_lock, flags);
return -EBUSY;
err = -EBUSY;
goto unlock;
}
timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
if (timeri->master && timeri->timer) {
@@ -556,8 +572,10 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri,
SNDRV_TIMER_EVENT_CONTINUE);
spin_unlock(&timeri->timer->lock);
}
err = 1; /* delayed start */
unlock:
spin_unlock_irqrestore(&slave_active_lock, flags);
return 1; /* delayed start */
return err;
}
/* stop/pause a master timer */
@@ -720,6 +738,46 @@ static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_l
timer->sticks = ticks;
}
/* call callbacks in timer ack list */
static void snd_timer_process_callbacks(struct snd_timer *timer,
struct list_head *head)
{
struct snd_timer_instance *ti;
unsigned long resolution, ticks;
while (!list_empty(head)) {
ti = list_first_entry(head, struct snd_timer_instance,
ack_list);
/* remove from ack_list and make empty */
list_del_init(&ti->ack_list);
if (!(ti->flags & SNDRV_TIMER_IFLG_DEAD)) {
ticks = ti->pticks;
ti->pticks = 0;
resolution = ti->resolution;
ti->flags |= SNDRV_TIMER_IFLG_CALLBACK;
spin_unlock(&timer->lock);
if (ti->callback)
ti->callback(ti, resolution, ticks);
spin_lock(&timer->lock);
ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;
}
}
}
/* clear pending instances from ack list */
static void snd_timer_clear_callbacks(struct snd_timer *timer,
struct list_head *head)
{
unsigned long flags;
spin_lock_irqsave(&timer->lock, flags);
while (!list_empty(head))
list_del_init(head->next);
spin_unlock_irqrestore(&timer->lock, flags);
}
/*
* timer tasklet
*
@@ -727,34 +785,15 @@ static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_l
static void snd_timer_tasklet(unsigned long arg)
{
struct snd_timer *timer = (struct snd_timer *) arg;
struct snd_timer_instance *ti;
struct list_head *p;
unsigned long resolution, ticks;
unsigned long flags;
if (timer->card && timer->card->shutdown)
if (timer->card && timer->card->shutdown) {
snd_timer_clear_callbacks(timer, &timer->sack_list_head);
return;
}
spin_lock_irqsave(&timer->lock, flags);
/* now process all callbacks */
while (!list_empty(&timer->sack_list_head)) {
p = timer->sack_list_head.next; /* get first item */
ti = list_entry(p, struct snd_timer_instance, ack_list);
/* remove from ack_list and make empty */
list_del_init(p);
ticks = ti->pticks;
ti->pticks = 0;
resolution = ti->resolution;
ti->flags |= SNDRV_TIMER_IFLG_CALLBACK;
spin_unlock(&timer->lock);
if (ti->callback)
ti->callback(ti, resolution, ticks);
spin_lock(&timer->lock);
ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;
}
snd_timer_process_callbacks(timer, &timer->sack_list_head);
spin_unlock_irqrestore(&timer->lock, flags);
}
@@ -767,16 +806,18 @@ static void snd_timer_tasklet(unsigned long arg)
void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
{
struct snd_timer_instance *ti, *ts, *tmp;
unsigned long resolution, ticks;
struct list_head *p, *ack_list_head;
unsigned long resolution;
struct list_head *ack_list_head;
unsigned long flags;
int use_tasklet = 0;
if (timer == NULL)
return;
if (timer->card && timer->card->shutdown)
if (timer->card && timer->card->shutdown) {
snd_timer_clear_callbacks(timer, &timer->ack_list_head);
return;
}
spin_lock_irqsave(&timer->lock, flags);
@@ -790,6 +831,8 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
*/
list_for_each_entry_safe(ti, tmp, &timer->active_list_head,
active_list) {
if (ti->flags & SNDRV_TIMER_IFLG_DEAD)
continue;
if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING))
continue;
ti->pticks += ticks_left;
@@ -839,23 +882,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
}
/* now process all fast callbacks */
while (!list_empty(&timer->ack_list_head)) {
p = timer->ack_list_head.next; /* get first item */
ti = list_entry(p, struct snd_timer_instance, ack_list);
/* remove from ack_list and make empty */
list_del_init(p);
ticks = ti->pticks;
ti->pticks = 0;
ti->flags |= SNDRV_TIMER_IFLG_CALLBACK;
spin_unlock(&timer->lock);
if (ti->callback)
ti->callback(ti, resolution, ticks);
spin_lock(&timer->lock);
ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;
}
snd_timer_process_callbacks(timer, &timer->ack_list_head);
/* do we have any slow callbacks? */
use_tasklet = !list_empty(&timer->sack_list_head);
@@ -1882,7 +1909,10 @@ static int snd_timer_user_start(struct file *file)
snd_timer_stop(tu->timeri);
tu->timeri->lost = 0;
tu->last_resolution = 0;
return (err = snd_timer_start(tu->timeri, tu->ticks)) < 0 ? err : 0;
err = snd_timer_start(tu->timeri, tu->ticks);
if (err < 0)
return err;
return 0;
}
static int snd_timer_user_stop(struct file *file)
@@ -1893,7 +1923,10 @@ static int snd_timer_user_stop(struct file *file)
tu = file->private_data;
if (!tu->timeri)
return -EBADFD;
return (err = snd_timer_stop(tu->timeri)) < 0 ? err : 0;
err = snd_timer_stop(tu->timeri);
if (err < 0)
return err;
return 0;
}
static int snd_timer_user_continue(struct file *file)
@@ -1908,7 +1941,10 @@ static int snd_timer_user_continue(struct file *file)
if (!(tu->timeri->flags & SNDRV_TIMER_IFLG_PAUSED))
return snd_timer_user_start(file);
tu->timeri->lost = 0;
return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0;
err = snd_timer_continue(tu->timeri);
if (err < 0)
return err;
return 0;
}
static int snd_timer_user_pause(struct file *file)
@@ -1919,7 +1955,10 @@ static int snd_timer_user_pause(struct file *file)
tu = file->private_data;
if (!tu->timeri)
return -EBADFD;
return (err = snd_timer_pause(tu->timeri)) < 0 ? err : 0;
err = snd_timer_pause(tu->timeri);
if (err < 0)
return err;
return 0;
}
enum {

Ver ficheiro

@@ -337,7 +337,7 @@ static int loopback_prepare(struct snd_pcm_substream *substream)
loopback_timer_stop_sync(dpcm);
salign = (snd_pcm_format_width(runtime->format) *
salign = (snd_pcm_format_physical_width(runtime->format) *
runtime->channels) / 8;
bps = salign * runtime->rate;
if (bps <= 0 || salign <= 0)
@@ -562,6 +562,8 @@ static const struct snd_pcm_hardware loopback_pcm_hardware =
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_RESUME),
.formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |
SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE),
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000,

Ver ficheiro

@@ -56,8 +56,9 @@
#define INTERRUPT_INTERVAL 16
#define QUEUE_LENGTH 48
#define IN_PACKET_HEADER_SIZE 4
#define IR_HEADER_SIZE 8 // For header and timestamp.
#define OUT_PACKET_HEADER_SIZE 0
#define HEADER_TSTAMP_MASK 0x0000ffff
static void pcm_period_tasklet(unsigned long data);
@@ -456,7 +457,7 @@ static inline int queue_out_packet(struct amdtp_stream *s,
static inline int queue_in_packet(struct amdtp_stream *s)
{
return queue_packet(s, IN_PACKET_HEADER_SIZE, s->max_payload_length);
return queue_packet(s, IR_HEADER_SIZE, s->max_payload_length);
}
static int handle_out_packet(struct amdtp_stream *s,
@@ -701,13 +702,6 @@ static inline u32 increment_cycle_count(u32 cycle, unsigned int addend)
return cycle;
}
static inline u32 decrement_cycle_count(u32 cycle, unsigned int subtrahend)
{
if (cycle < subtrahend)
cycle += 8 * CYCLES_PER_SECOND;
return cycle - subtrahend;
}
static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
size_t header_length, void *header,
void *private_data)
@@ -745,29 +739,26 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
struct amdtp_stream *s = private_data;
unsigned int i, packets;
unsigned int payload_length, max_payload_length;
__be32 *headers = header;
u32 cycle;
__be32 *ctx_header = header;
if (s->packet_index < 0)
return;
/* The number of packets in buffer */
packets = header_length / IN_PACKET_HEADER_SIZE;
cycle = compute_cycle_count(tstamp);
/* Align to actual cycle count for the last packet. */
cycle = decrement_cycle_count(cycle, packets);
packets = header_length / IR_HEADER_SIZE;
/* For buffer-over-run prevention. */
max_payload_length = s->max_payload_length;
for (i = 0; i < packets; i++) {
cycle = increment_cycle_count(cycle, 1);
u32 iso_header = be32_to_cpu(ctx_header[0]);
unsigned int cycle;
tstamp = be32_to_cpu(ctx_header[1]) & HEADER_TSTAMP_MASK;
cycle = compute_cycle_count(tstamp);
/* The number of bytes in this packet */
payload_length =
(be32_to_cpu(headers[i]) >> ISO_DATA_LENGTH_SHIFT);
payload_length = iso_header >> ISO_DATA_LENGTH_SHIFT;
if (payload_length > max_payload_length) {
dev_err(&s->unit->device,
"Detect jumbo payload: %04x %04x\n",
@@ -777,6 +768,8 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
if (s->handle_packet(s, payload_length, cycle, i) < 0)
break;
ctx_header += IR_HEADER_SIZE / sizeof(__be32);
}
/* Queueing error or detecting invalid payload. */
@@ -797,6 +790,7 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
void *header, void *private_data)
{
struct amdtp_stream *s = private_data;
__be32 *ctx_header = header;
u32 cycle;
unsigned int packets;
@@ -807,11 +801,10 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
s->callbacked = true;
wake_up(&s->callback_wait);
cycle = compute_cycle_count(tstamp);
if (s->direction == AMDTP_IN_STREAM) {
packets = header_length / IN_PACKET_HEADER_SIZE;
cycle = decrement_cycle_count(cycle, packets);
tstamp = be32_to_cpu(ctx_header[1]) & HEADER_TSTAMP_MASK;
cycle = compute_cycle_count(tstamp);
context->callback.sc = in_stream_callback;
if (s->flags & CIP_NO_HEADER)
s->handle_packet = handle_in_packet_without_header;
@@ -819,6 +812,7 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
s->handle_packet = handle_in_packet;
} else {
packets = header_length / 4;
cycle = compute_cycle_count(tstamp);
cycle = increment_cycle_count(cycle, QUEUE_LENGTH - packets);
context->callback.sc = out_stream_callback;
if (s->flags & CIP_NO_HEADER)
@@ -880,7 +874,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
if (s->direction == AMDTP_IN_STREAM) {
dir = DMA_FROM_DEVICE;
type = FW_ISO_CONTEXT_RECEIVE;
header_size = IN_PACKET_HEADER_SIZE;
header_size = IR_HEADER_SIZE;
} else {
dir = DMA_TO_DEVICE;
type = FW_ISO_CONTEXT_TRANSMIT;

Ver ficheiro

@@ -412,6 +412,12 @@ int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
CIP_HEADER_WITHOUT_EOH;
fmt = CIP_FMT_MOTU_TX_V3;
}
if (protocol == &snd_motu_protocol_v2) {
// 8pre has some quirks.
flags |= CIP_WRONG_DBS |
CIP_SKIP_DBC_ZERO_CHECK;
}
} else {
process_data_blocks = process_rx_data_blocks;
flags |= CIP_DBC_IS_END_EVENT;

Ver ficheiro

@@ -15,6 +15,8 @@
#define V2_CLOCK_SRC_SHIFT 0
#define V2_CLOCK_TRAVELER_FETCH_DISABLE 0x04000000
#define V2_CLOCK_TRAVELER_FETCH_ENABLE 0x03000000
#define V2_CLOCK_8PRE_FETCH_DISABLE 0x02000000
#define V2_CLOCK_8PRE_FETCH_ENABLE 0x00000000
#define V2_IN_OUT_CONF_OFFSET 0x0c04
#define V2_OPT_OUT_IFACE_MASK 0x00000c00
@@ -132,20 +134,31 @@ static int v2_switch_fetching_mode(struct snd_motu *motu, bool enable)
u32 data;
int err = 0;
if (motu->spec == &snd_motu_spec_traveler) {
if (motu->spec == &snd_motu_spec_traveler ||
motu->spec == &snd_motu_spec_8pre) {
err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET,
&reg, sizeof(reg));
if (err < 0)
return err;
data = be32_to_cpu(reg);
data &= ~(V2_CLOCK_TRAVELER_FETCH_DISABLE |
V2_CLOCK_TRAVELER_FETCH_ENABLE);
if (motu->spec == &snd_motu_spec_traveler) {
data &= ~(V2_CLOCK_TRAVELER_FETCH_DISABLE |
V2_CLOCK_TRAVELER_FETCH_ENABLE);
if (enable)
data |= V2_CLOCK_TRAVELER_FETCH_ENABLE;
else
data |= V2_CLOCK_TRAVELER_FETCH_DISABLE;
if (enable)
data |= V2_CLOCK_TRAVELER_FETCH_ENABLE;
else
data |= V2_CLOCK_TRAVELER_FETCH_DISABLE;
} else if (motu->spec == &snd_motu_spec_8pre) {
data &= ~(V2_CLOCK_8PRE_FETCH_DISABLE |
V2_CLOCK_8PRE_FETCH_ENABLE);
if (enable)
data |= V2_CLOCK_8PRE_FETCH_DISABLE;
else
data |= V2_CLOCK_8PRE_FETCH_ENABLE;
}
reg = cpu_to_be32(data);
err = snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET,
@@ -220,10 +233,16 @@ static void calculate_differed_part(struct snd_motu_packet_format *formats,
* interfaces.
*/
data = (data & mask) >> shift;
if ((flags & SND_MOTU_SPEC_HAS_OPT_IFACE_A) &&
data == V2_OPT_IFACE_MODE_ADAT) {
pcm_chunks[0] += 8;
pcm_chunks[1] += 4;
if (data == V2_OPT_IFACE_MODE_ADAT) {
if (flags & SND_MOTU_SPEC_HAS_OPT_IFACE_A) {
pcm_chunks[0] += 8;
pcm_chunks[1] += 4;
}
// 8pre has two sets of optical interface and doesn't reduce
// chunks for ADAT signals.
if (flags & SND_MOTU_SPEC_HAS_OPT_IFACE_B) {
pcm_chunks[1] += 4;
}
}
/* At mode x4, no data chunks are supported in this part. */

Ver ficheiro

@@ -203,6 +203,20 @@ const struct snd_motu_spec snd_motu_spec_traveler = {
.analog_out_ports = 8,
};
const struct snd_motu_spec snd_motu_spec_8pre = {
.name = "8pre",
.protocol = &snd_motu_protocol_v2,
// In tx, use coax chunks for mix-return 1/2. In rx, use coax chunks for
// dummy 1/2.
.flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
SND_MOTU_SPEC_HAS_OPT_IFACE_A |
SND_MOTU_SPEC_HAS_OPT_IFACE_B |
SND_MOTU_SPEC_RX_MIDI_2ND_Q |
SND_MOTU_SPEC_TX_MIDI_2ND_Q,
.analog_in_ports = 8,
.analog_out_ports = 2,
};
static const struct snd_motu_spec motu_828mk3 = {
.name = "828mk3",
.protocol = &snd_motu_protocol_v3,
@@ -248,6 +262,7 @@ static const struct snd_motu_spec motu_audio_express = {
static const struct ieee1394_device_id motu_id_table[] = {
SND_MOTU_DEV_ENTRY(0x000003, &motu_828mk2),
SND_MOTU_DEV_ENTRY(0x000009, &snd_motu_spec_traveler),
SND_MOTU_DEV_ENTRY(0x00000f, &snd_motu_spec_8pre),
SND_MOTU_DEV_ENTRY(0x000015, &motu_828mk3), /* FireWire only. */
SND_MOTU_DEV_ENTRY(0x000035, &motu_828mk3), /* Hybrid. */
SND_MOTU_DEV_ENTRY(0x000033, &motu_audio_express),

Ver ficheiro

@@ -130,6 +130,7 @@ extern const struct snd_motu_protocol snd_motu_protocol_v2;
extern const struct snd_motu_protocol snd_motu_protocol_v3;
extern const struct snd_motu_spec snd_motu_spec_traveler;
extern const struct snd_motu_spec snd_motu_spec_8pre;
int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir,

Ver ficheiro

@@ -104,9 +104,7 @@ int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev,
return ret;
bus->ext_ops = ext_ops;
INIT_LIST_HEAD(&bus->hlink_list);
bus->idx = idx++;
bus->cmd_dma_state = true;
return 0;

Ver ficheiro

@@ -39,6 +39,7 @@ int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
spin_lock_init(&bus->reg_lock);
mutex_init(&bus->cmd_mutex);
mutex_init(&bus->lock);
INIT_LIST_HEAD(&bus->hlink_list);
bus->irq = -1;
return 0;
}

Ver ficheiro

@@ -306,7 +306,7 @@ static void snd_gf1_mem_info_read(struct snd_info_entry *entry,
used = 0;
for (block = alloc->first, i = 0; block; block = block->next, i++) {
used += block->size;
snd_iprintf(buffer, "Block %i at 0x%lx onboard 0x%x size %i (0x%x):\n", i, (long) block, block->ptr, block->size, block->size);
snd_iprintf(buffer, "Block %i onboard 0x%x size %i (0x%x):\n", i, block->ptr, block->size, block->size);
if (block->share ||
block->share_id[0] || block->share_id[1] ||
block->share_id[2] || block->share_id[3])

Ver ficheiro

@@ -24,14 +24,18 @@
static int __init alsa_sound_last_init(void)
{
struct snd_card *card;
int idx, ok = 0;
printk(KERN_INFO "ALSA device list:\n");
for (idx = 0; idx < SNDRV_CARDS; idx++)
if (snd_cards[idx] != NULL) {
printk(KERN_INFO " #%i: %s\n", idx, snd_cards[idx]->longname);
for (idx = 0; idx < SNDRV_CARDS; idx++) {
card = snd_card_ref(idx);
if (card) {
printk(KERN_INFO " #%i: %s\n", idx, card->longname);
snd_card_unref(card);
ok++;
}
}
if (ok == 0)
printk(KERN_INFO " No soundcards found.\n");
return 0;

Ver ficheiro

@@ -1882,22 +1882,8 @@ int snd_emu10k1_create(struct snd_card *card,
c->name, pci->vendor, pci->device,
emu->serial);
if (!*card->id && c->id) {
int i, n = 0;
if (!*card->id && c->id)
strlcpy(card->id, c->id, sizeof(card->id));
for (;;) {
for (i = 0; i < snd_ecards_limit; i++) {
if (snd_cards[i] && !strcmp(snd_cards[i]->id, card->id))
break;
}
if (i >= snd_ecards_limit)
break;
n++;
if (n >= SNDRV_CARDS)
break;
snprintf(card->id, sizeof(card->id), "%s_%d", c->id, n);
}
}
is_audigy = emu->audigy = c->emu10k2_chip;

Ver ficheiro

@@ -832,7 +832,13 @@ static int snd_hda_codec_dev_free(struct snd_device *device)
struct hda_codec *codec = device->device_data;
codec->in_freeing = 1;
snd_hdac_device_unregister(&codec->core);
/*
* snd_hda_codec_device_new() is used by legacy HDA and ASoC driver.
* We can't unregister ASoC device since it will be unregistered in
* snd_hdac_ext_bus_device_remove().
*/
if (codec->core.type == HDA_DEV_LEGACY)
snd_hdac_device_unregister(&codec->core);
codec_display_power(codec, false);
put_device(hda_codec_dev(codec));
return 0;

Ver ficheiro

@@ -1788,9 +1788,6 @@ static int azx_first_init(struct azx *chip)
chip->msi = 0;
}
if (azx_acquire_irq(chip, 0) < 0)
return -EBUSY;
pci_set_master(pci);
synchronize_irq(bus->irq);
@@ -1904,6 +1901,9 @@ static int azx_first_init(struct azx *chip)
return -ENODEV;
}
if (azx_acquire_irq(chip, 0) < 0)
return -EBUSY;
strcpy(card->driver, "HDA-Intel");
strlcpy(card->shortname, driver_short_names[chip->driver_type],
sizeof(card->shortname));
@@ -2378,6 +2378,12 @@ static const struct pci_device_id azx_ids[] = {
/* Cannonlake */
{ PCI_DEVICE(0x8086, 0x9dc8),
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
/* CometLake-LP */
{ PCI_DEVICE(0x8086, 0x02C8),
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
/* CometLake-H */
{ PCI_DEVICE(0x8086, 0x06C8),
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
/* Icelake */
{ PCI_DEVICE(0x8086, 0x34c8),
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},

Ver ficheiro

@@ -1551,9 +1551,11 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
ret = !repoll || !eld->monitor_present || eld->eld_valid;
jack = snd_hda_jack_tbl_get(codec, pin_nid);
if (jack)
if (jack) {
jack->block_report = !ret;
jack->pin_sense = (eld->monitor_present && eld->eld_valid) ?
AC_PINSENSE_PRESENCE : 0;
}
mutex_unlock(&per_pin->lock);
return ret;
}
@@ -1663,6 +1665,11 @@ static void hdmi_repoll_eld(struct work_struct *work)
container_of(to_delayed_work(work), struct hdmi_spec_per_pin, work);
struct hda_codec *codec = per_pin->codec;
struct hdmi_spec *spec = codec->spec;
struct hda_jack_tbl *jack;
jack = snd_hda_jack_tbl_get(codec, per_pin->pin_nid);
if (jack)
jack->jack_dirty = 1;
if (per_pin->repoll_count++ > 6)
per_pin->repoll_count = 0;

Ver ficheiro

@@ -119,6 +119,7 @@ struct alc_spec {
unsigned int no_depop_delay:1;
unsigned int done_hp_init:1;
unsigned int no_shutup_pins:1;
unsigned int ultra_low_power:1;
/* for PLL fix */
hda_nid_t pll_nid;
@@ -803,11 +804,10 @@ static int alc_init(struct hda_codec *codec)
if (spec->init_hook)
spec->init_hook(codec);
snd_hda_gen_init(codec);
alc_fix_pll(codec);
alc_auto_init_amp(codec, spec->init_amp);
snd_hda_gen_init(codec);
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
return 0;
@@ -3197,7 +3197,7 @@ static void alc256_init(struct hda_codec *codec)
bool hp_pin_sense;
if (!hp_pin)
return;
hp_pin = 0x21;
msleep(30);
@@ -3207,17 +3207,25 @@ static void alc256_init(struct hda_codec *codec)
msleep(2);
alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */
if (spec->ultra_low_power) {
alc_update_coef_idx(codec, 0x03, 1<<1, 1<<1);
alc_update_coef_idx(codec, 0x08, 3<<2, 3<<2);
alc_update_coef_idx(codec, 0x08, 7<<4, 0);
alc_update_coef_idx(codec, 0x3b, 1<<15, 0);
alc_update_coef_idx(codec, 0x0e, 7<<6, 7<<6);
msleep(30);
}
snd_hda_codec_write(codec, hp_pin, 0,
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
if (hp_pin_sense)
if (hp_pin_sense || spec->ultra_low_power)
msleep(85);
snd_hda_codec_write(codec, hp_pin, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
if (hp_pin_sense)
if (hp_pin_sense || spec->ultra_low_power)
msleep(100);
alc_update_coef_idx(codec, 0x46, 3 << 12, 0);
@@ -3232,10 +3240,8 @@ static void alc256_shutup(struct hda_codec *codec)
hda_nid_t hp_pin = alc_get_hp_pin(spec);
bool hp_pin_sense;
if (!hp_pin) {
alc269_shutup(codec);
return;
}
if (!hp_pin)
hp_pin = 0x21;
hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
@@ -3245,7 +3251,7 @@ static void alc256_shutup(struct hda_codec *codec)
snd_hda_codec_write(codec, hp_pin, 0,
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
if (hp_pin_sense)
if (hp_pin_sense || spec->ultra_low_power)
msleep(85);
/* 3k pull low control for Headset jack. */
@@ -3256,11 +3262,20 @@ static void alc256_shutup(struct hda_codec *codec)
snd_hda_codec_write(codec, hp_pin, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
if (hp_pin_sense)
if (hp_pin_sense || spec->ultra_low_power)
msleep(100);
alc_auto_setup_eapd(codec, false);
alc_shutup_pins(codec);
if (spec->ultra_low_power) {
msleep(50);
alc_update_coef_idx(codec, 0x03, 1<<1, 0);
alc_update_coef_idx(codec, 0x08, 7<<4, 7<<4);
alc_update_coef_idx(codec, 0x08, 3<<2, 0);
alc_update_coef_idx(codec, 0x3b, 1<<15, 1<<15);
alc_update_coef_idx(codec, 0x0e, 7<<6, 0);
msleep(30);
}
}
static void alc225_init(struct hda_codec *codec)
@@ -3270,8 +3285,7 @@ static void alc225_init(struct hda_codec *codec)
bool hp1_pin_sense, hp2_pin_sense;
if (!hp_pin)
return;
hp_pin = 0x21;
msleep(30);
hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin);
@@ -3281,25 +3295,31 @@ static void alc225_init(struct hda_codec *codec)
msleep(2);
alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */
if (spec->ultra_low_power) {
alc_update_coef_idx(codec, 0x08, 0x0f << 2, 3<<2);
alc_update_coef_idx(codec, 0x0e, 7<<6, 7<<6);
alc_update_coef_idx(codec, 0x33, 1<<11, 0);
msleep(30);
}
if (hp1_pin_sense)
if (hp1_pin_sense || spec->ultra_low_power)
snd_hda_codec_write(codec, hp_pin, 0,
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
if (hp2_pin_sense)
snd_hda_codec_write(codec, 0x16, 0,
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
if (hp1_pin_sense || hp2_pin_sense)
if (hp1_pin_sense || hp2_pin_sense || spec->ultra_low_power)
msleep(85);
if (hp1_pin_sense)
if (hp1_pin_sense || spec->ultra_low_power)
snd_hda_codec_write(codec, hp_pin, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
if (hp2_pin_sense)
snd_hda_codec_write(codec, 0x16, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
if (hp1_pin_sense || hp2_pin_sense)
if (hp1_pin_sense || hp2_pin_sense || spec->ultra_low_power)
msleep(100);
alc_update_coef_idx(codec, 0x4a, 3 << 10, 0);
@@ -3312,11 +3332,8 @@ static void alc225_shutup(struct hda_codec *codec)
hda_nid_t hp_pin = alc_get_hp_pin(spec);
bool hp1_pin_sense, hp2_pin_sense;
if (!hp_pin) {
alc269_shutup(codec);
return;
}
if (!hp_pin)
hp_pin = 0x21;
/* 3k pull low control for Headset jack. */
alc_update_coef_idx(codec, 0x4a, 0, 3 << 10);
@@ -3326,28 +3343,36 @@ static void alc225_shutup(struct hda_codec *codec)
if (hp1_pin_sense || hp2_pin_sense)
msleep(2);
if (hp1_pin_sense)
if (hp1_pin_sense || spec->ultra_low_power)
snd_hda_codec_write(codec, hp_pin, 0,
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
if (hp2_pin_sense)
snd_hda_codec_write(codec, 0x16, 0,
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
if (hp1_pin_sense || hp2_pin_sense)
if (hp1_pin_sense || hp2_pin_sense || spec->ultra_low_power)
msleep(85);
if (hp1_pin_sense)
if (hp1_pin_sense || spec->ultra_low_power)
snd_hda_codec_write(codec, hp_pin, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
if (hp2_pin_sense)
snd_hda_codec_write(codec, 0x16, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
if (hp1_pin_sense || hp2_pin_sense)
if (hp1_pin_sense || hp2_pin_sense || spec->ultra_low_power)
msleep(100);
alc_auto_setup_eapd(codec, false);
alc_shutup_pins(codec);
if (spec->ultra_low_power) {
msleep(50);
alc_update_coef_idx(codec, 0x08, 0x0f << 2, 0x0c << 2);
alc_update_coef_idx(codec, 0x0e, 7<<6, 0);
alc_update_coef_idx(codec, 0x33, 1<<11, 1<<11);
alc_update_coef_idx(codec, 0x4a, 3<<4, 2<<4);
msleep(30);
}
}
static void alc_default_init(struct hda_codec *codec)
@@ -5527,7 +5552,12 @@ static void alc_fixup_headset_jack(struct hda_codec *codec,
static void alc295_fixup_chromebook(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
switch (action) {
case HDA_FIXUP_ACT_PRE_PROBE:
spec->ultra_low_power = true;
break;
case HDA_FIXUP_ACT_INIT:
switch (codec->core.vendor_id) {
case 0x10ec0295:
@@ -6933,6 +6963,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1462, 0xb120, "MSI Cubi MS-B120", ALC283_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1462, 0xb171, "Cubi N 8GL (MS-B171)", ALC283_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1558, 0x1325, "System76 Darter Pro (darp5)", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1558, 0x8550, "System76 Gazelle (gaze14)", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1558, 0x8560, "System76 Gazelle (gaze14)", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC233_FIXUP_LENOVO_MULTI_CODECS),
SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE),

Ver ficheiro

@@ -233,7 +233,6 @@ static int snd_ps3_program_dma(struct snd_ps3_card_info *card,
int fill_stages, dma_ch, stage;
enum snd_ps3_ch ch;
uint32_t ch0_kick_event = 0; /* initialize to mute gcc */
void *start_vaddr;
unsigned long irqsave;
int silent = 0;
@@ -257,7 +256,6 @@ static int snd_ps3_program_dma(struct snd_ps3_card_info *card,
fill_stages = 4;
spin_lock_irqsave(&card->dma_lock, irqsave);
for (ch = 0; ch < 2; ch++) {
start_vaddr = card->dma_next_transfer_vaddr[0];
for (stage = 0; stage < fill_stages; stage++) {
dma_ch = stage * 2 + ch;
if (silent)
@@ -526,9 +524,7 @@ static int snd_ps3_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
int pcm_index;
pcm_index = substream->pcm->device;
/* to retrieve substream/runtime in interrupt handler */
card->substream = substream;

Ver ficheiro

@@ -303,7 +303,7 @@ static void aica_period_elapsed(struct timer_list *t)
{
struct snd_card_aica *dreamcastcard = from_timer(dreamcastcard,
t, timer);
struct snd_pcm_substream *substream = dreamcastcard->timer_substream;
struct snd_pcm_substream *substream = dreamcastcard->substream;
/*timer function - so cannot sleep */
int play_period;
struct snd_pcm_runtime *runtime;
@@ -335,13 +335,6 @@ static void spu_begin_dma(struct snd_pcm_substream *substream)
dreamcastcard = substream->pcm->private_data;
/*get the queue to do the work */
schedule_work(&(dreamcastcard->spu_dma_work));
/* Timer may already be running */
if (unlikely(dreamcastcard->timer_substream)) {
mod_timer(&dreamcastcard->timer, jiffies + 4);
return;
}
timer_setup(&dreamcastcard->timer, aica_period_elapsed, 0);
dreamcastcard->timer_substream = substream;
mod_timer(&dreamcastcard->timer, jiffies + 4);
}
@@ -379,8 +372,8 @@ static int snd_aicapcm_pcm_close(struct snd_pcm_substream
{
struct snd_card_aica *dreamcastcard = substream->pcm->private_data;
flush_work(&(dreamcastcard->spu_dma_work));
if (dreamcastcard->timer_substream)
del_timer(&dreamcastcard->timer);
del_timer(&dreamcastcard->timer);
dreamcastcard->substream = NULL;
kfree(dreamcastcard->channel);
spu_disable();
return 0;
@@ -613,6 +606,7 @@ static int snd_aica_probe(struct platform_device *devptr)
"Yamaha AICA Super Intelligent Sound Processor for SEGA Dreamcast");
/* Prepare to use the queue */
INIT_WORK(&(dreamcastcard->spu_dma_work), run_spu_dma);
timer_setup(&dreamcastcard->timer, aica_period_elapsed, 0);
/* Load the PCM 'chip' */
err = snd_aicapcmchip(dreamcastcard, 0);
if (unlikely(err < 0))

Ver ficheiro

@@ -63,6 +63,7 @@ source "sound/soc/rockchip/Kconfig"
source "sound/soc/samsung/Kconfig"
source "sound/soc/sh/Kconfig"
source "sound/soc/sirf/Kconfig"
source "sound/soc/sof/Kconfig"
source "sound/soc/spear/Kconfig"
source "sound/soc/sprd/Kconfig"
source "sound/soc/sti/Kconfig"

Ver ficheiro

@@ -47,6 +47,7 @@ obj-$(CONFIG_SND_SOC) += rockchip/
obj-$(CONFIG_SND_SOC) += samsung/
obj-$(CONFIG_SND_SOC) += sh/
obj-$(CONFIG_SND_SOC) += sirf/
obj-$(CONFIG_SND_SOC) += sof/
obj-$(CONFIG_SND_SOC) += spear/
obj-$(CONFIG_SND_SOC) += sprd/
obj-$(CONFIG_SND_SOC) += sti/

Ver ficheiro

@@ -43,6 +43,9 @@ struct axi_i2s {
struct clk *clk;
struct clk *clk_ref;
bool has_capture;
bool has_playback;
struct snd_soc_dai_driver dai_driver;
struct snd_dmaengine_dai_dma_data capture_dma_data;
@@ -136,8 +139,10 @@ static int axi_i2s_dai_probe(struct snd_soc_dai *dai)
{
struct axi_i2s *i2s = snd_soc_dai_get_drvdata(dai);
snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data,
&i2s->capture_dma_data);
snd_soc_dai_init_dma_data(
dai,
i2s->has_playback ? &i2s->playback_dma_data : NULL,
i2s->has_capture ? &i2s->capture_dma_data : NULL);
return 0;
}
@@ -151,18 +156,6 @@ static const struct snd_soc_dai_ops axi_i2s_dai_ops = {
static struct snd_soc_dai_driver axi_i2s_dai = {
.probe = axi_i2s_dai_probe,
.playback = {
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_KNOT,
.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE,
},
.capture = {
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_KNOT,
.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE,
},
.ops = &axi_i2s_dai_ops,
.symmetric_rates = 1,
};
@@ -178,6 +171,19 @@ static const struct regmap_config axi_i2s_regmap_config = {
.max_register = AXI_I2S_REG_STATUS,
};
static void axi_i2s_parse_of(struct axi_i2s *i2s, const struct device_node *np)
{
struct property *dma_names;
const char *dma_name;
of_property_for_each_string(np, "dma-names", dma_names, dma_name) {
if (strcmp(dma_name, "rx") == 0)
i2s->has_capture = true;
if (strcmp(dma_name, "tx") == 0)
i2s->has_playback = true;
}
}
static int axi_i2s_probe(struct platform_device *pdev)
{
struct resource *res;
@@ -191,6 +197,8 @@ static int axi_i2s_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, i2s);
axi_i2s_parse_of(i2s, pdev->dev.of_node);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
@@ -213,13 +221,29 @@ static int axi_i2s_probe(struct platform_device *pdev)
if (ret)
return ret;
i2s->playback_dma_data.addr = res->start + AXI_I2S_REG_TX_FIFO;
i2s->playback_dma_data.addr_width = 4;
i2s->playback_dma_data.maxburst = 1;
if (i2s->has_playback) {
axi_i2s_dai.playback.channels_min = 2;
axi_i2s_dai.playback.channels_max = 2;
axi_i2s_dai.playback.rates = SNDRV_PCM_RATE_KNOT;
axi_i2s_dai.playback.formats =
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE;
i2s->capture_dma_data.addr = res->start + AXI_I2S_REG_RX_FIFO;
i2s->capture_dma_data.addr_width = 4;
i2s->capture_dma_data.maxburst = 1;
i2s->playback_dma_data.addr = res->start + AXI_I2S_REG_TX_FIFO;
i2s->playback_dma_data.addr_width = 4;
i2s->playback_dma_data.maxburst = 1;
}
if (i2s->has_capture) {
axi_i2s_dai.capture.channels_min = 2;
axi_i2s_dai.capture.channels_max = 2;
axi_i2s_dai.capture.rates = SNDRV_PCM_RATE_KNOT;
axi_i2s_dai.capture.formats =
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE;
i2s->capture_dma_data.addr = res->start + AXI_I2S_REG_RX_FIFO;
i2s->capture_dma_data.addr_width = 4;
i2s->capture_dma_data.maxburst = 1;
}
i2s->ratnum.num = clk_get_rate(i2s->clk_ref) / 2 / AXI_I2S_BITS_PER_FRAME;
i2s->ratnum.den_step = 1;
@@ -240,6 +264,10 @@ static int axi_i2s_probe(struct platform_device *pdev)
if (ret)
goto err_clk_disable;
dev_info(&pdev->dev, "probed, capture %s, playback %s\n",
i2s->has_capture ? "enabled" : "disabled",
i2s->has_playback ? "enabled" : "disabled");
return 0;
err_clk_disable:

Ver ficheiro

@@ -46,8 +46,9 @@
#define DUAL_CHANNEL 2
static struct snd_soc_jack cz_jack;
static struct clk *da7219_dai_clk;
extern int bt_uart_enable;
static struct clk *da7219_dai_wclk;
static struct clk *da7219_dai_bclk;
extern bool bt_uart_enable;
static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd)
{
@@ -72,7 +73,8 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd)
return ret;
}
da7219_dai_clk = clk_get(component->dev, "da7219-dai-clks");
da7219_dai_wclk = clk_get(component->dev, "da7219-dai-wclk");
da7219_dai_bclk = clk_get(component->dev, "da7219-dai-bclk");
ret = snd_soc_card_jack_new(card, "Headset Jack",
SND_JACK_HEADSET | SND_JACK_LINEOUT |
@@ -94,12 +96,15 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd)
return 0;
}
static int da7219_clk_enable(struct snd_pcm_substream *substream)
static int da7219_clk_enable(struct snd_pcm_substream *substream,
int wclk_rate, int bclk_rate)
{
int ret = 0;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
ret = clk_prepare_enable(da7219_dai_clk);
clk_set_rate(da7219_dai_wclk, wclk_rate);
clk_set_rate(da7219_dai_bclk, bclk_rate);
ret = clk_prepare_enable(da7219_dai_bclk);
if (ret < 0) {
dev_err(rtd->dev, "can't enable master clock %d\n", ret);
return ret;
@@ -110,7 +115,7 @@ static int da7219_clk_enable(struct snd_pcm_substream *substream)
static void da7219_clk_disable(void)
{
clk_disable_unprepare(da7219_dai_clk);
clk_disable_unprepare(da7219_dai_bclk);
}
static const unsigned int channels[] = {
@@ -151,7 +156,7 @@ static int cz_da7219_play_startup(struct snd_pcm_substream *substream)
&constraints_rates);
machine->play_i2s_instance = I2S_SP_INSTANCE;
return da7219_clk_enable(substream);
return 0;
}
static int cz_da7219_cap_startup(struct snd_pcm_substream *substream)
@@ -173,12 +178,7 @@ static int cz_da7219_cap_startup(struct snd_pcm_substream *substream)
machine->cap_i2s_instance = I2S_SP_INSTANCE;
machine->capture_channel = CAP_CHANNEL1;
return da7219_clk_enable(substream);
}
static void cz_da7219_shutdown(struct snd_pcm_substream *substream)
{
da7219_clk_disable();
return 0;
}
static int cz_max_startup(struct snd_pcm_substream *substream)
@@ -199,12 +199,7 @@ static int cz_max_startup(struct snd_pcm_substream *substream)
&constraints_rates);
machine->play_i2s_instance = I2S_BT_INSTANCE;
return da7219_clk_enable(substream);
}
static void cz_max_shutdown(struct snd_pcm_substream *substream)
{
da7219_clk_disable();
return 0;
}
static int cz_dmic0_startup(struct snd_pcm_substream *substream)
@@ -225,7 +220,7 @@ static int cz_dmic0_startup(struct snd_pcm_substream *substream)
&constraints_rates);
machine->cap_i2s_instance = I2S_BT_INSTANCE;
return da7219_clk_enable(substream);
return 0;
}
static int cz_dmic1_startup(struct snd_pcm_substream *substream)
@@ -247,10 +242,28 @@ static int cz_dmic1_startup(struct snd_pcm_substream *substream)
machine->cap_i2s_instance = I2S_SP_INSTANCE;
machine->capture_channel = CAP_CHANNEL0;
return da7219_clk_enable(substream);
return 0;
}
static void cz_dmic_shutdown(struct snd_pcm_substream *substream)
static int cz_da7219_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
int wclk, bclk;
wclk = params_rate(params);
bclk = wclk * params_channels(params) *
snd_pcm_format_width(params_format(params));
/* ADAU7002 spec: "The ADAU7002 requires a BCLK rate
* that is minimum of 64x the LRCLK sample rate."
* DA7219 is the only clk source so for all codecs
* we have to limit bclk to 64X lrclk.
*/
if (bclk < (wclk * 64))
bclk = wclk * 64;
return da7219_clk_enable(substream, wclk, bclk);
}
static void cz_da7219_shutdown(struct snd_pcm_substream *substream)
{
da7219_clk_disable();
}
@@ -258,26 +271,31 @@ static void cz_dmic_shutdown(struct snd_pcm_substream *substream)
static const struct snd_soc_ops cz_da7219_play_ops = {
.startup = cz_da7219_play_startup,
.shutdown = cz_da7219_shutdown,
.hw_params = cz_da7219_params,
};
static const struct snd_soc_ops cz_da7219_cap_ops = {
.startup = cz_da7219_cap_startup,
.shutdown = cz_da7219_shutdown,
.hw_params = cz_da7219_params,
};
static const struct snd_soc_ops cz_max_play_ops = {
.startup = cz_max_startup,
.shutdown = cz_max_shutdown,
.shutdown = cz_da7219_shutdown,
.hw_params = cz_da7219_params,
};
static const struct snd_soc_ops cz_dmic0_cap_ops = {
.startup = cz_dmic0_startup,
.shutdown = cz_dmic_shutdown,
.shutdown = cz_da7219_shutdown,
.hw_params = cz_da7219_params,
};
static const struct snd_soc_ops cz_dmic1_cap_ops = {
.startup = cz_dmic1_startup,
.shutdown = cz_dmic_shutdown,
.shutdown = cz_da7219_shutdown,
.hw_params = cz_da7219_params,
};
static struct snd_soc_dai_link cz_dai_7219_98357[] = {

Ver ficheiro

@@ -558,7 +558,7 @@ static int acp3x_dai_i2s_trigger(struct snd_pcm_substream *substream,
return ret;
}
struct snd_soc_dai_ops acp3x_dai_i2s_ops = {
static 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,

Ver ficheiro

@@ -109,4 +109,18 @@ config SND_SOC_MIKROE_PROTO
using I2C over SDA (MPU Data Input) and SCL (MPU Clock Input) pins.
Both playback and capture are supported.
config SND_MCHP_SOC_I2S_MCC
tristate "Microchip ASoC driver for boards using I2S MCC"
depends on OF && (ARCH_AT91 || COMPILE_TEST)
select SND_SOC_GENERIC_DMAENGINE_PCM
select REGMAP_MMIO
help
Say Y or M if you want to add support for I2S Multi-Channel ASoC
driver on the following Microchip platforms:
- sam9x60
The I2SMCC complies with the Inter-IC Sound (I2S) bus specification
and supports a Time Division Multiplexed (TDM) interface with
external multi-channel audio codecs.
endif

Ver ficheiro

@@ -4,11 +4,13 @@ snd-soc-atmel-pcm-pdc-objs := atmel-pcm-pdc.o
snd-soc-atmel-pcm-dma-objs := atmel-pcm-dma.o
snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o
snd-soc-atmel-i2s-objs := atmel-i2s.o
snd-soc-mchp-i2s-mcc-objs := mchp-i2s-mcc.o
obj-$(CONFIG_SND_ATMEL_SOC_PDC) += snd-soc-atmel-pcm-pdc.o
obj-$(CONFIG_SND_ATMEL_SOC_DMA) += snd-soc-atmel-pcm-dma.o
obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
obj-$(CONFIG_SND_ATMEL_SOC_I2S) += snd-soc-atmel-i2s.o
obj-$(CONFIG_SND_MCHP_SOC_I2S_MCC) += snd-soc-mchp-i2s-mcc.o
# AT91 Machine Support
snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o

974
sound/soc/atmel/mchp-i2s-mcc.c Ficheiro normal
Ver ficheiro

@@ -0,0 +1,974 @@
// SPDX-License-Identifier: GPL-2.0
//
// Driver for Microchip I2S Multi-channel controller
//
// Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries
//
// Author: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/mfd/syscon.h>
#include <linux/lcm.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <sound/dmaengine_pcm.h>
/*
* ---- I2S Controller Register map ----
*/
#define MCHP_I2SMCC_CR 0x0000 /* Control Register */
#define MCHP_I2SMCC_MRA 0x0004 /* Mode Register A */
#define MCHP_I2SMCC_MRB 0x0008 /* Mode Register B */
#define MCHP_I2SMCC_SR 0x000C /* Status Register */
#define MCHP_I2SMCC_IERA 0x0010 /* Interrupt Enable Register A */
#define MCHP_I2SMCC_IDRA 0x0014 /* Interrupt Disable Register A */
#define MCHP_I2SMCC_IMRA 0x0018 /* Interrupt Mask Register A */
#define MCHP_I2SMCC_ISRA 0X001C /* Interrupt Status Register A */
#define MCHP_I2SMCC_IERB 0x0020 /* Interrupt Enable Register B */
#define MCHP_I2SMCC_IDRB 0x0024 /* Interrupt Disable Register B */
#define MCHP_I2SMCC_IMRB 0x0028 /* Interrupt Mask Register B */
#define MCHP_I2SMCC_ISRB 0X002C /* Interrupt Status Register B */
#define MCHP_I2SMCC_RHR 0x0030 /* Receiver Holding Register */
#define MCHP_I2SMCC_THR 0x0034 /* Transmitter Holding Register */
#define MCHP_I2SMCC_RHL0R 0x0040 /* Receiver Holding Left 0 Register */
#define MCHP_I2SMCC_RHR0R 0x0044 /* Receiver Holding Right 0 Register */
#define MCHP_I2SMCC_RHL1R 0x0048 /* Receiver Holding Left 1 Register */
#define MCHP_I2SMCC_RHR1R 0x004C /* Receiver Holding Right 1 Register */
#define MCHP_I2SMCC_RHL2R 0x0050 /* Receiver Holding Left 2 Register */
#define MCHP_I2SMCC_RHR2R 0x0054 /* Receiver Holding Right 2 Register */
#define MCHP_I2SMCC_RHL3R 0x0058 /* Receiver Holding Left 3 Register */
#define MCHP_I2SMCC_RHR3R 0x005C /* Receiver Holding Right 3 Register */
#define MCHP_I2SMCC_THL0R 0x0060 /* Transmitter Holding Left 0 Register */
#define MCHP_I2SMCC_THR0R 0x0064 /* Transmitter Holding Right 0 Register */
#define MCHP_I2SMCC_THL1R 0x0068 /* Transmitter Holding Left 1 Register */
#define MCHP_I2SMCC_THR1R 0x006C /* Transmitter Holding Right 1 Register */
#define MCHP_I2SMCC_THL2R 0x0070 /* Transmitter Holding Left 2 Register */
#define MCHP_I2SMCC_THR2R 0x0074 /* Transmitter Holding Right 2 Register */
#define MCHP_I2SMCC_THL3R 0x0078 /* Transmitter Holding Left 3 Register */
#define MCHP_I2SMCC_THR3R 0x007C /* Transmitter Holding Right 3 Register */
#define MCHP_I2SMCC_VERSION 0x00FC /* Version Register */
/*
* ---- Control Register (Write-only) ----
*/
#define MCHP_I2SMCC_CR_RXEN BIT(0) /* Receiver Enable */
#define MCHP_I2SMCC_CR_RXDIS BIT(1) /* Receiver Disable */
#define MCHP_I2SMCC_CR_CKEN BIT(2) /* Clock Enable */
#define MCHP_I2SMCC_CR_CKDIS BIT(3) /* Clock Disable */
#define MCHP_I2SMCC_CR_TXEN BIT(4) /* Transmitter Enable */
#define MCHP_I2SMCC_CR_TXDIS BIT(5) /* Transmitter Disable */
#define MCHP_I2SMCC_CR_SWRST BIT(7) /* Software Reset */
/*
* ---- Mode Register A (Read/Write) ----
*/
#define MCHP_I2SMCC_MRA_MODE_MASK GENMASK(0, 0)
#define MCHP_I2SMCC_MRA_MODE_SLAVE (0 << 0)
#define MCHP_I2SMCC_MRA_MODE_MASTER (1 << 0)
#define MCHP_I2SMCC_MRA_DATALENGTH_MASK GENMASK(3, 1)
#define MCHP_I2SMCC_MRA_DATALENGTH_32_BITS (0 << 1)
#define MCHP_I2SMCC_MRA_DATALENGTH_24_BITS (1 << 1)
#define MCHP_I2SMCC_MRA_DATALENGTH_20_BITS (2 << 1)
#define MCHP_I2SMCC_MRA_DATALENGTH_18_BITS (3 << 1)
#define MCHP_I2SMCC_MRA_DATALENGTH_16_BITS (4 << 1)
#define MCHP_I2SMCC_MRA_DATALENGTH_16_BITS_COMPACT (5 << 1)
#define MCHP_I2SMCC_MRA_DATALENGTH_8_BITS (6 << 1)
#define MCHP_I2SMCC_MRA_DATALENGTH_8_BITS_COMPACT (7 << 1)
#define MCHP_I2SMCC_MRA_WIRECFG_MASK GENMASK(5, 4)
#define MCHP_I2SMCC_MRA_WIRECFG_I2S_1_TDM_0 (0 << 4)
#define MCHP_I2SMCC_MRA_WIRECFG_I2S_2_TDM_1 (1 << 4)
#define MCHP_I2SMCC_MRA_WIRECFG_I2S_4_TDM_2 (2 << 4)
#define MCHP_I2SMCC_MRA_WIRECFG_TDM_3 (3 << 4)
#define MCHP_I2SMCC_MRA_FORMAT_MASK GENMASK(7, 6)
#define MCHP_I2SMCC_MRA_FORMAT_I2S (0 << 6)
#define MCHP_I2SMCC_MRA_FORMAT_LJ (1 << 6) /* Left Justified */
#define MCHP_I2SMCC_MRA_FORMAT_TDM (2 << 6)
#define MCHP_I2SMCC_MRA_FORMAT_TDMLJ (3 << 6)
/* Transmitter uses one DMA channel ... */
/* Left audio samples duplicated to right audio channel */
#define MCHP_I2SMCC_MRA_RXMONO BIT(8)
/* I2SDO output of I2SC is internally connected to I2SDI input */
#define MCHP_I2SMCC_MRA_RXLOOP BIT(9)
/* Receiver uses one DMA channel ... */
/* Left audio samples duplicated to right audio channel */
#define MCHP_I2SMCC_MRA_TXMONO BIT(10)
/* x sample transmitted when underrun */
#define MCHP_I2SMCC_MRA_TXSAME_ZERO (0 << 11) /* Zero sample */
#define MCHP_I2SMCC_MRA_TXSAME_PREVIOUS (1 << 11) /* Previous sample */
/* select between peripheral clock and generated clock */
#define MCHP_I2SMCC_MRA_SRCCLK_PCLK (0 << 12)
#define MCHP_I2SMCC_MRA_SRCCLK_GCLK (1 << 12)
/* Number of TDM Channels - 1 */
#define MCHP_I2SMCC_MRA_NBCHAN_MASK GENMASK(15, 13)
#define MCHP_I2SMCC_MRA_NBCHAN(ch) \
((((ch) - 1) << 13) & MCHP_I2SMCC_MRA_NBCHAN_MASK)
/* Selected Clock to I2SMCC Master Clock ratio */
#define MCHP_I2SMCC_MRA_IMCKDIV_MASK GENMASK(21, 16)
#define MCHP_I2SMCC_MRA_IMCKDIV(div) \
(((div) << 16) & MCHP_I2SMCC_MRA_IMCKDIV_MASK)
/* TDM Frame Synchronization */
#define MCHP_I2SMCC_MRA_TDMFS_MASK GENMASK(23, 22)
#define MCHP_I2SMCC_MRA_TDMFS_SLOT (0 << 22)
#define MCHP_I2SMCC_MRA_TDMFS_HALF (1 << 22)
#define MCHP_I2SMCC_MRA_TDMFS_BIT (2 << 22)
/* Selected Clock to I2SMC Serial Clock ratio */
#define MCHP_I2SMCC_MRA_ISCKDIV_MASK GENMASK(29, 24)
#define MCHP_I2SMCC_MRA_ISCKDIV(div) \
(((div) << 24) & MCHP_I2SMCC_MRA_ISCKDIV_MASK)
/* Master Clock mode */
#define MCHP_I2SMCC_MRA_IMCKMODE_MASK GENMASK(30, 30)
/* 0: No master clock generated*/
#define MCHP_I2SMCC_MRA_IMCKMODE_NONE (0 << 30)
/* 1: master clock generated (internally generated clock drives I2SMCK pin) */
#define MCHP_I2SMCC_MRA_IMCKMODE_GEN (1 << 30)
/* Slot Width */
/* 0: slot is 32 bits wide for DATALENGTH = 18/20/24 bits. */
/* 1: slot is 24 bits wide for DATALENGTH = 18/20/24 bits. */
#define MCHP_I2SMCC_MRA_IWS BIT(31)
/*
* ---- Mode Register B (Read/Write) ----
*/
/* all enabled I2S left channels are filled first, then I2S right channels */
#define MCHP_I2SMCC_MRB_CRAMODE_LEFT_FIRST (0 << 0)
/*
* an enabled I2S left channel is filled, then the corresponding right
* channel, until all channels are filled
*/
#define MCHP_I2SMCC_MRB_CRAMODE_REGULAR (1 << 0)
#define MCHP_I2SMCC_MRB_FIFOEN BIT(1)
#define MCHP_I2SMCC_MRB_DMACHUNK_MASK GENMASK(9, 8)
#define MCHP_I2SMCC_MRB_DMACHUNK(no_words) \
(((fls(no_words) - 1) << 8) & MCHP_I2SMCC_MRB_DMACHUNK_MASK)
#define MCHP_I2SMCC_MRB_CLKSEL_MASK GENMASK(16, 16)
#define MCHP_I2SMCC_MRB_CLKSEL_EXT (0 << 16)
#define MCHP_I2SMCC_MRB_CLKSEL_INT (1 << 16)
/*
* ---- Status Registers (Read-only) ----
*/
#define MCHP_I2SMCC_SR_RXEN BIT(0) /* Receiver Enabled */
#define MCHP_I2SMCC_SR_TXEN BIT(4) /* Transmitter Enabled */
/*
* ---- Interrupt Enable/Disable/Mask/Status Registers A ----
*/
#define MCHP_I2SMCC_INT_TXRDY_MASK(ch) GENMASK((ch) - 1, 0)
#define MCHP_I2SMCC_INT_TXRDYCH(ch) BIT(ch)
#define MCHP_I2SMCC_INT_TXUNF_MASK(ch) GENMASK((ch) + 7, 8)
#define MCHP_I2SMCC_INT_TXUNFCH(ch) BIT((ch) + 8)
#define MCHP_I2SMCC_INT_RXRDY_MASK(ch) GENMASK((ch) + 15, 16)
#define MCHP_I2SMCC_INT_RXRDYCH(ch) BIT((ch) + 16)
#define MCHP_I2SMCC_INT_RXOVF_MASK(ch) GENMASK((ch) + 23, 24)
#define MCHP_I2SMCC_INT_RXOVFCH(ch) BIT((ch) + 24)
/*
* ---- Interrupt Enable/Disable/Mask/Status Registers B ----
*/
#define MCHP_I2SMCC_INT_WERR BIT(0)
#define MCHP_I2SMCC_INT_TXFFRDY BIT(8)
#define MCHP_I2SMCC_INT_TXFFEMP BIT(9)
#define MCHP_I2SMCC_INT_RXFFRDY BIT(12)
#define MCHP_I2SMCC_INT_RXFFFUL BIT(13)
/*
* ---- Version Register (Read-only) ----
*/
#define MCHP_I2SMCC_VERSION_MASK GENMASK(11, 0)
#define MCHP_I2SMCC_MAX_CHANNELS 8
#define MCHP_I2MCC_TDM_SLOT_WIDTH 32
static const struct regmap_config mchp_i2s_mcc_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = MCHP_I2SMCC_VERSION,
};
struct mchp_i2s_mcc_dev {
struct wait_queue_head wq_txrdy;
struct wait_queue_head wq_rxrdy;
struct device *dev;
struct regmap *regmap;
struct clk *pclk;
struct clk *gclk;
struct snd_dmaengine_dai_dma_data playback;
struct snd_dmaengine_dai_dma_data capture;
unsigned int fmt;
unsigned int sysclk;
unsigned int frame_length;
int tdm_slots;
int channels;
int gclk_use:1;
int gclk_running:1;
int tx_rdy:1;
int rx_rdy:1;
};
static irqreturn_t mchp_i2s_mcc_interrupt(int irq, void *dev_id)
{
struct mchp_i2s_mcc_dev *dev = dev_id;
u32 sra, imra, srb, imrb, pendinga, pendingb, idra = 0;
irqreturn_t ret = IRQ_NONE;
regmap_read(dev->regmap, MCHP_I2SMCC_IMRA, &imra);
regmap_read(dev->regmap, MCHP_I2SMCC_ISRA, &sra);
pendinga = imra & sra;
regmap_read(dev->regmap, MCHP_I2SMCC_IMRB, &imrb);
regmap_read(dev->regmap, MCHP_I2SMCC_ISRB, &srb);
pendingb = imrb & srb;
if (!pendinga && !pendingb)
return IRQ_NONE;
/*
* Tx/Rx ready interrupts are enabled when stopping only, to assure
* availability and to disable clocks if necessary
*/
idra |= pendinga & (MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels) |
MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels));
if (idra)
ret = IRQ_HANDLED;
if ((imra & MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels)) &&
(imra & MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels)) ==
(idra & MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels))) {
dev->tx_rdy = 1;
wake_up_interruptible(&dev->wq_txrdy);
}
if ((imra & MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels)) &&
(imra & MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels)) ==
(idra & MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels))) {
dev->rx_rdy = 1;
wake_up_interruptible(&dev->wq_rxrdy);
}
regmap_write(dev->regmap, MCHP_I2SMCC_IDRA, idra);
return ret;
}
static int mchp_i2s_mcc_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
dev_dbg(dev->dev, "%s() clk_id=%d freq=%u dir=%d\n",
__func__, clk_id, freq, dir);
/* We do not need SYSCLK */
if (dir == SND_SOC_CLOCK_IN)
return 0;
dev->sysclk = freq;
return 0;
}
static int mchp_i2s_mcc_set_bclk_ratio(struct snd_soc_dai *dai,
unsigned int ratio)
{
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
dev_dbg(dev->dev, "%s() ratio=%u\n", __func__, ratio);
dev->frame_length = ratio;
return 0;
}
static int mchp_i2s_mcc_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
dev_dbg(dev->dev, "%s() fmt=%#x\n", __func__, fmt);
/* We don't support any kind of clock inversion */
if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF)
return -EINVAL;
/* We can't generate only FSYNC */
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFS)
return -EINVAL;
/* We can only reconfigure the IP when it's stopped */
if (fmt & SND_SOC_DAIFMT_CONT)
return -EINVAL;
dev->fmt = fmt;
return 0;
}
static int mchp_i2s_mcc_set_dai_tdm_slot(struct snd_soc_dai *dai,
unsigned int tx_mask,
unsigned int rx_mask,
int slots, int slot_width)
{
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
dev_dbg(dev->dev,
"%s() tx_mask=0x%08x rx_mask=0x%08x slots=%d width=%d\n",
__func__, tx_mask, rx_mask, slots, slot_width);
if (slots < 0 || slots > MCHP_I2SMCC_MAX_CHANNELS ||
slot_width != MCHP_I2MCC_TDM_SLOT_WIDTH)
return -EINVAL;
if (slots) {
/* We do not support daisy chain */
if (rx_mask != GENMASK(slots - 1, 0) ||
rx_mask != tx_mask)
return -EINVAL;
}
dev->tdm_slots = slots;
dev->frame_length = slots * MCHP_I2MCC_TDM_SLOT_WIDTH;
return 0;
}
static int mchp_i2s_mcc_clk_get_rate_diff(struct clk *clk,
unsigned long rate,
struct clk **best_clk,
unsigned long *best_rate,
unsigned long *best_diff_rate)
{
long round_rate;
unsigned int diff_rate;
round_rate = clk_round_rate(clk, rate);
if (round_rate < 0)
return (int)round_rate;
diff_rate = abs(rate - round_rate);
if (diff_rate < *best_diff_rate) {
*best_clk = clk;
*best_diff_rate = diff_rate;
*best_rate = rate;
}
return 0;
}
static int mchp_i2s_mcc_config_divs(struct mchp_i2s_mcc_dev *dev,
unsigned int bclk, unsigned int *mra)
{
unsigned long clk_rate;
unsigned long lcm_rate;
unsigned long best_rate = 0;
unsigned long best_diff_rate = ~0;
unsigned int sysclk;
struct clk *best_clk = NULL;
int ret;
/* For code simplification */
if (!dev->sysclk)
sysclk = bclk;
else
sysclk = dev->sysclk;
/*
* MCLK is Selected CLK / (2 * IMCKDIV),
* BCLK is Selected CLK / (2 * ISCKDIV);
* if IMCKDIV or ISCKDIV are 0, MCLK or BCLK = Selected CLK
*/
lcm_rate = lcm(sysclk, bclk);
if ((lcm_rate / sysclk % 2 == 1 && lcm_rate / sysclk > 2) ||
(lcm_rate / bclk % 2 == 1 && lcm_rate / bclk > 2))
lcm_rate *= 2;
for (clk_rate = lcm_rate;
(clk_rate == sysclk || clk_rate / (sysclk * 2) <= GENMASK(5, 0)) &&
(clk_rate == bclk || clk_rate / (bclk * 2) <= GENMASK(5, 0));
clk_rate += lcm_rate) {
ret = mchp_i2s_mcc_clk_get_rate_diff(dev->gclk, clk_rate,
&best_clk, &best_rate,
&best_diff_rate);
if (ret) {
dev_err(dev->dev, "gclk error for rate %lu: %d",
clk_rate, ret);
} else {
if (!best_diff_rate) {
dev_dbg(dev->dev, "found perfect rate on gclk: %lu\n",
clk_rate);
break;
}
}
ret = mchp_i2s_mcc_clk_get_rate_diff(dev->pclk, clk_rate,
&best_clk, &best_rate,
&best_diff_rate);
if (ret) {
dev_err(dev->dev, "pclk error for rate %lu: %d",
clk_rate, ret);
} else {
if (!best_diff_rate) {
dev_dbg(dev->dev, "found perfect rate on pclk: %lu\n",
clk_rate);
break;
}
}
}
/* check if clocks returned only errors */
if (!best_clk) {
dev_err(dev->dev, "unable to change rate to clocks\n");
return -EINVAL;
}
dev_dbg(dev->dev, "source CLK is %s with rate %lu, diff %lu\n",
best_clk == dev->pclk ? "pclk" : "gclk",
best_rate, best_diff_rate);
/* set the rate */
ret = clk_set_rate(best_clk, best_rate);
if (ret) {
dev_err(dev->dev, "unable to set rate %lu to %s: %d\n",
best_rate, best_clk == dev->pclk ? "PCLK" : "GCLK",
ret);
return ret;
}
/* Configure divisors */
if (dev->sysclk)
*mra |= MCHP_I2SMCC_MRA_IMCKDIV(best_rate / (2 * sysclk));
*mra |= MCHP_I2SMCC_MRA_ISCKDIV(best_rate / (2 * bclk));
if (best_clk == dev->gclk) {
*mra |= MCHP_I2SMCC_MRA_SRCCLK_GCLK;
ret = clk_prepare(dev->gclk);
if (ret < 0)
dev_err(dev->dev, "unable to prepare GCLK: %d\n", ret);
else
dev->gclk_use = 1;
} else {
*mra |= MCHP_I2SMCC_MRA_SRCCLK_PCLK;
dev->gclk_use = 0;
}
return 0;
}
static int mchp_i2s_mcc_is_running(struct mchp_i2s_mcc_dev *dev)
{
u32 sr;
regmap_read(dev->regmap, MCHP_I2SMCC_SR, &sr);
return !!(sr & (MCHP_I2SMCC_SR_TXEN | MCHP_I2SMCC_SR_RXEN));
}
static int mchp_i2s_mcc_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
u32 mra = 0;
u32 mrb = 0;
unsigned int channels = params_channels(params);
unsigned int frame_length = dev->frame_length;
unsigned int bclk_rate;
int set_divs = 0;
int ret;
bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
dev_dbg(dev->dev, "%s() rate=%u format=%#x width=%u channels=%u\n",
__func__, params_rate(params), params_format(params),
params_width(params), params_channels(params));
switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
if (dev->tdm_slots) {
dev_err(dev->dev, "I2S with TDM is not supported\n");
return -EINVAL;
}
mra |= MCHP_I2SMCC_MRA_FORMAT_I2S;
break;
case SND_SOC_DAIFMT_LEFT_J:
if (dev->tdm_slots) {
dev_err(dev->dev, "Left-Justified with TDM is not supported\n");
return -EINVAL;
}
mra |= MCHP_I2SMCC_MRA_FORMAT_LJ;
break;
case SND_SOC_DAIFMT_DSP_A:
mra |= MCHP_I2SMCC_MRA_FORMAT_TDM;
break;
default:
dev_err(dev->dev, "unsupported bus format\n");
return -EINVAL;
}
switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
/* cpu is BCLK and LRC master */
mra |= MCHP_I2SMCC_MRA_MODE_MASTER;
if (dev->sysclk)
mra |= MCHP_I2SMCC_MRA_IMCKMODE_GEN;
set_divs = 1;
break;
case SND_SOC_DAIFMT_CBS_CFM:
/* cpu is BCLK master */
mrb |= MCHP_I2SMCC_MRB_CLKSEL_INT;
set_divs = 1;
/* fall through */
case SND_SOC_DAIFMT_CBM_CFM:
/* cpu is slave */
mra |= MCHP_I2SMCC_MRA_MODE_SLAVE;
if (dev->sysclk)
dev_warn(dev->dev, "Unable to generate MCLK in Slave mode\n");
break;
default:
dev_err(dev->dev, "unsupported master/slave mode\n");
return -EINVAL;
}
if (dev->fmt & (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J)) {
switch (channels) {
case 1:
if (is_playback)
mra |= MCHP_I2SMCC_MRA_TXMONO;
else
mra |= MCHP_I2SMCC_MRA_RXMONO;
break;
case 2:
break;
default:
dev_err(dev->dev, "unsupported number of audio channels\n");
return -EINVAL;
}
if (!frame_length)
frame_length = 2 * params_physical_width(params);
} else if (dev->fmt & SND_SOC_DAIFMT_DSP_A) {
if (dev->tdm_slots) {
if (channels % 2 && channels * 2 <= dev->tdm_slots) {
/*
* Duplicate data for even-numbered channels
* to odd-numbered channels
*/
if (is_playback)
mra |= MCHP_I2SMCC_MRA_TXMONO;
else
mra |= MCHP_I2SMCC_MRA_RXMONO;
}
channels = dev->tdm_slots;
}
mra |= MCHP_I2SMCC_MRA_NBCHAN(channels);
if (!frame_length)
frame_length = channels * MCHP_I2MCC_TDM_SLOT_WIDTH;
}
/*
* We must have the same burst size configured
* in the DMA transfer and in out IP
*/
mrb |= MCHP_I2SMCC_MRB_DMACHUNK(channels);
if (is_playback)
dev->playback.maxburst = 1 << (fls(channels) - 1);
else
dev->capture.maxburst = 1 << (fls(channels) - 1);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
mra |= MCHP_I2SMCC_MRA_DATALENGTH_8_BITS;
break;
case SNDRV_PCM_FORMAT_S16_LE:
mra |= MCHP_I2SMCC_MRA_DATALENGTH_16_BITS;
break;
case SNDRV_PCM_FORMAT_S18_3LE:
mra |= MCHP_I2SMCC_MRA_DATALENGTH_18_BITS |
MCHP_I2SMCC_MRA_IWS;
break;
case SNDRV_PCM_FORMAT_S20_3LE:
mra |= MCHP_I2SMCC_MRA_DATALENGTH_20_BITS |
MCHP_I2SMCC_MRA_IWS;
break;
case SNDRV_PCM_FORMAT_S24_3LE:
mra |= MCHP_I2SMCC_MRA_DATALENGTH_24_BITS |
MCHP_I2SMCC_MRA_IWS;
break;
case SNDRV_PCM_FORMAT_S24_LE:
mra |= MCHP_I2SMCC_MRA_DATALENGTH_24_BITS;
break;
case SNDRV_PCM_FORMAT_S32_LE:
mra |= MCHP_I2SMCC_MRA_DATALENGTH_32_BITS;
break;
default:
dev_err(dev->dev, "unsupported size/endianness for audio samples\n");
return -EINVAL;
}
/*
* If we are already running, the wanted setup must be
* the same with the one that's currently ongoing
*/
if (mchp_i2s_mcc_is_running(dev)) {
u32 mra_cur;
u32 mrb_cur;
regmap_read(dev->regmap, MCHP_I2SMCC_MRA, &mra_cur);
regmap_read(dev->regmap, MCHP_I2SMCC_MRB, &mrb_cur);
if (mra != mra_cur || mrb != mrb_cur)
return -EINVAL;
return 0;
}
/* Save the number of channels to know what interrupts to enable */
dev->channels = channels;
if (set_divs) {
bclk_rate = frame_length * params_rate(params);
ret = mchp_i2s_mcc_config_divs(dev, bclk_rate, &mra);
if (ret) {
dev_err(dev->dev, "unable to configure the divisors: %d\n",
ret);
return ret;
}
}
ret = regmap_write(dev->regmap, MCHP_I2SMCC_MRA, mra);
if (ret < 0)
return ret;
return regmap_write(dev->regmap, MCHP_I2SMCC_MRB, mrb);
}
static int mchp_i2s_mcc_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
long err;
if (is_playback) {
err = wait_event_interruptible_timeout(dev->wq_txrdy,
dev->tx_rdy,
msecs_to_jiffies(500));
} else {
err = wait_event_interruptible_timeout(dev->wq_rxrdy,
dev->rx_rdy,
msecs_to_jiffies(500));
}
if (err == 0) {
u32 idra;
dev_warn_once(dev->dev, "Timeout waiting for %s\n",
is_playback ? "Tx ready" : "Rx ready");
if (is_playback)
idra = MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels);
else
idra = MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels);
regmap_write(dev->regmap, MCHP_I2SMCC_IDRA, idra);
}
if (!mchp_i2s_mcc_is_running(dev)) {
regmap_write(dev->regmap, MCHP_I2SMCC_CR, MCHP_I2SMCC_CR_CKDIS);
if (dev->gclk_running) {
clk_disable_unprepare(dev->gclk);
dev->gclk_running = 0;
}
}
return 0;
}
static int mchp_i2s_mcc_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
u32 cr = 0;
u32 iera = 0;
u32 sr;
int err;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (is_playback)
cr = MCHP_I2SMCC_CR_TXEN | MCHP_I2SMCC_CR_CKEN;
else
cr = MCHP_I2SMCC_CR_RXEN | MCHP_I2SMCC_CR_CKEN;
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
regmap_read(dev->regmap, MCHP_I2SMCC_SR, &sr);
if (is_playback && (sr & MCHP_I2SMCC_SR_TXEN)) {
cr = MCHP_I2SMCC_CR_TXDIS;
dev->tx_rdy = 0;
/*
* Enable Tx Ready interrupts on all channels
* to assure all data is sent
*/
iera = MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels);
} else if (!is_playback && (sr & MCHP_I2SMCC_SR_RXEN)) {
cr = MCHP_I2SMCC_CR_RXDIS;
dev->rx_rdy = 0;
/*
* Enable Rx Ready interrupts on all channels
* to assure all data is received
*/
iera = MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels);
}
break;
default:
return -EINVAL;
}
if ((cr & MCHP_I2SMCC_CR_CKEN) && dev->gclk_use &&
!dev->gclk_running) {
err = clk_enable(dev->gclk);
if (err) {
dev_err_once(dev->dev, "failed to enable GCLK: %d\n",
err);
} else {
dev->gclk_running = 1;
}
}
regmap_write(dev->regmap, MCHP_I2SMCC_IERA, iera);
regmap_write(dev->regmap, MCHP_I2SMCC_CR, cr);
return 0;
}
static int mchp_i2s_mcc_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
/* Software reset the IP if it's not running */
if (!mchp_i2s_mcc_is_running(dev)) {
return regmap_write(dev->regmap, MCHP_I2SMCC_CR,
MCHP_I2SMCC_CR_SWRST);
}
return 0;
}
static const struct snd_soc_dai_ops mchp_i2s_mcc_dai_ops = {
.set_sysclk = mchp_i2s_mcc_set_sysclk,
.set_bclk_ratio = mchp_i2s_mcc_set_bclk_ratio,
.startup = mchp_i2s_mcc_startup,
.trigger = mchp_i2s_mcc_trigger,
.hw_params = mchp_i2s_mcc_hw_params,
.hw_free = mchp_i2s_mcc_hw_free,
.set_fmt = mchp_i2s_mcc_set_dai_fmt,
.set_tdm_slot = mchp_i2s_mcc_set_dai_tdm_slot,
};
static int mchp_i2s_mcc_dai_probe(struct snd_soc_dai *dai)
{
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
init_waitqueue_head(&dev->wq_txrdy);
init_waitqueue_head(&dev->wq_rxrdy);
snd_soc_dai_init_dma_data(dai, &dev->playback, &dev->capture);
return 0;
}
#define MCHP_I2SMCC_RATES SNDRV_PCM_RATE_8000_192000
#define MCHP_I2SMCC_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S18_3LE | \
SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_driver mchp_i2s_mcc_dai = {
.probe = mchp_i2s_mcc_dai_probe,
.playback = {
.stream_name = "I2SMCC-Playback",
.channels_min = 1,
.channels_max = 8,
.rates = MCHP_I2SMCC_RATES,
.formats = MCHP_I2SMCC_FORMATS,
},
.capture = {
.stream_name = "I2SMCC-Capture",
.channels_min = 1,
.channels_max = 8,
.rates = MCHP_I2SMCC_RATES,
.formats = MCHP_I2SMCC_FORMATS,
},
.ops = &mchp_i2s_mcc_dai_ops,
.symmetric_rates = 1,
.symmetric_samplebits = 1,
.symmetric_channels = 1,
};
static const struct snd_soc_component_driver mchp_i2s_mcc_component = {
.name = "mchp-i2s-mcc",
};
#ifdef CONFIG_OF
static const struct of_device_id mchp_i2s_mcc_dt_ids[] = {
{
.compatible = "microchip,sam9x60-i2smcc",
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mchp_i2s_mcc_dt_ids);
#endif
static int mchp_i2s_mcc_probe(struct platform_device *pdev)
{
struct mchp_i2s_mcc_dev *dev;
struct resource *mem;
struct regmap *regmap;
void __iomem *base;
u32 version;
int irq;
int err;
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(base))
return PTR_ERR(base);
regmap = devm_regmap_init_mmio(&pdev->dev, base,
&mchp_i2s_mcc_regmap_config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
err = devm_request_irq(&pdev->dev, irq, mchp_i2s_mcc_interrupt, 0,
dev_name(&pdev->dev), dev);
if (err)
return err;
dev->pclk = devm_clk_get(&pdev->dev, "pclk");
if (IS_ERR(dev->pclk)) {
err = PTR_ERR(dev->pclk);
dev_err(&pdev->dev,
"failed to get the peripheral clock: %d\n", err);
return err;
}
/* Get the optional generated clock */
dev->gclk = devm_clk_get(&pdev->dev, "gclk");
if (IS_ERR(dev->gclk)) {
if (PTR_ERR(dev->gclk) == -EPROBE_DEFER)
return -EPROBE_DEFER;
dev_warn(&pdev->dev,
"generated clock not found: %d\n", err);
dev->gclk = NULL;
}
dev->dev = &pdev->dev;
dev->regmap = regmap;
platform_set_drvdata(pdev, dev);
err = clk_prepare_enable(dev->pclk);
if (err) {
dev_err(&pdev->dev,
"failed to enable the peripheral clock: %d\n", err);
return err;
}
err = devm_snd_soc_register_component(&pdev->dev,
&mchp_i2s_mcc_component,
&mchp_i2s_mcc_dai, 1);
if (err) {
dev_err(&pdev->dev, "failed to register DAI: %d\n", err);
clk_disable_unprepare(dev->pclk);
return err;
}
dev->playback.addr = (dma_addr_t)mem->start + MCHP_I2SMCC_THR;
dev->capture.addr = (dma_addr_t)mem->start + MCHP_I2SMCC_RHR;
err = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
if (err) {
dev_err(&pdev->dev, "failed to register PCM: %d\n", err);
clk_disable_unprepare(dev->pclk);
return err;
}
/* Get IP version. */
regmap_read(dev->regmap, MCHP_I2SMCC_VERSION, &version);
dev_info(&pdev->dev, "hw version: %#lx\n",
version & MCHP_I2SMCC_VERSION_MASK);
return 0;
}
static int mchp_i2s_mcc_remove(struct platform_device *pdev)
{
struct mchp_i2s_mcc_dev *dev = platform_get_drvdata(pdev);
clk_disable_unprepare(dev->pclk);
return 0;
}
static struct platform_driver mchp_i2s_mcc_driver = {
.driver = {
.name = "mchp_i2s_mcc",
.of_match_table = of_match_ptr(mchp_i2s_mcc_dt_ids),
},
.probe = mchp_i2s_mcc_probe,
.remove = mchp_i2s_mcc_remove,
};
module_platform_driver(mchp_i2s_mcc_driver);
MODULE_DESCRIPTION("Microchip I2S Multi-Channel Controller driver");
MODULE_AUTHOR("Codrin Ciubotariu <codrin.ciubotariu@microchip.com>");
MODULE_LICENSE("GPL v2");

Ver ficheiro

@@ -117,8 +117,8 @@ static int tse850_put_mux2(struct snd_kcontrol *kctrl,
return snd_soc_dapm_put_enum_double(kctrl, ucontrol);
}
int tse850_get_mix(struct snd_kcontrol *kctrl,
struct snd_ctl_elem_value *ucontrol)
static int tse850_get_mix(struct snd_kcontrol *kctrl,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
struct snd_soc_card *card = dapm->card;
@@ -129,8 +129,8 @@ int tse850_get_mix(struct snd_kcontrol *kctrl,
return 0;
}
int tse850_put_mix(struct snd_kcontrol *kctrl,
struct snd_ctl_elem_value *ucontrol)
static int tse850_put_mix(struct snd_kcontrol *kctrl,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
struct snd_soc_card *card = dapm->card;
@@ -151,8 +151,8 @@ int tse850_put_mix(struct snd_kcontrol *kctrl,
return 1;
}
int tse850_get_ana(struct snd_kcontrol *kctrl,
struct snd_ctl_elem_value *ucontrol)
static int tse850_get_ana(struct snd_kcontrol *kctrl,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
struct snd_soc_card *card = dapm->card;
@@ -184,8 +184,8 @@ int tse850_get_ana(struct snd_kcontrol *kctrl,
return 0;
}
int tse850_put_ana(struct snd_kcontrol *kctrl,
struct snd_ctl_elem_value *ucontrol)
static int tse850_put_ana(struct snd_kcontrol *kctrl,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
struct snd_soc_card *card = dapm->card;

Ver ficheiro

@@ -94,6 +94,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_JZ4725B_CODEC
select SND_SOC_LM4857 if I2C
select SND_SOC_LM49453 if I2C
select SND_SOC_LOCHNAGAR_SC if MFD_LOCHNAGAR
select SND_SOC_MAX98088 if I2C
select SND_SOC_MAX98090 if I2C
select SND_SOC_MAX98095 if I2C
@@ -179,8 +180,8 @@ config SND_SOC_ALL_CODECS
select SND_SOC_TLV320AIC23_SPI if SPI_MASTER
select SND_SOC_TLV320AIC26 if SPI_MASTER
select SND_SOC_TLV320AIC31XX if I2C
select SND_SOC_TLV320AIC32X4_I2C if I2C
select SND_SOC_TLV320AIC32X4_SPI if SPI_MASTER
select SND_SOC_TLV320AIC32X4_I2C if I2C && COMMON_CLK
select SND_SOC_TLV320AIC32X4_SPI if SPI_MASTER && COMMON_CLK
select SND_SOC_TLV320AIC3X if I2C
select SND_SOC_TPA6130A2 if I2C
select SND_SOC_TLV320DAC33 if I2C
@@ -688,6 +689,13 @@ config SND_SOC_ISABELLE
config SND_SOC_LM49453
tristate
config SND_SOC_LOCHNAGAR_SC
tristate "Lochnagar Sound Card"
depends on MFD_LOCHNAGAR
help
This driver support the sound card functionality of the Cirrus
Logic Lochnagar audio development board.
config SND_SOC_MAX98088
tristate "Maxim MAX98088/9 Low-Power, Stereo Audio Codec"
depends on I2C
@@ -1097,15 +1105,18 @@ config SND_SOC_TLV320AIC31XX
config SND_SOC_TLV320AIC32X4
tristate
depends on COMMON_CLK
config SND_SOC_TLV320AIC32X4_I2C
tristate "Texas Instruments TLV320AIC32x4 audio CODECs - I2C"
depends on I2C
depends on COMMON_CLK
select SND_SOC_TLV320AIC32X4
config SND_SOC_TLV320AIC32X4_SPI
tristate "Texas Instruments TLV320AIC32x4 audio CODECs - SPI"
depends on SPI_MASTER
depends on COMMON_CLK
select SND_SOC_TLV320AIC32X4
config SND_SOC_TLV320AIC3X

Ver ficheiro

@@ -91,6 +91,7 @@ snd-soc-jz4725b-codec-objs := jz4725b.o
snd-soc-l3-objs := l3.o
snd-soc-lm4857-objs := lm4857.o
snd-soc-lm49453-objs := lm49453.o
snd-soc-lochnagar-sc-objs := lochnagar-sc.o
snd-soc-max9759-objs := max9759.o
snd-soc-max9768-objs := max9768.o
snd-soc-max98088-objs := max98088.o
@@ -192,7 +193,7 @@ snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o
snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o
snd-soc-tlv320aic26-objs := tlv320aic26.o
snd-soc-tlv320aic31xx-objs := tlv320aic31xx.o
snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o
snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o tlv320aic32x4-clk.o
snd-soc-tlv320aic32x4-i2c-objs := tlv320aic32x4-i2c.o
snd-soc-tlv320aic32x4-spi-objs := tlv320aic32x4-spi.o
snd-soc-tlv320aic3x-objs := tlv320aic3x.o
@@ -364,6 +365,7 @@ obj-$(CONFIG_SND_SOC_JZ4725B_CODEC) += snd-soc-jz4725b-codec.o
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o
obj-$(CONFIG_SND_SOC_LM49453) += snd-soc-lm49453.o
obj-$(CONFIG_SND_SOC_LOCHNAGAR_SC) += snd-soc-lochnagar-sc.o
obj-$(CONFIG_SND_SOC_MAX9759) += snd-soc-max9759.o
obj-$(CONFIG_SND_SOC_MAX9768) += snd-soc-max9768.o
obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o

Ver ficheiro

@@ -29,18 +29,27 @@ static int cs42l51_i2c_probe(struct i2c_client *i2c,
struct regmap_config config;
config = cs42l51_regmap;
config.val_bits = 8;
config.reg_bits = 8;
return cs42l51_probe(&i2c->dev, devm_regmap_init_i2c(i2c, &config));
}
static int cs42l51_i2c_remove(struct i2c_client *i2c)
{
return cs42l51_remove(&i2c->dev);
}
static const struct dev_pm_ops cs42l51_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(cs42l51_suspend, cs42l51_resume)
};
static struct i2c_driver cs42l51_i2c_driver = {
.driver = {
.name = "cs42l51",
.of_match_table = cs42l51_of_match,
.pm = &cs42l51_pm_ops,
},
.probe = cs42l51_i2c_probe,
.remove = cs42l51_i2c_remove,
.id_table = cs42l51_i2c_id,
};

Ver ficheiro

@@ -30,7 +30,9 @@
#include <sound/initval.h>
#include <sound/pcm_params.h>
#include <sound/pcm.h>
#include <linux/gpio/consumer.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include "cs42l51.h"
@@ -40,11 +42,21 @@ enum master_slave_mode {
MODE_MASTER,
};
static const char * const cs42l51_supply_names[] = {
"VL",
"VD",
"VA",
"VAHP",
};
struct cs42l51_private {
unsigned int mclk;
struct clk *mclk_handle;
unsigned int audio_mode; /* The mode (I2S or left-justified) */
enum master_slave_mode func;
struct regulator_bulk_data supplies[ARRAY_SIZE(cs42l51_supply_names)];
struct gpio_desc *reset_gpio;
struct regmap *regmap;
};
#define CS42L51_FORMATS ( \
@@ -111,6 +123,7 @@ static const DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0);
static const DECLARE_TLV_DB_SCALE(aout_tlv, -10200, 50, 0);
static const DECLARE_TLV_DB_SCALE(boost_tlv, 1600, 1600, 0);
static const DECLARE_TLV_DB_SCALE(adc_boost_tlv, 2000, 2000, 0);
static const char *chan_mix[] = {
"L R",
"L+R",
@@ -139,6 +152,8 @@ static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
SOC_SINGLE("Zero Cross Switch", CS42L51_DAC_CTL, 0, 0, 0),
SOC_DOUBLE_TLV("Mic Boost Volume",
CS42L51_MIC_CTL, 0, 1, 1, 0, boost_tlv),
SOC_DOUBLE_TLV("ADC Boost Volume",
CS42L51_MIC_CTL, 5, 6, 1, 0, adc_boost_tlv),
SOC_SINGLE_TLV("Bass Volume", CS42L51_TONE_CTL, 0, 0xf, 1, tone_tlv),
SOC_SINGLE_TLV("Treble Volume", CS42L51_TONE_CTL, 4, 0xf, 1, tone_tlv),
SOC_ENUM_EXT("PCM channel mixer",
@@ -195,7 +210,8 @@ static const struct snd_kcontrol_new cs42l51_adcr_mux_controls =
SOC_DAPM_ENUM("Route", cs42l51_adcr_mux_enum);
static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = {
SND_SOC_DAPM_MICBIAS("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1),
SND_SOC_DAPM_SUPPLY("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1, NULL,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_PGA_E("Left PGA", CS42L51_POWER_CTL1, 3, 1, NULL, 0,
cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
SND_SOC_DAPM_PGA_E("Right PGA", CS42L51_POWER_CTL1, 4, 1, NULL, 0,
@@ -329,6 +345,19 @@ static struct cs42l51_ratios slave_auto_ratios[] = {
{ 256, CS42L51_DSM_MODE, 1 }, { 384, CS42L51_DSM_MODE, 1 },
};
/*
* Master mode mclk/fs ratios.
* Recommended configurations are SSM for 4-50khz and DSM for 50-100kHz ranges
* The table below provides support of following ratios:
* 128: SSM (%128) with div2 disabled
* 256: SSM (%128) with div2 enabled
* In both cases, if sampling rate is above 50kHz, SSM is overridden
* with DSM (%128) configuration
*/
static struct cs42l51_ratios master_ratios[] = {
{ 128, CS42L51_SSM_MODE, 0 }, { 256, CS42L51_SSM_MODE, 1 },
};
static int cs42l51_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
@@ -351,11 +380,13 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream,
unsigned int ratio;
struct cs42l51_ratios *ratios = NULL;
int nr_ratios = 0;
int intf_ctl, power_ctl, fmt;
int intf_ctl, power_ctl, fmt, mode;
switch (cs42l51->func) {
case MODE_MASTER:
return -EINVAL;
ratios = master_ratios;
nr_ratios = ARRAY_SIZE(master_ratios);
break;
case MODE_SLAVE:
ratios = slave_ratios;
nr_ratios = ARRAY_SIZE(slave_ratios);
@@ -391,7 +422,16 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream,
switch (cs42l51->func) {
case MODE_MASTER:
intf_ctl |= CS42L51_INTF_CTL_MASTER;
power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
mode = ratios[i].speed_mode;
/* Force DSM mode if sampling rate is above 50kHz */
if (rate > 50000)
mode = CS42L51_DSM_MODE;
power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(mode);
/*
* Auto detect mode is not applicable for master mode and has to
* be disabled. Otherwise SPEED[1:0] bits will be ignored.
*/
power_ctl &= ~CS42L51_MIC_POWER_CTL_AUTO;
break;
case MODE_SLAVE:
power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
@@ -464,6 +504,13 @@ static int cs42l51_dai_mute(struct snd_soc_dai *dai, int mute)
return snd_soc_component_write(component, CS42L51_DAC_OUT_CTL, reg);
}
static int cs42l51_of_xlate_dai_id(struct snd_soc_component *component,
struct device_node *endpoint)
{
/* return dai id 0, whatever the endpoint index */
return 0;
}
static const struct snd_soc_dai_ops cs42l51_dai_ops = {
.hw_params = cs42l51_hw_params,
.set_sysclk = cs42l51_set_dai_sysclk,
@@ -526,13 +573,113 @@ static const struct snd_soc_component_driver soc_component_device_cs42l51 = {
.num_dapm_widgets = ARRAY_SIZE(cs42l51_dapm_widgets),
.dapm_routes = cs42l51_routes,
.num_dapm_routes = ARRAY_SIZE(cs42l51_routes),
.of_xlate_dai_id = cs42l51_of_xlate_dai_id,
.idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
.non_legacy_dai_naming = 1,
};
static bool cs42l51_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case CS42L51_POWER_CTL1:
case CS42L51_MIC_POWER_CTL:
case CS42L51_INTF_CTL:
case CS42L51_MIC_CTL:
case CS42L51_ADC_CTL:
case CS42L51_ADC_INPUT:
case CS42L51_DAC_OUT_CTL:
case CS42L51_DAC_CTL:
case CS42L51_ALC_PGA_CTL:
case CS42L51_ALC_PGB_CTL:
case CS42L51_ADCA_ATT:
case CS42L51_ADCB_ATT:
case CS42L51_ADCA_VOL:
case CS42L51_ADCB_VOL:
case CS42L51_PCMA_VOL:
case CS42L51_PCMB_VOL:
case CS42L51_BEEP_FREQ:
case CS42L51_BEEP_VOL:
case CS42L51_BEEP_CONF:
case CS42L51_TONE_CTL:
case CS42L51_AOUTA_VOL:
case CS42L51_AOUTB_VOL:
case CS42L51_PCM_MIXER:
case CS42L51_LIMIT_THRES_DIS:
case CS42L51_LIMIT_REL:
case CS42L51_LIMIT_ATT:
case CS42L51_ALC_EN:
case CS42L51_ALC_REL:
case CS42L51_ALC_THRES:
case CS42L51_NOISE_CONF:
case CS42L51_CHARGE_FREQ:
return true;
default:
return false;
}
}
static bool cs42l51_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case CS42L51_STATUS:
return true;
default:
return false;
}
}
static bool cs42l51_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case CS42L51_CHIP_REV_ID:
case CS42L51_POWER_CTL1:
case CS42L51_MIC_POWER_CTL:
case CS42L51_INTF_CTL:
case CS42L51_MIC_CTL:
case CS42L51_ADC_CTL:
case CS42L51_ADC_INPUT:
case CS42L51_DAC_OUT_CTL:
case CS42L51_DAC_CTL:
case CS42L51_ALC_PGA_CTL:
case CS42L51_ALC_PGB_CTL:
case CS42L51_ADCA_ATT:
case CS42L51_ADCB_ATT:
case CS42L51_ADCA_VOL:
case CS42L51_ADCB_VOL:
case CS42L51_PCMA_VOL:
case CS42L51_PCMB_VOL:
case CS42L51_BEEP_FREQ:
case CS42L51_BEEP_VOL:
case CS42L51_BEEP_CONF:
case CS42L51_TONE_CTL:
case CS42L51_AOUTA_VOL:
case CS42L51_AOUTB_VOL:
case CS42L51_PCM_MIXER:
case CS42L51_LIMIT_THRES_DIS:
case CS42L51_LIMIT_REL:
case CS42L51_LIMIT_ATT:
case CS42L51_ALC_EN:
case CS42L51_ALC_REL:
case CS42L51_ALC_THRES:
case CS42L51_NOISE_CONF:
case CS42L51_STATUS:
case CS42L51_CHARGE_FREQ:
return true;
default:
return false;
}
}
const struct regmap_config cs42l51_regmap = {
.reg_bits = 8,
.reg_stride = 1,
.val_bits = 8,
.use_single_write = true,
.readable_reg = cs42l51_readable_reg,
.volatile_reg = cs42l51_volatile_reg,
.writeable_reg = cs42l51_writeable_reg,
.max_register = CS42L51_CHARGE_FREQ,
.cache_type = REGCACHE_RBTREE,
};
@@ -542,7 +689,7 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap)
{
struct cs42l51_private *cs42l51;
unsigned int val;
int ret;
int ret, i;
if (IS_ERR(regmap))
return PTR_ERR(regmap);
@@ -553,6 +700,7 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap)
return -ENOMEM;
dev_set_drvdata(dev, cs42l51);
cs42l51->regmap = regmap;
cs42l51->mclk_handle = devm_clk_get(dev, "MCLK");
if (IS_ERR(cs42l51->mclk_handle)) {
@@ -561,6 +709,34 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap)
cs42l51->mclk_handle = NULL;
}
for (i = 0; i < ARRAY_SIZE(cs42l51->supplies); i++)
cs42l51->supplies[i].supply = cs42l51_supply_names[i];
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs42l51->supplies),
cs42l51->supplies);
if (ret != 0) {
dev_err(dev, "Failed to request supplies: %d\n", ret);
return ret;
}
ret = regulator_bulk_enable(ARRAY_SIZE(cs42l51->supplies),
cs42l51->supplies);
if (ret != 0) {
dev_err(dev, "Failed to enable supplies: %d\n", ret);
return ret;
}
cs42l51->reset_gpio = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_LOW);
if (IS_ERR(cs42l51->reset_gpio))
return PTR_ERR(cs42l51->reset_gpio);
if (cs42l51->reset_gpio) {
dev_dbg(dev, "Release reset gpio\n");
gpiod_set_value_cansleep(cs42l51->reset_gpio, 0);
mdelay(2);
}
/* Verify that we have a CS42L51 */
ret = regmap_read(regmap, CS42L51_CHIP_REV_ID, &val);
if (ret < 0) {
@@ -579,11 +755,50 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap)
ret = devm_snd_soc_register_component(dev,
&soc_component_device_cs42l51, &cs42l51_dai, 1);
if (ret < 0)
goto error;
return 0;
error:
regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies),
cs42l51->supplies);
return ret;
}
EXPORT_SYMBOL_GPL(cs42l51_probe);
int cs42l51_remove(struct device *dev)
{
struct cs42l51_private *cs42l51 = dev_get_drvdata(dev);
gpiod_set_value_cansleep(cs42l51->reset_gpio, 1);
return regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies),
cs42l51->supplies);
}
EXPORT_SYMBOL_GPL(cs42l51_remove);
int __maybe_unused cs42l51_suspend(struct device *dev)
{
struct cs42l51_private *cs42l51 = dev_get_drvdata(dev);
regcache_cache_only(cs42l51->regmap, true);
regcache_mark_dirty(cs42l51->regmap);
return 0;
}
EXPORT_SYMBOL_GPL(cs42l51_suspend);
int __maybe_unused cs42l51_resume(struct device *dev)
{
struct cs42l51_private *cs42l51 = dev_get_drvdata(dev);
regcache_cache_only(cs42l51->regmap, false);
return regcache_sync(cs42l51->regmap);
}
EXPORT_SYMBOL_GPL(cs42l51_resume);
const struct of_device_id cs42l51_of_match[] = {
{ .compatible = "cirrus,cs42l51", },
{ }

Ver ficheiro

@@ -22,6 +22,9 @@ struct device;
extern const struct regmap_config cs42l51_regmap;
int cs42l51_probe(struct device *dev, struct regmap *regmap);
int cs42l51_remove(struct device *dev);
int __maybe_unused cs42l51_suspend(struct device *dev);
int __maybe_unused cs42l51_resume(struct device *dev);
extern const struct of_device_id cs42l51_of_match[];
#define CS42L51_CHIP_ID 0x1B

Ver ficheiro

@@ -2322,6 +2322,8 @@ static int cs43130_probe(struct snd_soc_component *component)
return ret;
cs43130->wq = create_singlethread_workqueue("cs43130_hp");
if (!cs43130->wq)
return -ENOMEM;
INIT_WORK(&cs43130->work, cs43130_imp_meas);
}

Ver ficheiro

@@ -75,7 +75,9 @@ static int cs47l24_adsp_power_ev(struct snd_soc_dapm_widget *w,
v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT;
return wm_adsp2_early_event(w, kcontrol, event, v);
wm_adsp2_set_dspclk(w, v);
return wm_adsp_early_event(w, kcontrol, event);
}
static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);

Ver ficheiro

@@ -1305,7 +1305,10 @@ static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
/* By default only 64 BCLK per WCLK is supported */
dai_clk_mode |= DA7213_DAI_BCLKS_PER_WCLK_64;
snd_soc_component_write(component, DA7213_DAI_CLK_MODE, dai_clk_mode);
snd_soc_component_update_bits(component, DA7213_DAI_CLK_MODE,
DA7213_DAI_BCLKS_PER_WCLK_MASK |
DA7213_DAI_CLK_POL_MASK | DA7213_DAI_WCLK_POL_MASK,
dai_clk_mode);
snd_soc_component_update_bits(component, DA7213_DAI_CTRL, DA7213_DAI_FORMAT_MASK,
dai_ctrl);
snd_soc_component_write(component, DA7213_DAI_OFFSET, dai_offset);

Ver ficheiro

@@ -181,7 +181,9 @@
#define DA7213_DAI_BCLKS_PER_WCLK_256 (0x3 << 0)
#define DA7213_DAI_BCLKS_PER_WCLK_MASK (0x3 << 0)
#define DA7213_DAI_CLK_POL_INV (0x1 << 2)
#define DA7213_DAI_CLK_POL_MASK (0x1 << 2)
#define DA7213_DAI_WCLK_POL_INV (0x1 << 3)
#define DA7213_DAI_WCLK_POL_MASK (0x1 << 3)
#define DA7213_DAI_CLK_EN_MASK (0x1 << 7)
/* DA7213_DAI_CTRL = 0x29 */

Ver ficheiro

@@ -797,6 +797,7 @@ static int da7219_dai_event(struct snd_soc_dapm_widget *w,
{
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
struct clk *bclk = da7219->dai_clks[DA7219_DAI_BCLK_IDX];
u8 pll_ctrl, pll_status;
int i = 0, ret;
bool srm_lock = false;
@@ -805,11 +806,11 @@ static int da7219_dai_event(struct snd_soc_dapm_widget *w,
case SND_SOC_DAPM_PRE_PMU:
if (da7219->master) {
/* Enable DAI clks for master mode */
if (da7219->dai_clks) {
ret = clk_prepare_enable(da7219->dai_clks);
if (bclk) {
ret = clk_prepare_enable(bclk);
if (ret) {
dev_err(component->dev,
"Failed to enable dai_clks\n");
"Failed to enable DAI clks\n");
return ret;
}
} else {
@@ -852,8 +853,8 @@ static int da7219_dai_event(struct snd_soc_dapm_widget *w,
/* Disable DAI clks if in master mode */
if (da7219->master) {
if (da7219->dai_clks)
clk_disable_unprepare(da7219->dai_clks);
if (bclk)
clk_disable_unprepare(bclk);
else
snd_soc_component_update_bits(component,
DA7219_DAI_CLK_MODE,
@@ -1385,17 +1386,50 @@ static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return 0;
}
static int da7219_set_bclks_per_wclk(struct snd_soc_component *component,
unsigned long factor)
{
u8 bclks_per_wclk;
switch (factor) {
case 32:
bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32;
break;
case 64:
bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64;
break;
case 128:
bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128;
break;
case 256:
bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256;
break;
default:
return -EINVAL;
}
snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
DA7219_DAI_BCLKS_PER_WCLK_MASK,
bclks_per_wclk);
return 0;
}
static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask,
int slots, int slot_width)
{
struct snd_soc_component *component = dai->component;
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
struct clk *wclk = da7219->dai_clks[DA7219_DAI_WCLK_IDX];
struct clk *bclk = da7219->dai_clks[DA7219_DAI_BCLK_IDX];
unsigned int ch_mask;
u8 dai_bclks_per_wclk, slot_offset;
unsigned long sr, bclk_rate;
u8 slot_offset;
u16 offset;
__le16 dai_offset;
u32 frame_size;
int ret;
/* No channels enabled so disable TDM */
if (!tx_mask) {
@@ -1432,28 +1466,26 @@ static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai,
*/
if (da7219->master) {
frame_size = slots * slot_width;
switch (frame_size) {
case 32:
dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32;
break;
case 64:
dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64;
break;
case 128:
dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128;
break;
case 256:
dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256;
break;
default:
dev_err(component->dev, "Invalid frame size %d\n",
frame_size);
return -EINVAL;
}
snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
DA7219_DAI_BCLKS_PER_WCLK_MASK,
dai_bclks_per_wclk);
if (bclk) {
sr = clk_get_rate(wclk);
bclk_rate = sr * frame_size;
ret = clk_set_rate(bclk, bclk_rate);
if (ret) {
dev_err(component->dev,
"Failed to set TDM BCLK rate %lu: %d\n",
bclk_rate, ret);
return ret;
}
} else {
ret = da7219_set_bclks_per_wclk(component, frame_size);
if (ret) {
dev_err(component->dev,
"Failed to set TDM BCLKs per WCLK %d: %d\n",
frame_size, ret);
return ret;
}
}
}
dai_offset = cpu_to_le16(offset);
@@ -1471,44 +1503,12 @@ static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai,
return 0;
}
static int da7219_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
static int da7219_set_sr(struct snd_soc_component *component,
unsigned long rate)
{
struct snd_soc_component *component = dai->component;
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
u8 dai_ctrl = 0, dai_bclks_per_wclk = 0, fs;
unsigned int channels;
int word_len = params_width(params);
int frame_size;
u8 fs;
switch (word_len) {
case 16:
dai_ctrl |= DA7219_DAI_WORD_LENGTH_S16_LE;
break;
case 20:
dai_ctrl |= DA7219_DAI_WORD_LENGTH_S20_LE;
break;
case 24:
dai_ctrl |= DA7219_DAI_WORD_LENGTH_S24_LE;
break;
case 32:
dai_ctrl |= DA7219_DAI_WORD_LENGTH_S32_LE;
break;
default:
return -EINVAL;
}
channels = params_channels(params);
if ((channels < 1) || (channels > DA7219_DAI_CH_NUM_MAX)) {
dev_err(component->dev,
"Invalid number of channels, only 1 to %d supported\n",
DA7219_DAI_CH_NUM_MAX);
return -EINVAL;
}
dai_ctrl |= channels << DA7219_DAI_CH_NUM_SHIFT;
switch (params_rate(params)) {
switch (rate) {
case 8000:
fs = DA7219_SR_8000;
break;
@@ -1546,28 +1546,118 @@ static int da7219_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
snd_soc_component_write(component, DA7219_SR, fs);
return 0;
}
static int da7219_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
struct clk *wclk = da7219->dai_clks[DA7219_DAI_WCLK_IDX];
struct clk *bclk = da7219->dai_clks[DA7219_DAI_BCLK_IDX];
u8 dai_ctrl = 0;
unsigned int channels;
unsigned long sr, bclk_rate;
int word_len = params_width(params);
int frame_size, ret;
switch (word_len) {
case 16:
dai_ctrl |= DA7219_DAI_WORD_LENGTH_S16_LE;
break;
case 20:
dai_ctrl |= DA7219_DAI_WORD_LENGTH_S20_LE;
break;
case 24:
dai_ctrl |= DA7219_DAI_WORD_LENGTH_S24_LE;
break;
case 32:
dai_ctrl |= DA7219_DAI_WORD_LENGTH_S32_LE;
break;
default:
return -EINVAL;
}
channels = params_channels(params);
if ((channels < 1) || (channels > DA7219_DAI_CH_NUM_MAX)) {
dev_err(component->dev,
"Invalid number of channels, only 1 to %d supported\n",
DA7219_DAI_CH_NUM_MAX);
return -EINVAL;
}
dai_ctrl |= channels << DA7219_DAI_CH_NUM_SHIFT;
sr = params_rate(params);
if (da7219->master && wclk) {
ret = clk_set_rate(wclk, sr);
if (ret) {
dev_err(component->dev,
"Failed to set WCLK SR %lu: %d\n", sr, ret);
return ret;
}
} else {
ret = da7219_set_sr(component, sr);
if (ret) {
dev_err(component->dev,
"Failed to set SR %lu: %d\n", sr, ret);
return ret;
}
}
/*
* If we're master, then we have a limited set of BCLK rates we
* support. For slave mode this isn't the case and the codec can detect
* the BCLK rate automatically.
*/
if (da7219->master && !da7219->tdm_en) {
frame_size = word_len * 2;
if (frame_size <= 32)
dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32;
if ((word_len * DA7219_DAI_CH_NUM_MAX) <= 32)
frame_size = 32;
else
dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64;
frame_size = 64;
snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
DA7219_DAI_BCLKS_PER_WCLK_MASK,
dai_bclks_per_wclk);
if (bclk) {
bclk_rate = frame_size * sr;
/*
* Rounding the rate here avoids failure trying to set a
* new rate on an already enabled bclk. In that
* instance this will just set the same rate as is
* currently in use, and so should continue without
* problem, as long as the BCLK rate is suitable for the
* desired frame size.
*/
bclk_rate = clk_round_rate(bclk, bclk_rate);
if ((bclk_rate / sr) < frame_size) {
dev_err(component->dev,
"BCLK rate mismatch against frame size");
return -EINVAL;
}
ret = clk_set_rate(bclk, bclk_rate);
if (ret) {
dev_err(component->dev,
"Failed to set BCLK rate %lu: %d\n",
bclk_rate, ret);
return ret;
}
} else {
ret = da7219_set_bclks_per_wclk(component, frame_size);
if (ret) {
dev_err(component->dev,
"Failed to set BCLKs per WCLK %d: %d\n",
frame_size, ret);
return ret;
}
}
}
snd_soc_component_update_bits(component, DA7219_DAI_CTRL,
DA7219_DAI_WORD_LENGTH_MASK |
DA7219_DAI_CH_NUM_MASK,
dai_ctrl);
snd_soc_component_write(component, DA7219_SR, fs);
return 0;
}
@@ -1583,20 +1673,26 @@ static const struct snd_soc_dai_ops da7219_dai_ops = {
#define DA7219_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
#define DA7219_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
SNDRV_PCM_RATE_96000)
static struct snd_soc_dai_driver da7219_dai = {
.name = "da7219-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = DA7219_DAI_CH_NUM_MAX,
.rates = SNDRV_PCM_RATE_8000_96000,
.rates = DA7219_RATES,
.formats = DA7219_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = DA7219_DAI_CH_NUM_MAX,
.rates = SNDRV_PCM_RATE_8000_96000,
.rates = DA7219_RATES,
.formats = DA7219_FORMATS,
},
.ops = &da7219_dai_ops,
@@ -1672,11 +1768,14 @@ static struct da7219_pdata *da7219_fw_to_pdata(struct snd_soc_component *compone
pdata->wakeup_source = device_property_read_bool(dev, "wakeup-source");
pdata->dai_clks_name = "da7219-dai-clks";
if (device_property_read_string(dev, "clock-output-names",
&pdata->dai_clks_name))
dev_warn(dev, "Using default clk name: %s\n",
pdata->dai_clks_name);
pdata->dai_clk_names[DA7219_DAI_WCLK_IDX] = "da7219-dai-wclk";
pdata->dai_clk_names[DA7219_DAI_BCLK_IDX] = "da7219-dai-bclk";
if (device_property_read_string_array(dev, "clock-output-names",
pdata->dai_clk_names,
DA7219_DAI_NUM_CLKS) < 0)
dev_warn(dev, "Using default DAI clk names: %s, %s\n",
pdata->dai_clk_names[DA7219_DAI_WCLK_IDX],
pdata->dai_clk_names[DA7219_DAI_BCLK_IDX]);
if (device_property_read_u32(dev, "dlg,micbias-lvl", &of_val32) >= 0)
pdata->micbias_lvl = da7219_fw_micbias_lvl(dev, of_val32);
@@ -1793,12 +1892,16 @@ static int da7219_handle_supplies(struct snd_soc_component *component)
}
#ifdef CONFIG_COMMON_CLK
static int da7219_dai_clks_prepare(struct clk_hw *hw)
static int da7219_wclk_prepare(struct clk_hw *hw)
{
struct da7219_priv *da7219 =
container_of(hw, struct da7219_priv, dai_clks_hw);
container_of(hw, struct da7219_priv,
dai_clks_hw[DA7219_DAI_WCLK_IDX]);
struct snd_soc_component *component = da7219->component;
if (!da7219->master)
return -EINVAL;
snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
DA7219_DAI_CLK_EN_MASK,
DA7219_DAI_CLK_EN_MASK);
@@ -1806,33 +1909,42 @@ static int da7219_dai_clks_prepare(struct clk_hw *hw)
return 0;
}
static void da7219_dai_clks_unprepare(struct clk_hw *hw)
static void da7219_wclk_unprepare(struct clk_hw *hw)
{
struct da7219_priv *da7219 =
container_of(hw, struct da7219_priv, dai_clks_hw);
container_of(hw, struct da7219_priv,
dai_clks_hw[DA7219_DAI_WCLK_IDX]);
struct snd_soc_component *component = da7219->component;
if (!da7219->master)
return;
snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
DA7219_DAI_CLK_EN_MASK, 0);
}
static int da7219_dai_clks_is_prepared(struct clk_hw *hw)
static int da7219_wclk_is_prepared(struct clk_hw *hw)
{
struct da7219_priv *da7219 =
container_of(hw, struct da7219_priv, dai_clks_hw);
container_of(hw, struct da7219_priv,
dai_clks_hw[DA7219_DAI_WCLK_IDX]);
struct snd_soc_component *component = da7219->component;
u8 clk_reg;
if (!da7219->master)
return -EINVAL;
clk_reg = snd_soc_component_read32(component, DA7219_DAI_CLK_MODE);
return !!(clk_reg & DA7219_DAI_CLK_EN_MASK);
}
static unsigned long da7219_dai_clks_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
static unsigned long da7219_wclk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct da7219_priv *da7219 =
container_of(hw, struct da7219_priv, dai_clks_hw);
container_of(hw, struct da7219_priv,
dai_clks_hw[DA7219_DAI_WCLK_IDX]);
struct snd_soc_component *component = da7219->component;
u8 fs = snd_soc_component_read32(component, DA7219_SR);
@@ -1864,11 +1976,148 @@ static unsigned long da7219_dai_clks_recalc_rate(struct clk_hw *hw,
}
}
static const struct clk_ops da7219_dai_clks_ops = {
.prepare = da7219_dai_clks_prepare,
.unprepare = da7219_dai_clks_unprepare,
.is_prepared = da7219_dai_clks_is_prepared,
.recalc_rate = da7219_dai_clks_recalc_rate,
static long da7219_wclk_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct da7219_priv *da7219 =
container_of(hw, struct da7219_priv,
dai_clks_hw[DA7219_DAI_WCLK_IDX]);
if (!da7219->master)
return -EINVAL;
if (rate < 11025)
return 8000;
else if (rate < 12000)
return 11025;
else if (rate < 16000)
return 12000;
else if (rate < 22050)
return 16000;
else if (rate < 24000)
return 22050;
else if (rate < 32000)
return 24000;
else if (rate < 44100)
return 32000;
else if (rate < 48000)
return 44100;
else if (rate < 88200)
return 48000;
else if (rate < 96000)
return 88200;
else
return 96000;
}
static int da7219_wclk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct da7219_priv *da7219 =
container_of(hw, struct da7219_priv,
dai_clks_hw[DA7219_DAI_WCLK_IDX]);
struct snd_soc_component *component = da7219->component;
if (!da7219->master)
return -EINVAL;
return da7219_set_sr(component, rate);
}
static unsigned long da7219_bclk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct da7219_priv *da7219 =
container_of(hw, struct da7219_priv,
dai_clks_hw[DA7219_DAI_BCLK_IDX]);
struct snd_soc_component *component = da7219->component;
u8 bclks_per_wclk = snd_soc_component_read32(component,
DA7219_DAI_CLK_MODE);
switch (bclks_per_wclk & DA7219_DAI_BCLKS_PER_WCLK_MASK) {
case DA7219_DAI_BCLKS_PER_WCLK_32:
return parent_rate * 32;
case DA7219_DAI_BCLKS_PER_WCLK_64:
return parent_rate * 64;
case DA7219_DAI_BCLKS_PER_WCLK_128:
return parent_rate * 128;
case DA7219_DAI_BCLKS_PER_WCLK_256:
return parent_rate * 256;
default:
return 0;
}
}
static unsigned long da7219_bclk_get_factor(unsigned long rate,
unsigned long parent_rate)
{
unsigned long factor;
factor = rate / parent_rate;
if (factor < 64)
return 32;
else if (factor < 128)
return 64;
else if (factor < 256)
return 128;
else
return 256;
}
static long da7219_bclk_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct da7219_priv *da7219 =
container_of(hw, struct da7219_priv,
dai_clks_hw[DA7219_DAI_BCLK_IDX]);
unsigned long factor;
if (!*parent_rate || !da7219->master)
return -EINVAL;
/*
* We don't allow changing the parent rate as some BCLK rates can be
* derived from multiple parent WCLK rates (BCLK rates are set as a
* multiplier of WCLK in HW). We just do some rounding down based on the
* parent WCLK rate set and find the appropriate multiplier of BCLK to
* get the rounded down BCLK value.
*/
factor = da7219_bclk_get_factor(rate, *parent_rate);
return *parent_rate * factor;
}
static int da7219_bclk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct da7219_priv *da7219 =
container_of(hw, struct da7219_priv,
dai_clks_hw[DA7219_DAI_BCLK_IDX]);
struct snd_soc_component *component = da7219->component;
unsigned long factor;
if (!da7219->master)
return -EINVAL;
factor = da7219_bclk_get_factor(rate, parent_rate);
return da7219_set_bclks_per_wclk(component, factor);
}
static const struct clk_ops da7219_dai_clk_ops[DA7219_DAI_NUM_CLKS] = {
[DA7219_DAI_WCLK_IDX] = {
.prepare = da7219_wclk_prepare,
.unprepare = da7219_wclk_unprepare,
.is_prepared = da7219_wclk_is_prepared,
.recalc_rate = da7219_wclk_recalc_rate,
.round_rate = da7219_wclk_round_rate,
.set_rate = da7219_wclk_set_rate,
},
[DA7219_DAI_BCLK_IDX] = {
.recalc_rate = da7219_bclk_recalc_rate,
.round_rate = da7219_bclk_round_rate,
.set_rate = da7219_bclk_set_rate,
},
};
static int da7219_register_dai_clks(struct snd_soc_component *component)
@@ -1876,47 +2125,81 @@ static int da7219_register_dai_clks(struct snd_soc_component *component)
struct device *dev = component->dev;
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
struct da7219_pdata *pdata = da7219->pdata;
struct clk_init_data init = {};
struct clk *dai_clks;
struct clk_lookup *dai_clks_lookup;
const char *parent_name;
int i, ret;
if (da7219->mclk) {
parent_name = __clk_get_name(da7219->mclk);
init.parent_names = &parent_name;
init.num_parents = 1;
} else {
init.parent_names = NULL;
init.num_parents = 0;
}
for (i = 0; i < DA7219_DAI_NUM_CLKS; ++i) {
struct clk_init_data init = {};
struct clk *dai_clk;
struct clk_lookup *dai_clk_lookup;
struct clk_hw *dai_clk_hw = &da7219->dai_clks_hw[i];
init.name = pdata->dai_clks_name;
init.ops = &da7219_dai_clks_ops;
init.flags = CLK_GET_RATE_NOCACHE;
da7219->dai_clks_hw.init = &init;
switch (i) {
case DA7219_DAI_WCLK_IDX:
/*
* If we can, make MCLK the parent of WCLK to ensure
* it's enabled as required.
*/
if (da7219->mclk) {
parent_name = __clk_get_name(da7219->mclk);
init.parent_names = &parent_name;
init.num_parents = 1;
} else {
init.parent_names = NULL;
init.num_parents = 0;
}
break;
case DA7219_DAI_BCLK_IDX:
/* Make WCLK the parent of BCLK */
parent_name = __clk_get_name(da7219->dai_clks[DA7219_DAI_WCLK_IDX]);
init.parent_names = &parent_name;
init.num_parents = 1;
break;
default:
dev_err(dev, "Invalid clock index\n");
ret = -EINVAL;
goto err;
}
dai_clks = devm_clk_register(dev, &da7219->dai_clks_hw);
if (IS_ERR(dai_clks)) {
dev_warn(dev, "Failed to register DAI clocks: %ld\n",
PTR_ERR(dai_clks));
return PTR_ERR(dai_clks);
}
da7219->dai_clks = dai_clks;
init.name = pdata->dai_clk_names[i];
init.ops = &da7219_dai_clk_ops[i];
init.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE;
dai_clk_hw->init = &init;
/* If we're using DT, then register as provider accordingly */
if (dev->of_node) {
devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
&da7219->dai_clks_hw);
} else {
dai_clks_lookup = clkdev_create(dai_clks, pdata->dai_clks_name,
"%s", dev_name(dev));
if (!dai_clks_lookup)
return -ENOMEM;
else
da7219->dai_clks_lookup = dai_clks_lookup;
dai_clk = devm_clk_register(dev, dai_clk_hw);
if (IS_ERR(dai_clk)) {
dev_warn(dev, "Failed to register %s: %ld\n",
init.name, PTR_ERR(dai_clk));
ret = PTR_ERR(dai_clk);
goto err;
}
da7219->dai_clks[i] = dai_clk;
/* If we're using DT, then register as provider accordingly */
if (dev->of_node) {
devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
dai_clk_hw);
} else {
dai_clk_lookup = clkdev_create(dai_clk, init.name,
"%s", dev_name(dev));
if (!dai_clk_lookup) {
ret = -ENOMEM;
goto err;
} else {
da7219->dai_clks_lookup[i] = dai_clk_lookup;
}
}
}
return 0;
err:
do {
if (da7219->dai_clks_lookup[i])
clkdev_drop(da7219->dai_clks_lookup[i]);
} while (i-- > 0);
return ret;
}
#else
static inline int da7219_register_dai_clks(struct snd_soc_component *component)
@@ -2080,12 +2363,17 @@ err_disable_reg:
static void da7219_remove(struct snd_soc_component *component)
{
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
#ifdef CONFIG_COMMON_CLK
int i;
#endif
da7219_aad_exit(component);
#ifdef CONFIG_COMMON_CLK
if (da7219->dai_clks_lookup)
clkdev_drop(da7219->dai_clks_lookup);
for (i = DA7219_DAI_NUM_CLKS - 1; i >= 0; --i) {
if (da7219->dai_clks_lookup[i])
clkdev_drop(da7219->dai_clks_lookup[i]);
}
#endif
/* Supplies */

Ver ficheiro

@@ -820,10 +820,10 @@ struct da7219_priv {
struct mutex pll_lock;
#ifdef CONFIG_COMMON_CLK
struct clk_hw dai_clks_hw;
struct clk_hw dai_clks_hw[DA7219_DAI_NUM_CLKS];
#endif
struct clk_lookup *dai_clks_lookup;
struct clk *dai_clks;
struct clk_lookup *dai_clks_lookup[DA7219_DAI_NUM_CLKS];
struct clk *dai_clks[DA7219_DAI_NUM_CLKS];
struct clk *mclk;
unsigned int mclk_rate;

Ver ficheiro

@@ -43,6 +43,7 @@ struct es8316_priv {
unsigned int sysclk;
unsigned int allowed_rates[NR_SUPPORTED_MCLK_LRCK_RATIOS];
struct snd_pcm_hw_constraint_list sysclk_constraints;
bool jd_inverted;
};
/*
@@ -577,6 +578,9 @@ static irqreturn_t es8316_irq(int irq, void *data)
if (!es8316->jack)
goto out;
if (es8316->jd_inverted)
flags ^= ES8316_GPIO_FLAG_HP_NOT_INSERTED;
dev_dbg(comp->dev, "gpio flags %#04x\n", flags);
if (flags & ES8316_GPIO_FLAG_HP_NOT_INSERTED) {
/* Jack removed, or spurious IRQ? */
@@ -592,6 +596,8 @@ static irqreturn_t es8316_irq(int irq, void *data)
/* Jack inserted, determine type */
es8316_enable_micbias_for_mic_gnd_short_detect(comp);
regmap_read(es8316->regmap, ES8316_GPIO_FLAG, &flags);
if (es8316->jd_inverted)
flags ^= ES8316_GPIO_FLAG_HP_NOT_INSERTED;
dev_dbg(comp->dev, "gpio flags %#04x\n", flags);
if (flags & ES8316_GPIO_FLAG_HP_NOT_INSERTED) {
/* Jack unplugged underneath us */
@@ -633,6 +639,14 @@ static void es8316_enable_jack_detect(struct snd_soc_component *component,
{
struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
/*
* Init es8316->jd_inverted here and not in the probe, as we cannot
* guarantee that the bytchr-es8316 driver, which might set this
* property, will probe before us.
*/
es8316->jd_inverted = device_property_read_bool(component->dev,
"everest,jack-detect-inverted");
mutex_lock(&es8316->lock);
es8316->jack = jack;

Ver ficheiro

@@ -328,6 +328,12 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component)
dev_err(&hdev->dev, "failed to create hda codec %d\n", ret);
goto error_no_pm;
}
/*
* Overwrite type to HDA_DEV_ASOC since it is a ASoC driver
* hda_codec.c will check this flag to determine if unregister
* device is needed.
*/
hdev->type = HDA_DEV_ASOC;
/*
* snd_hda_codec_device_new decrements the usage count so call get pm

Ver ficheiro

@@ -455,24 +455,11 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hparams, struct snd_soc_dai *dai)
{
struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai);
struct hdac_device *hdev = hdmi->hdev;
struct hdac_hdmi_dai_port_map *dai_map;
struct hdac_hdmi_port *port;
struct hdac_hdmi_pcm *pcm;
int format;
dai_map = &hdmi->dai_map[dai->id];
port = dai_map->port;
if (!port)
return -ENODEV;
if ((!port->eld.monitor_present) || (!port->eld.eld_valid)) {
dev_err(&hdev->dev,
"device is not configured for this pin:port%d:%d\n",
port->pin->nid, port->id);
return -ENODEV;
}
format = snd_hdac_calc_stream_format(params_rate(hparams),
params_channels(hparams), params_format(hparams),
@@ -1854,6 +1841,17 @@ static int hdmi_codec_probe(struct snd_soc_component *component)
/* Imp: Store the card pointer in hda_codec */
hdmi->card = dapm->card->snd_card;
/*
* Setup a device_link between card device and HDMI codec device.
* The card device is the consumer and the HDMI codec device is
* the supplier. With this setting, we can make sure that the audio
* domain in display power will be always turned on before operating
* on the HDMI audio codec registers.
* Let's use the flag DL_FLAG_AUTOREMOVE_CONSUMER. This can make
* sure the device link is freed when the machine driver is removed.
*/
device_link_add(component->card->dev, &hdev->dev, DL_FLAG_RPM_ACTIVE |
DL_FLAG_AUTOREMOVE_CONSUMER);
/*
* hdac_device core already sets the state to active and calls
* get_noresume. So enable runtime and set the device to suspend.

Ver ficheiro

@@ -439,8 +439,12 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream,
if (!ret) {
ret = snd_pcm_hw_constraint_eld(substream->runtime,
hcp->eld);
if (ret)
if (ret) {
mutex_lock(&hcp->current_stream_lock);
hcp->current_stream = NULL;
mutex_unlock(&hcp->current_stream_lock);
return ret;
}
}
/* Select chmap supported */
hdmi_codec_eld_chmap(hcp);
@@ -492,10 +496,6 @@ static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
return ret;
}
ret = hdmi_codec_new_stream(substream, dai);
if (ret)
return ret;
hdmi_audio_infoframe_init(&hp.cea);
hp.cea.channels = params_channels(params);
hp.cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
@@ -757,7 +757,7 @@ static int hdmi_codec_probe(struct platform_device *pdev)
dev_dbg(dev, "%s()\n", __func__);
if (!hcd) {
dev_err(dev, "%s: No plalform data\n", __func__);
dev_err(dev, "%s: No platform data\n", __func__);
return -EINVAL;
}

266
sound/soc/codecs/lochnagar-sc.c Ficheiro normal
Ver ficheiro

@@ -0,0 +1,266 @@
// SPDX-License-Identifier: GPL-2.0
//
// Lochnagar sound card driver
//
// Copyright (c) 2017-2019 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
//
// Author: Charles Keepax <ckeepax@opensource.cirrus.com>
// Piotr Stankiewicz <piotrs@opensource.cirrus.com>
#include <linux/clk.h>
#include <linux/module.h>
#include <sound/soc.h>
#include <linux/mfd/lochnagar.h>
#include <linux/mfd/lochnagar1_regs.h>
#include <linux/mfd/lochnagar2_regs.h>
struct lochnagar_sc_priv {
struct clk *mclk;
};
static const struct snd_soc_dapm_widget lochnagar_sc_widgets[] = {
SND_SOC_DAPM_LINE("Line Jack", NULL),
SND_SOC_DAPM_LINE("USB Audio", NULL),
};
static const struct snd_soc_dapm_route lochnagar_sc_routes[] = {
{ "Line Jack", NULL, "AIF1 Playback" },
{ "AIF1 Capture", NULL, "Line Jack" },
{ "USB Audio", NULL, "USB1 Playback" },
{ "USB Audio", NULL, "USB2 Playback" },
{ "USB1 Capture", NULL, "USB Audio" },
{ "USB2 Capture", NULL, "USB Audio" },
};
static const unsigned int lochnagar_sc_chan_vals[] = {
4, 8,
};
static const struct snd_pcm_hw_constraint_list lochnagar_sc_chan_constraint = {
.count = ARRAY_SIZE(lochnagar_sc_chan_vals),
.list = lochnagar_sc_chan_vals,
};
static const unsigned int lochnagar_sc_rate_vals[] = {
8000, 16000, 24000, 32000, 48000, 96000, 192000,
22050, 44100, 88200, 176400,
};
static const struct snd_pcm_hw_constraint_list lochnagar_sc_rate_constraint = {
.count = ARRAY_SIZE(lochnagar_sc_rate_vals),
.list = lochnagar_sc_rate_vals,
};
static int lochnagar_sc_hw_rule_rate(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct snd_interval range = {
.min = 8000,
.max = 24576000 / hw_param_interval(params, rule->deps[0])->max,
};
return snd_interval_refine(hw_param_interval(params, rule->var),
&range);
}
static int lochnagar_sc_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_component *comp = dai->component;
struct lochnagar_sc_priv *priv = snd_soc_component_get_drvdata(comp);
int ret;
ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
&lochnagar_sc_rate_constraint);
if (ret)
return ret;
return snd_pcm_hw_rule_add(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
lochnagar_sc_hw_rule_rate, priv,
SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);
}
static int lochnagar_sc_line_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_component *comp = dai->component;
struct lochnagar_sc_priv *priv = snd_soc_component_get_drvdata(comp);
int ret;
ret = clk_prepare_enable(priv->mclk);
if (ret < 0) {
dev_err(dai->dev, "Failed to enable MCLK: %d\n", ret);
return ret;
}
ret = lochnagar_sc_startup(substream, dai);
if (ret)
return ret;
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_CHANNELS,
&lochnagar_sc_chan_constraint);
}
static void lochnagar_sc_line_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_component *comp = dai->component;
struct lochnagar_sc_priv *priv = snd_soc_component_get_drvdata(comp);
clk_disable_unprepare(priv->mclk);
}
static int lochnagar_sc_check_fmt(struct snd_soc_dai *dai, unsigned int fmt,
unsigned int tar)
{
tar |= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF;
if ((fmt & ~SND_SOC_DAIFMT_CLOCK_MASK) != tar)
return -EINVAL;
return 0;
}
static int lochnagar_sc_set_line_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
return lochnagar_sc_check_fmt(dai, fmt, SND_SOC_DAIFMT_CBS_CFS);
}
static int lochnagar_sc_set_usb_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
return lochnagar_sc_check_fmt(dai, fmt, SND_SOC_DAIFMT_CBM_CFM);
}
static const struct snd_soc_dai_ops lochnagar_sc_line_ops = {
.startup = lochnagar_sc_line_startup,
.shutdown = lochnagar_sc_line_shutdown,
.set_fmt = lochnagar_sc_set_line_fmt,
};
static const struct snd_soc_dai_ops lochnagar_sc_usb_ops = {
.startup = lochnagar_sc_startup,
.set_fmt = lochnagar_sc_set_usb_fmt,
};
static struct snd_soc_dai_driver lochnagar_sc_dai[] = {
{
.name = "lochnagar-line",
.playback = {
.stream_name = "AIF1 Playback",
.channels_min = 4,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_KNOT,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
},
.capture = {
.stream_name = "AIF1 Capture",
.channels_min = 4,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_KNOT,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
},
.ops = &lochnagar_sc_line_ops,
.symmetric_rates = true,
.symmetric_samplebits = true,
},
{
.name = "lochnagar-usb1",
.playback = {
.stream_name = "USB1 Playback",
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_KNOT,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
},
.capture = {
.stream_name = "USB1 Capture",
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_KNOT,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
},
.ops = &lochnagar_sc_usb_ops,
.symmetric_rates = true,
.symmetric_samplebits = true,
},
{
.name = "lochnagar-usb2",
.playback = {
.stream_name = "USB2 Playback",
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_KNOT,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
},
.capture = {
.stream_name = "USB2 Capture",
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_KNOT,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
},
.ops = &lochnagar_sc_usb_ops,
.symmetric_rates = true,
.symmetric_samplebits = true,
},
};
static const struct snd_soc_component_driver lochnagar_sc_driver = {
.non_legacy_dai_naming = 1,
.dapm_widgets = lochnagar_sc_widgets,
.num_dapm_widgets = ARRAY_SIZE(lochnagar_sc_widgets),
.dapm_routes = lochnagar_sc_routes,
.num_dapm_routes = ARRAY_SIZE(lochnagar_sc_routes),
};
static int lochnagar_sc_probe(struct platform_device *pdev)
{
struct lochnagar_sc_priv *priv;
int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->mclk = devm_clk_get(&pdev->dev, "mclk");
if (IS_ERR(priv->mclk)) {
ret = PTR_ERR(priv->mclk);
dev_err(&pdev->dev, "Failed to get MCLK: %d\n", ret);
return ret;
}
platform_set_drvdata(pdev, priv);
return devm_snd_soc_register_component(&pdev->dev,
&lochnagar_sc_driver,
lochnagar_sc_dai,
ARRAY_SIZE(lochnagar_sc_dai));
}
static const struct of_device_id lochnagar_of_match[] = {
{ .compatible = "cirrus,lochnagar2-soundcard" },
{}
};
MODULE_DEVICE_TABLE(of, lochnagar_of_match);
static struct platform_driver lochnagar_sc_codec_driver = {
.driver = {
.name = "lochnagar-soundcard",
.of_match_table = of_match_ptr(lochnagar_of_match),
},
.probe = lochnagar_sc_probe,
};
module_platform_driver(lochnagar_sc_codec_driver);
MODULE_DESCRIPTION("ASoC Lochnagar Sound Card Driver");
MODULE_AUTHOR("Piotr Stankiewicz <piotrs@opensource.cirrus.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:lochnagar-soundcard");

Ver ficheiro

@@ -1194,14 +1194,14 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
&max98090_right_rcv_mixer_controls[0],
ARRAY_SIZE(max98090_right_rcv_mixer_controls)),
SND_SOC_DAPM_MUX("LINMOD Mux", M98090_REG_LOUTR_MIXER,
M98090_LINMOD_SHIFT, 0, &max98090_linmod_mux),
SND_SOC_DAPM_MUX("LINMOD Mux", SND_SOC_NOPM, 0, 0,
&max98090_linmod_mux),
SND_SOC_DAPM_MUX("MIXHPLSEL Mux", M98090_REG_HP_CONTROL,
M98090_MIXHPLSEL_SHIFT, 0, &max98090_mixhplsel_mux),
SND_SOC_DAPM_MUX("MIXHPLSEL Mux", SND_SOC_NOPM, 0, 0,
&max98090_mixhplsel_mux),
SND_SOC_DAPM_MUX("MIXHPRSEL Mux", M98090_REG_HP_CONTROL,
M98090_MIXHPRSEL_SHIFT, 0, &max98090_mixhprsel_mux),
SND_SOC_DAPM_MUX("MIXHPRSEL Mux", SND_SOC_NOPM, 0, 0,
&max98090_mixhprsel_mux),
SND_SOC_DAPM_PGA("HP Left Out", M98090_REG_OUTPUT_ENABLE,
M98090_HPLEN_SHIFT, 0, NULL, 0),

Ver ficheiro

@@ -97,7 +97,10 @@ static struct snd_soc_dai_driver max98357a_dai_driver = {
SNDRV_PCM_FMTBIT_S32,
.rates = SNDRV_PCM_RATE_8000 |
SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_88200 |
SNDRV_PCM_RATE_96000,
.rate_min = 8000,
.rate_max = 96000,

Ver ficheiro

@@ -320,32 +320,6 @@ enum {
#define DL_GAIN_N_40DB_REG (DL_GAIN_N_40DB << 7 | DL_GAIN_N_40DB)
#define DL_GAIN_REG_MASK 0x0f9f
static void lo_store_gain(struct mt6358_priv *priv)
{
unsigned int reg;
unsigned int gain_l, gain_r;
regmap_read(priv->regmap, MT6358_ZCD_CON1, &reg);
gain_l = (reg >> RG_AUDLOLGAIN_SFT) & RG_AUDLOLGAIN_MASK;
gain_r = (reg >> RG_AUDLORGAIN_SFT) & RG_AUDLORGAIN_MASK;
priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL] = gain_l;
priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR] = gain_r;
}
static void hp_store_gain(struct mt6358_priv *priv)
{
unsigned int reg;
unsigned int gain_l, gain_r;
regmap_read(priv->regmap, MT6358_ZCD_CON2, &reg);
gain_l = (reg >> RG_AUDHPLGAIN_SFT) & RG_AUDHPLGAIN_MASK;
gain_r = (reg >> RG_AUDHPRGAIN_SFT) & RG_AUDHPRGAIN_MASK;
priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL] = gain_l;
priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR] = gain_r;
}
static void hp_zcd_disable(struct mt6358_priv *priv)
{
regmap_write(priv->regmap, MT6358_ZCD_CON0, 0x0000);
@@ -405,10 +379,9 @@ static bool is_valid_hp_pga_idx(int reg_idx)
reg_idx == DL_GAIN_N_40DB;
}
static void headset_volume_ramp(struct mt6358_priv *priv,
int from, int to)
static void headset_volume_ramp(struct mt6358_priv *priv, int from, int to)
{
int offset = 0, count = 1, reg_idx;
int offset = 0, count = 0, reg_idx;
if (!is_valid_hp_pga_idx(from) || !is_valid_hp_pga_idx(to))
dev_warn(priv->dev, "%s(), volume index is not valid, from %d, to %d\n",
@@ -422,7 +395,7 @@ static void headset_volume_ramp(struct mt6358_priv *priv,
else
offset = from - to;
while (offset > 0) {
while (offset >= 0) {
if (to > from)
reg_idx = from + count;
else
@@ -440,25 +413,76 @@ static void headset_volume_ramp(struct mt6358_priv *priv,
}
}
static int mt6358_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component =
snd_soc_kcontrol_component(kcontrol);
struct mt6358_priv *priv = snd_soc_component_get_drvdata(component);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg;
int ret;
ret = snd_soc_put_volsw(kcontrol, ucontrol);
if (ret < 0)
return ret;
switch (mc->reg) {
case MT6358_ZCD_CON2:
regmap_read(priv->regmap, MT6358_ZCD_CON2, &reg);
priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL] =
(reg >> RG_AUDHPLGAIN_SFT) & RG_AUDHPLGAIN_MASK;
priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR] =
(reg >> RG_AUDHPRGAIN_SFT) & RG_AUDHPRGAIN_MASK;
break;
case MT6358_ZCD_CON1:
regmap_read(priv->regmap, MT6358_ZCD_CON1, &reg);
priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL] =
(reg >> RG_AUDLOLGAIN_SFT) & RG_AUDLOLGAIN_MASK;
priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR] =
(reg >> RG_AUDLORGAIN_SFT) & RG_AUDLORGAIN_MASK;
break;
case MT6358_ZCD_CON3:
regmap_read(priv->regmap, MT6358_ZCD_CON3, &reg);
priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL] =
(reg >> RG_AUDHSGAIN_SFT) & RG_AUDHSGAIN_MASK;
priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTR] =
(reg >> RG_AUDHSGAIN_SFT) & RG_AUDHSGAIN_MASK;
break;
case MT6358_AUDENC_ANA_CON0:
case MT6358_AUDENC_ANA_CON1:
regmap_read(priv->regmap, MT6358_AUDENC_ANA_CON0, &reg);
priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1] =
(reg >> RG_AUDPREAMPLGAIN_SFT) & RG_AUDPREAMPLGAIN_MASK;
regmap_read(priv->regmap, MT6358_AUDENC_ANA_CON1, &reg);
priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2] =
(reg >> RG_AUDPREAMPRGAIN_SFT) & RG_AUDPREAMPRGAIN_MASK;
break;
}
return ret;
}
static const DECLARE_TLV_DB_SCALE(playback_tlv, -1000, 100, 0);
static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 600, 0);
static const struct snd_kcontrol_new mt6358_snd_controls[] = {
/* dl pga gain */
SOC_DOUBLE_TLV("Headphone Volume",
MT6358_ZCD_CON2, 0, 7, 0x12, 1,
playback_tlv),
SOC_DOUBLE_TLV("Lineout Volume",
MT6358_ZCD_CON1, 0, 7, 0x12, 1,
playback_tlv),
SOC_SINGLE_TLV("Handset Volume",
MT6358_ZCD_CON3, 0, 0x12, 1,
playback_tlv),
SOC_DOUBLE_EXT_TLV("Headphone Volume",
MT6358_ZCD_CON2, 0, 7, 0x12, 1,
snd_soc_get_volsw, mt6358_put_volsw, playback_tlv),
SOC_DOUBLE_EXT_TLV("Lineout Volume",
MT6358_ZCD_CON1, 0, 7, 0x12, 1,
snd_soc_get_volsw, mt6358_put_volsw, playback_tlv),
SOC_SINGLE_EXT_TLV("Handset Volume",
MT6358_ZCD_CON3, 0, 0x12, 1,
snd_soc_get_volsw, mt6358_put_volsw, playback_tlv),
/* ul pga gain */
SOC_DOUBLE_R_TLV("PGA Volume",
MT6358_AUDENC_ANA_CON0, MT6358_AUDENC_ANA_CON1,
8, 4, 0,
pga_tlv),
SOC_DOUBLE_R_EXT_TLV("PGA Volume",
MT6358_AUDENC_ANA_CON0, MT6358_AUDENC_ANA_CON1,
8, 4, 0,
snd_soc_get_volsw, mt6358_put_volsw, pga_tlv),
};
/* MUX */
@@ -832,8 +856,6 @@ static int mtk_hp_enable(struct mt6358_priv *priv)
/* Reduce ESD resistance of AU_REFN */
regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON2, 0x4000);
/* save target gain to restore after hardware open complete */
hp_store_gain(priv);
/* Set HPR/HPL gain as minimum (~ -40dB) */
regmap_write(priv->regmap, MT6358_ZCD_CON2, DL_GAIN_N_40DB_REG);
@@ -1043,8 +1065,6 @@ static int mtk_hp_spk_enable(struct mt6358_priv *priv)
/* Reduce ESD resistance of AU_REFN */
regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON2, 0x4000);
/* save target gain to restore after hardware open complete */
hp_store_gain(priv);
/* Set HPR/HPL gain to -10dB */
regmap_write(priv->regmap, MT6358_ZCD_CON2, DL_GAIN_N_10DB_REG);
@@ -1104,7 +1124,6 @@ static int mtk_hp_spk_enable(struct mt6358_priv *priv)
hp_main_output_ramp(priv, true);
/* Set LO gain as minimum (~ -40dB) */
lo_store_gain(priv);
regmap_write(priv->regmap, MT6358_ZCD_CON1, DL_GAIN_N_40DB_REG);
/* apply volume setting */
headset_volume_ramp(priv,
@@ -1740,6 +1759,21 @@ static void mt6358_dmic_disable(struct mt6358_priv *priv)
regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON9, 0x0000);
}
static void mt6358_restore_pga(struct mt6358_priv *priv)
{
unsigned int gain_l, gain_r;
gain_l = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1];
gain_r = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2];
regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0,
RG_AUDPREAMPLGAIN_MASK_SFT,
gain_l << RG_AUDPREAMPLGAIN_SFT);
regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
RG_AUDPREAMPRGAIN_MASK_SFT,
gain_r << RG_AUDPREAMPRGAIN_SFT);
}
static int mt_mic_type_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
@@ -1764,6 +1798,7 @@ static int mt_mic_type_event(struct snd_soc_dapm_widget *w,
mt6358_amic_enable(priv);
break;
}
mt6358_restore_pga(priv);
break;
case SND_SOC_DAPM_POST_PMD:

Ver ficheiro

@@ -493,7 +493,7 @@ static int nau8810_set_sysclk(struct snd_soc_dai *dai,
return 0;
}
static int nau88l0_calc_pll(unsigned int pll_in,
static int nau8810_calc_pll(unsigned int pll_in,
unsigned int fs, struct nau8810_pll *pll_param)
{
u64 f2, f2_max, pll_ratio;
@@ -505,7 +505,8 @@ static int nau88l0_calc_pll(unsigned int pll_in,
f2_max = 0;
scal_sel = ARRAY_SIZE(nau8810_mclk_scaler);
for (i = 0; i < ARRAY_SIZE(nau8810_mclk_scaler); i++) {
f2 = 256 * fs * 4 * nau8810_mclk_scaler[i] / 10;
f2 = 256ULL * fs * 4 * nau8810_mclk_scaler[i];
f2 = div_u64(f2, 10);
if (f2 > NAU_PLL_FREQ_MIN && f2 < NAU_PLL_FREQ_MAX &&
f2_max < f2) {
f2_max = f2;
@@ -542,7 +543,7 @@ static int nau8810_set_pll(struct snd_soc_dai *codec_dai, int pll_id,
int ret, fs;
fs = freq_out / 256;
ret = nau88l0_calc_pll(freq_in, fs, pll_param);
ret = nau8810_calc_pll(freq_in, fs, pll_param);
if (ret < 0) {
dev_err(nau8810->dev, "Unsupported input clock %d\n", freq_in);
return ret;
@@ -667,6 +668,24 @@ static int nau8810_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_component *component = dai->component;
struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component);
int val_len = 0, val_rate = 0, ret = 0;
unsigned int ctrl_val, bclk_fs, bclk_div;
/* Select BCLK configuration if the codec as master. */
regmap_read(nau8810->regmap, NAU8810_REG_CLOCK, &ctrl_val);
if (ctrl_val & NAU8810_CLKIO_MASTER) {
/* get the bclk and fs ratio */
bclk_fs = snd_soc_params_to_bclk(params) / params_rate(params);
if (bclk_fs <= 32)
bclk_div = NAU8810_BCLKDIV_8;
else if (bclk_fs <= 64)
bclk_div = NAU8810_BCLKDIV_4;
else if (bclk_fs <= 128)
bclk_div = NAU8810_BCLKDIV_2;
else
return -EINVAL;
regmap_update_bits(nau8810->regmap, NAU8810_REG_CLOCK,
NAU8810_BCLKSEL_MASK, bclk_div);
}
switch (params_width(params)) {
case 16:

Ver ficheiro

@@ -457,13 +457,16 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
if (chan > 2) {
switch (fmt) {
case PCM3168A_FMT_I2S:
case PCM3168A_FMT_DSP_A:
fmt = PCM3168A_FMT_I2S_TDM;
break;
case PCM3168A_FMT_LEFT_J:
case PCM3168A_FMT_DSP_B:
fmt = PCM3168A_FMT_LEFT_J_TDM;
break;
default:
dev_err(component->dev, "TDM is supported under I2S/Left_J only\n");
dev_err(component->dev,
"TDM is supported under DSP/I2S/Left_J only\n");
return -EINVAL;
}
}
@@ -526,6 +529,8 @@ static int pcm3168a_startup(struct snd_pcm_substream *substream,
break;
case PCM3168A_FMT_LEFT_J:
case PCM3168A_FMT_I2S:
case PCM3168A_FMT_DSP_A:
case PCM3168A_FMT_DSP_B:
sample_min = 24;
channel_max = channel_maxs[tx];
break;

Ver ficheiro

@@ -3419,6 +3419,9 @@ static int rt5645_probe(struct snd_soc_component *component)
RT5645_HWEQ_NUM, sizeof(struct rt5645_eq_param_s),
GFP_KERNEL);
if (!rt5645->eq_param)
return -ENOMEM;
return 0;
}
@@ -3631,6 +3634,11 @@ static const struct rt5645_platform_data jd_mode3_platform_data = {
.jd_mode = 3,
};
static const struct rt5645_platform_data lattepanda_board_platform_data = {
.jd_mode = 2,
.inv_jd1_1 = true
};
static const struct dmi_system_id dmi_platform_data[] = {
{
.ident = "Chrome Buddy",
@@ -3728,6 +3736,15 @@ static const struct dmi_system_id dmi_platform_data[] = {
},
.driver_data = (void *)&intel_braswell_platform_data,
},
{
.ident = "LattePanda board",
.matches = {
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"),
DMI_EXACT_MATCH(DMI_BOARD_VERSION, "Default string"),
},
.driver_data = (void *)&lattepanda_board_platform_data,
},
{ }
};

Ver ficheiro

@@ -1645,7 +1645,10 @@ static bool rt5651_jack_inserted(struct snd_soc_component *component)
break;
}
return val == 0;
if (rt5651->jd_active_high)
return val != 0;
else
return val == 0;
}
/* Jack detect and button-press timings */
@@ -1868,20 +1871,47 @@ static void rt5651_enable_jack_detect(struct snd_soc_component *component,
case RT5651_JD1_1:
snd_soc_component_update_bits(component, RT5651_JD_CTRL2,
RT5651_JD_TRG_SEL_MASK, RT5651_JD_TRG_SEL_JD1_1);
snd_soc_component_update_bits(component, RT5651_IRQ_CTRL1,
RT5651_JD1_1_IRQ_EN, RT5651_JD1_1_IRQ_EN);
/* active-low is normal, set inv flag for active-high */
if (rt5651->jd_active_high)
snd_soc_component_update_bits(component,
RT5651_IRQ_CTRL1,
RT5651_JD1_1_IRQ_EN | RT5651_JD1_1_INV,
RT5651_JD1_1_IRQ_EN | RT5651_JD1_1_INV);
else
snd_soc_component_update_bits(component,
RT5651_IRQ_CTRL1,
RT5651_JD1_1_IRQ_EN | RT5651_JD1_1_INV,
RT5651_JD1_1_IRQ_EN);
break;
case RT5651_JD1_2:
snd_soc_component_update_bits(component, RT5651_JD_CTRL2,
RT5651_JD_TRG_SEL_MASK, RT5651_JD_TRG_SEL_JD1_2);
snd_soc_component_update_bits(component, RT5651_IRQ_CTRL1,
RT5651_JD1_2_IRQ_EN, RT5651_JD1_2_IRQ_EN);
/* active-low is normal, set inv flag for active-high */
if (rt5651->jd_active_high)
snd_soc_component_update_bits(component,
RT5651_IRQ_CTRL1,
RT5651_JD1_2_IRQ_EN | RT5651_JD1_2_INV,
RT5651_JD1_2_IRQ_EN | RT5651_JD1_2_INV);
else
snd_soc_component_update_bits(component,
RT5651_IRQ_CTRL1,
RT5651_JD1_2_IRQ_EN | RT5651_JD1_2_INV,
RT5651_JD1_2_IRQ_EN);
break;
case RT5651_JD2:
snd_soc_component_update_bits(component, RT5651_JD_CTRL2,
RT5651_JD_TRG_SEL_MASK, RT5651_JD_TRG_SEL_JD2);
snd_soc_component_update_bits(component, RT5651_IRQ_CTRL1,
RT5651_JD2_IRQ_EN, RT5651_JD2_IRQ_EN);
/* active-low is normal, set inv flag for active-high */
if (rt5651->jd_active_high)
snd_soc_component_update_bits(component,
RT5651_IRQ_CTRL1,
RT5651_JD2_IRQ_EN | RT5651_JD2_INV,
RT5651_JD2_IRQ_EN | RT5651_JD2_INV);
else
snd_soc_component_update_bits(component,
RT5651_IRQ_CTRL1,
RT5651_JD2_IRQ_EN | RT5651_JD2_INV,
RT5651_JD2_IRQ_EN);
break;
default:
dev_err(component->dev, "Currently only JD1_1 / JD1_2 / JD2 are supported\n");
@@ -1986,6 +2016,9 @@ static void rt5651_apply_properties(struct snd_soc_component *component)
"realtek,jack-detect-source", &val) == 0)
rt5651->jd_src = val;
if (device_property_read_bool(component->dev, "realtek,jack-detect-not-inverted"))
rt5651->jd_active_high = true;
/*
* Testing on various boards has shown that good defaults for the OVCD
* threshold and scale-factor are 2000µA and 0.75. For an effective

Ver ficheiro

@@ -2083,6 +2083,7 @@ struct rt5651_priv {
int release_count;
int poll_count;
unsigned int jd_src;
bool jd_active_high;
unsigned int ovcd_th;
unsigned int ovcd_sf;

Ver ficheiro

@@ -25,6 +25,7 @@
#include <linux/sysfs.h>
#include <linux/clk.h>
#include <linux/firmware.h>
#include <linux/acpi.h>
#include "rt5677-spi.h"
@@ -57,13 +58,15 @@ static DEFINE_MUTEX(spi_mutex);
* RT5677_SPI_READ/WRITE_32: Transfer 4 bytes
* RT5677_SPI_READ/WRITE_BURST: Transfer any multiples of 8 bytes
*
* For example, reading 260 bytes at 0x60030002 uses the following commands:
* 0x60030002 RT5677_SPI_READ_16 2 bytes
* Note:
* 16 Bit writes and reads are restricted to the address range
* 0x18020000 ~ 0x18021000
*
* For example, reading 256 bytes at 0x60030004 uses the following commands:
* 0x60030004 RT5677_SPI_READ_32 4 bytes
* 0x60030008 RT5677_SPI_READ_BURST 240 bytes
* 0x600300F8 RT5677_SPI_READ_BURST 8 bytes
* 0x60030100 RT5677_SPI_READ_32 4 bytes
* 0x60030104 RT5677_SPI_READ_16 2 bytes
*
* Input:
* @read: true for read commands; false for write commands
@@ -78,15 +81,13 @@ static u8 rt5677_spi_select_cmd(bool read, u32 align, u32 remain, u32 *len)
{
u8 cmd;
if (align == 2 || align == 6 || remain == 2) {
cmd = RT5677_SPI_READ_16;
*len = 2;
} else if (align == 4 || remain <= 6) {
if (align == 4 || remain <= 4) {
cmd = RT5677_SPI_READ_32;
*len = 4;
} else {
cmd = RT5677_SPI_READ_BURST;
*len = min_t(u32, remain & ~7, RT5677_SPI_BURST_LEN);
*len = (((remain - 1) >> 3) + 1) << 3;
*len = min_t(u32, *len, RT5677_SPI_BURST_LEN);
}
return read ? cmd : cmd + 1;
}
@@ -107,7 +108,7 @@ static void rt5677_spi_reverse(u8 *dst, u32 dstlen, const u8 *src, u32 srclen)
}
}
/* Read DSP address space using SPI. addr and len have to be 2-byte aligned. */
/* Read DSP address space using SPI. addr and len have to be 4-byte aligned. */
int rt5677_spi_read(u32 addr, void *rxbuf, size_t len)
{
u32 offset;
@@ -123,7 +124,7 @@ int rt5677_spi_read(u32 addr, void *rxbuf, size_t len)
if (!g_spi)
return -ENODEV;
if ((addr & 1) || (len & 1)) {
if ((addr & 3) || (len & 3)) {
dev_err(&g_spi->dev, "Bad read align 0x%x(%zu)\n", addr, len);
return -EACCES;
}
@@ -158,13 +159,13 @@ int rt5677_spi_read(u32 addr, void *rxbuf, size_t len)
}
EXPORT_SYMBOL_GPL(rt5677_spi_read);
/* Write DSP address space using SPI. addr has to be 2-byte aligned.
* If len is not 2-byte aligned, an extra byte of zero is written at the end
/* Write DSP address space using SPI. addr has to be 4-byte aligned.
* If len is not 4-byte aligned, then extra zeros are written at the end
* as padding.
*/
int rt5677_spi_write(u32 addr, const void *txbuf, size_t len)
{
u32 offset, len_with_pad = len;
u32 offset;
int status = 0;
struct spi_transfer t;
struct spi_message m;
@@ -177,22 +178,19 @@ int rt5677_spi_write(u32 addr, const void *txbuf, size_t len)
if (!g_spi)
return -ENODEV;
if (addr & 1) {
if (addr & 3) {
dev_err(&g_spi->dev, "Bad write align 0x%x(%zu)\n", addr, len);
return -EACCES;
}
if (len & 1)
len_with_pad = len + 1;
memset(&t, 0, sizeof(t));
t.tx_buf = buf;
t.speed_hz = RT5677_SPI_FREQ;
spi_message_init_with_transfers(&m, &t, 1);
for (offset = 0; offset < len_with_pad;) {
for (offset = 0; offset < len;) {
spi_cmd = rt5677_spi_select_cmd(false, (addr + offset) & 7,
len_with_pad - offset, &t.len);
len - offset, &t.len);
/* Construct SPI message header */
buf[0] = spi_cmd;
@@ -226,9 +224,16 @@ static int rt5677_spi_probe(struct spi_device *spi)
return 0;
}
static const struct acpi_device_id rt5677_spi_acpi_id[] = {
{ "RT5677AA", 0 },
{ }
};
MODULE_DEVICE_TABLE(acpi, rt5677_spi_acpi_id);
static struct spi_driver rt5677_spi_driver = {
.driver = {
.name = "rt5677",
.acpi_match_table = ACPI_PTR(rt5677_spi_acpi_id),
},
.probe = rt5677_spi_probe,
};

Ver ficheiro

@@ -2588,6 +2588,7 @@ static int rt5682_i2c_probe(struct i2c_client *i2c,
rt5682_reset(rt5682->regmap);
mutex_init(&rt5682->calibrate_mutex);
rt5682_calibrate(rt5682);
ret = regmap_multi_reg_write(rt5682->regmap, patch_list,
@@ -2654,7 +2655,6 @@ static int rt5682_i2c_probe(struct i2c_client *i2c,
INIT_DELAYED_WORK(&rt5682->jd_check_work,
rt5682_jd_check_handler);
mutex_init(&rt5682->calibrate_mutex);
if (i2c->irq) {
ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,

Ver ficheiro

@@ -89,7 +89,8 @@ static int simple_amp_probe(struct platform_device *pdev)
return -ENOMEM;
platform_set_drvdata(pdev, priv);
priv->gpiod_enable = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
priv->gpiod_enable = devm_gpiod_get_optional(dev, "enable",
GPIOD_OUT_LOW);
if (IS_ERR(priv->gpiod_enable)) {
err = PTR_ERR(priv->gpiod_enable);
if (err != -EPROBE_DEFER)

Ver ficheiro

@@ -461,9 +461,6 @@ static int sirf_audio_codec_driver_probe(struct platform_device *pdev)
struct sirf_audio_codec *sirf_audio_codec;
void __iomem *base;
struct resource *mem_res;
const struct of_device_id *match;
match = of_match_node(sirf_audio_codec_of_match, pdev->dev.of_node);
sirf_audio_codec = devm_kzalloc(&pdev->dev,
sizeof(struct sirf_audio_codec), GFP_KERNEL);

Ver ficheiro

@@ -25,6 +25,7 @@
#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
@@ -89,6 +90,7 @@ static bool aic31xx_volatile(struct device *dev, unsigned int reg)
case AIC31XX_INTRADCFLAG: /* Sticky interrupt flags */
case AIC31XX_INTRDACFLAG2:
case AIC31XX_INTRADCFLAG2:
case AIC31XX_HSDETECT:
return true;
}
return false;
@@ -163,6 +165,7 @@ struct aic31xx_priv {
struct aic31xx_pdata pdata;
struct regulator_bulk_data supplies[AIC31XX_NUM_SUPPLIES];
struct aic31xx_disable_nb disable_nb[AIC31XX_NUM_SUPPLIES];
struct snd_soc_jack *jack;
unsigned int sysclk;
u8 p_div;
int rate_div_line;
@@ -1261,6 +1264,20 @@ static int aic31xx_set_bias_level(struct snd_soc_component *component,
return 0;
}
static int aic31xx_set_jack(struct snd_soc_component *component,
struct snd_soc_jack *jack, void *data)
{
struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
aic31xx->jack = jack;
/* Enable/Disable jack detection */
regmap_write(aic31xx->regmap, AIC31XX_HSDETECT,
jack ? AIC31XX_HSD_ENABLE : 0);
return 0;
}
static int aic31xx_codec_probe(struct snd_soc_component *component)
{
struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
@@ -1301,6 +1318,7 @@ static int aic31xx_codec_probe(struct snd_soc_component *component)
static const struct snd_soc_component_driver soc_codec_driver_aic31xx = {
.probe = aic31xx_codec_probe,
.set_jack = aic31xx_set_jack,
.set_bias_level = aic31xx_set_bias_level,
.controls = common31xx_snd_controls,
.num_controls = ARRAY_SIZE(common31xx_snd_controls),
@@ -1405,8 +1423,47 @@ static irqreturn_t aic31xx_irq(int irq, void *data)
dev_err(dev, "Short circuit on Left output is detected\n");
if (value & AIC31XX_HPRSCDETECT)
dev_err(dev, "Short circuit on Right output is detected\n");
if (value & (AIC31XX_HSPLUG | AIC31XX_BUTTONPRESS)) {
unsigned int val;
int status = 0;
ret = regmap_read(aic31xx->regmap, AIC31XX_INTRDACFLAG2,
&val);
if (ret) {
dev_err(dev, "Failed to read interrupt mask: %d\n",
ret);
goto exit;
}
if (val & AIC31XX_BUTTONPRESS)
status |= SND_JACK_BTN_0;
ret = regmap_read(aic31xx->regmap, AIC31XX_HSDETECT, &val);
if (ret) {
dev_err(dev, "Failed to read headset type: %d\n", ret);
goto exit;
}
switch ((val & AIC31XX_HSD_TYPE_MASK) >>
AIC31XX_HSD_TYPE_SHIFT) {
case AIC31XX_HSD_HP:
status |= SND_JACK_HEADPHONE;
break;
case AIC31XX_HSD_HS:
status |= SND_JACK_HEADSET;
break;
default:
break;
}
if (aic31xx->jack)
snd_soc_jack_report(aic31xx->jack, status,
AIC31XX_JACK_MASK);
}
if (value & ~(AIC31XX_HPLSCDETECT |
AIC31XX_HPRSCDETECT))
AIC31XX_HPRSCDETECT |
AIC31XX_HSPLUG |
AIC31XX_BUTTONPRESS))
dev_err(dev, "Unknown DAC interrupt flags: 0x%08x\n", value);
read_overflow:
@@ -1518,6 +1575,8 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c,
AIC31XX_GPIO1_FUNC_SHIFT);
regmap_write(aic31xx->regmap, AIC31XX_INT1CTRL,
AIC31XX_HSPLUGDET |
AIC31XX_BUTTONPRESSDET |
AIC31XX_SC |
AIC31XX_ENGINE);

Ver ficheiro

@@ -20,6 +20,10 @@
#define AIC31XX_MINIDSP_BIT BIT(2)
#define DAC31XX_BIT BIT(3)
#define AIC31XX_JACK_MASK (SND_JACK_HEADPHONE | \
SND_JACK_HEADSET | \
SND_JACK_BTN_0)
enum aic31xx_type {
AIC3100 = 0,
AIC3110 = AIC31XX_STEREO_CLASS_D_BIT,
@@ -220,6 +224,14 @@ struct aic31xx_pdata {
/* AIC31XX_DACMUTE */
#define AIC31XX_DACMUTE_MASK GENMASK(3, 2)
/* AIC31XX_HSDETECT */
#define AIC31XX_HSD_ENABLE BIT(7)
#define AIC31XX_HSD_TYPE_MASK GENMASK(6, 5)
#define AIC31XX_HSD_TYPE_SHIFT 5
#define AIC31XX_HSD_NONE 0x00
#define AIC31XX_HSD_HP 0x01
#define AIC31XX_HSD_HS 0x03
/* AIC31XX_MICBIAS */
#define AIC31XX_MICBIAS_MASK GENMASK(1, 0)
#define AIC31XX_MICBIAS_SHIFT 0

Ver ficheiro

@@ -0,0 +1,483 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Clock Tree for the Texas Instruments TLV320AIC32x4
*
* Copyright 2019 Annaliese McDermond
*
* Author: Annaliese McDermond <nh6z@nh6z.net>
*/
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/regmap.h>
#include <linux/device.h>
#include "tlv320aic32x4.h"
#define to_clk_aic32x4(_hw) container_of(_hw, struct clk_aic32x4, hw)
struct clk_aic32x4 {
struct clk_hw hw;
struct device *dev;
struct regmap *regmap;
unsigned int reg;
};
/*
* struct clk_aic32x4_pll_muldiv - Multiplier/divider settings
* @p: Divider
* @r: first multiplier
* @j: integer part of second multiplier
* @d: decimal part of second multiplier
*/
struct clk_aic32x4_pll_muldiv {
u8 p;
u16 r;
u8 j;
u16 d;
};
struct aic32x4_clkdesc {
const char *name;
const char * const *parent_names;
unsigned int num_parents;
const struct clk_ops *ops;
unsigned int reg;
};
static int clk_aic32x4_pll_prepare(struct clk_hw *hw)
{
struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
return regmap_update_bits(pll->regmap, AIC32X4_PLLPR,
AIC32X4_PLLEN, AIC32X4_PLLEN);
}
static void clk_aic32x4_pll_unprepare(struct clk_hw *hw)
{
struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
regmap_update_bits(pll->regmap, AIC32X4_PLLPR,
AIC32X4_PLLEN, 0);
}
static int clk_aic32x4_pll_is_prepared(struct clk_hw *hw)
{
struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
unsigned int val;
int ret;
ret = regmap_read(pll->regmap, AIC32X4_PLLPR, &val);
if (ret < 0)
return ret;
return !!(val & AIC32X4_PLLEN);
}
static int clk_aic32x4_pll_get_muldiv(struct clk_aic32x4 *pll,
struct clk_aic32x4_pll_muldiv *settings)
{
/* Change to use regmap_bulk_read? */
unsigned int val;
int ret;
ret = regmap_read(pll->regmap, AIC32X4_PLLPR, &val);
if (ret < 0)
return ret;
settings->r = val & AIC32X4_PLL_R_MASK;
settings->p = (val & AIC32X4_PLL_P_MASK) >> AIC32X4_PLL_P_SHIFT;
ret = regmap_read(pll->regmap, AIC32X4_PLLJ, &val);
if (ret < 0)
return ret;
settings->j = val;
ret = regmap_read(pll->regmap, AIC32X4_PLLDMSB, &val);
if (ret < 0)
return ret;
settings->d = val << 8;
ret = regmap_read(pll->regmap, AIC32X4_PLLDLSB, &val);
if (ret < 0)
return ret;
settings->d |= val;
return 0;
}
static int clk_aic32x4_pll_set_muldiv(struct clk_aic32x4 *pll,
struct clk_aic32x4_pll_muldiv *settings)
{
int ret;
/* Change to use regmap_bulk_write for some if not all? */
ret = regmap_update_bits(pll->regmap, AIC32X4_PLLPR,
AIC32X4_PLL_R_MASK, settings->r);
if (ret < 0)
return ret;
ret = regmap_update_bits(pll->regmap, AIC32X4_PLLPR,
AIC32X4_PLL_P_MASK,
settings->p << AIC32X4_PLL_P_SHIFT);
if (ret < 0)
return ret;
ret = regmap_write(pll->regmap, AIC32X4_PLLJ, settings->j);
if (ret < 0)
return ret;
ret = regmap_write(pll->regmap, AIC32X4_PLLDMSB, (settings->d >> 8));
if (ret < 0)
return ret;
ret = regmap_write(pll->regmap, AIC32X4_PLLDLSB, (settings->d & 0xff));
if (ret < 0)
return ret;
return 0;
}
static unsigned long clk_aic32x4_pll_calc_rate(
struct clk_aic32x4_pll_muldiv *settings,
unsigned long parent_rate)
{
u64 rate;
/*
* We scale j by 10000 to account for the decimal part of P and divide
* it back out later.
*/
rate = (u64) parent_rate * settings->r *
((settings->j * 10000) + settings->d);
return (unsigned long) DIV_ROUND_UP_ULL(rate, settings->p * 10000);
}
static int clk_aic32x4_pll_calc_muldiv(struct clk_aic32x4_pll_muldiv *settings,
unsigned long rate, unsigned long parent_rate)
{
u64 multiplier;
settings->p = parent_rate / AIC32X4_MAX_PLL_CLKIN + 1;
if (settings->p > 8)
return -1;
/*
* We scale this figure by 10000 so that we can get the decimal part
* of the multiplier. This is because we can't do floating point
* math in the kernel.
*/
multiplier = (u64) rate * settings->p * 10000;
do_div(multiplier, parent_rate);
/*
* J can't be over 64, so R can scale this.
* R can't be greater than 4.
*/
settings->r = ((u32) multiplier / 640000) + 1;
if (settings->r > 4)
return -1;
do_div(multiplier, settings->r);
/*
* J can't be < 1.
*/
if (multiplier < 10000)
return -1;
/* Figure out the integer part, J, and the fractional part, D. */
settings->j = (u32) multiplier / 10000;
settings->d = (u32) multiplier % 10000;
return 0;
}
static unsigned long clk_aic32x4_pll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
struct clk_aic32x4_pll_muldiv settings;
int ret;
ret = clk_aic32x4_pll_get_muldiv(pll, &settings);
if (ret < 0)
return 0;
return clk_aic32x4_pll_calc_rate(&settings, parent_rate);
}
static long clk_aic32x4_pll_round_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long *parent_rate)
{
struct clk_aic32x4_pll_muldiv settings;
int ret;
ret = clk_aic32x4_pll_calc_muldiv(&settings, rate, *parent_rate);
if (ret < 0)
return 0;
return clk_aic32x4_pll_calc_rate(&settings, *parent_rate);
}
static int clk_aic32x4_pll_set_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long parent_rate)
{
struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
struct clk_aic32x4_pll_muldiv settings;
int ret;
ret = clk_aic32x4_pll_calc_muldiv(&settings, rate, parent_rate);
if (ret < 0)
return -EINVAL;
return clk_aic32x4_pll_set_muldiv(pll, &settings);
}
static int clk_aic32x4_pll_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
return regmap_update_bits(pll->regmap,
AIC32X4_CLKMUX,
AIC32X4_PLL_CLKIN_MASK,
index << AIC32X4_PLL_CLKIN_SHIFT);
}
static u8 clk_aic32x4_pll_get_parent(struct clk_hw *hw)
{
struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
unsigned int val;
regmap_read(pll->regmap, AIC32X4_PLLPR, &val);
return (val & AIC32X4_PLL_CLKIN_MASK) >> AIC32X4_PLL_CLKIN_SHIFT;
}
static const struct clk_ops aic32x4_pll_ops = {
.prepare = clk_aic32x4_pll_prepare,
.unprepare = clk_aic32x4_pll_unprepare,
.is_prepared = clk_aic32x4_pll_is_prepared,
.recalc_rate = clk_aic32x4_pll_recalc_rate,
.round_rate = clk_aic32x4_pll_round_rate,
.set_rate = clk_aic32x4_pll_set_rate,
.set_parent = clk_aic32x4_pll_set_parent,
.get_parent = clk_aic32x4_pll_get_parent,
};
static int clk_aic32x4_codec_clkin_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_aic32x4 *mux = to_clk_aic32x4(hw);
return regmap_update_bits(mux->regmap,
AIC32X4_CLKMUX,
AIC32X4_CODEC_CLKIN_MASK, index << AIC32X4_CODEC_CLKIN_SHIFT);
}
static u8 clk_aic32x4_codec_clkin_get_parent(struct clk_hw *hw)
{
struct clk_aic32x4 *mux = to_clk_aic32x4(hw);
unsigned int val;
regmap_read(mux->regmap, AIC32X4_CLKMUX, &val);
return (val & AIC32X4_CODEC_CLKIN_MASK) >> AIC32X4_CODEC_CLKIN_SHIFT;
}
static const struct clk_ops aic32x4_codec_clkin_ops = {
.set_parent = clk_aic32x4_codec_clkin_set_parent,
.get_parent = clk_aic32x4_codec_clkin_get_parent,
};
static int clk_aic32x4_div_prepare(struct clk_hw *hw)
{
struct clk_aic32x4 *div = to_clk_aic32x4(hw);
return regmap_update_bits(div->regmap, div->reg,
AIC32X4_DIVEN, AIC32X4_DIVEN);
}
static void clk_aic32x4_div_unprepare(struct clk_hw *hw)
{
struct clk_aic32x4 *div = to_clk_aic32x4(hw);
regmap_update_bits(div->regmap, div->reg,
AIC32X4_DIVEN, 0);
}
static int clk_aic32x4_div_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_aic32x4 *div = to_clk_aic32x4(hw);
u8 divisor;
divisor = DIV_ROUND_UP(parent_rate, rate);
if (divisor > 128)
return -EINVAL;
return regmap_update_bits(div->regmap, div->reg,
AIC32X4_DIV_MASK, divisor);
}
static long clk_aic32x4_div_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
unsigned long divisor;
divisor = DIV_ROUND_UP(*parent_rate, rate);
if (divisor > 128)
return -EINVAL;
return DIV_ROUND_UP(*parent_rate, divisor);
}
static unsigned long clk_aic32x4_div_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_aic32x4 *div = to_clk_aic32x4(hw);
unsigned int val;
regmap_read(div->regmap, div->reg, &val);
return DIV_ROUND_UP(parent_rate, val & AIC32X4_DIV_MASK);
}
static const struct clk_ops aic32x4_div_ops = {
.prepare = clk_aic32x4_div_prepare,
.unprepare = clk_aic32x4_div_unprepare,
.set_rate = clk_aic32x4_div_set_rate,
.round_rate = clk_aic32x4_div_round_rate,
.recalc_rate = clk_aic32x4_div_recalc_rate,
};
static int clk_aic32x4_bdiv_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_aic32x4 *mux = to_clk_aic32x4(hw);
return regmap_update_bits(mux->regmap, AIC32X4_IFACE3,
AIC32X4_BDIVCLK_MASK, index);
}
static u8 clk_aic32x4_bdiv_get_parent(struct clk_hw *hw)
{
struct clk_aic32x4 *mux = to_clk_aic32x4(hw);
unsigned int val;
regmap_read(mux->regmap, AIC32X4_IFACE3, &val);
return val & AIC32X4_BDIVCLK_MASK;
}
static const struct clk_ops aic32x4_bdiv_ops = {
.prepare = clk_aic32x4_div_prepare,
.unprepare = clk_aic32x4_div_unprepare,
.set_parent = clk_aic32x4_bdiv_set_parent,
.get_parent = clk_aic32x4_bdiv_get_parent,
.set_rate = clk_aic32x4_div_set_rate,
.round_rate = clk_aic32x4_div_round_rate,
.recalc_rate = clk_aic32x4_div_recalc_rate,
};
static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = {
{
.name = "pll",
.parent_names =
(const char* []) { "mclk", "bclk", "gpio", "din" },
.num_parents = 4,
.ops = &aic32x4_pll_ops,
.reg = 0,
},
{
.name = "codec_clkin",
.parent_names =
(const char *[]) { "mclk", "bclk", "gpio", "pll" },
.num_parents = 4,
.ops = &aic32x4_codec_clkin_ops,
.reg = 0,
},
{
.name = "ndac",
.parent_names = (const char * []) { "codec_clkin" },
.num_parents = 1,
.ops = &aic32x4_div_ops,
.reg = AIC32X4_NDAC,
},
{
.name = "mdac",
.parent_names = (const char * []) { "ndac" },
.num_parents = 1,
.ops = &aic32x4_div_ops,
.reg = AIC32X4_MDAC,
},
{
.name = "nadc",
.parent_names = (const char * []) { "codec_clkin" },
.num_parents = 1,
.ops = &aic32x4_div_ops,
.reg = AIC32X4_NADC,
},
{
.name = "madc",
.parent_names = (const char * []) { "nadc" },
.num_parents = 1,
.ops = &aic32x4_div_ops,
.reg = AIC32X4_MADC,
},
{
.name = "bdiv",
.parent_names =
(const char *[]) { "ndac", "mdac", "nadc", "madc" },
.num_parents = 4,
.ops = &aic32x4_bdiv_ops,
.reg = AIC32X4_BCLKN,
},
};
static struct clk *aic32x4_register_clk(struct device *dev,
struct aic32x4_clkdesc *desc)
{
struct clk_init_data init;
struct clk_aic32x4 *priv;
const char *devname = dev_name(dev);
init.ops = desc->ops;
init.name = desc->name;
init.parent_names = desc->parent_names;
init.num_parents = desc->num_parents;
init.flags = 0;
priv = devm_kzalloc(dev, sizeof(struct clk_aic32x4), GFP_KERNEL);
if (priv == NULL)
return (struct clk *) -ENOMEM;
priv->dev = dev;
priv->hw.init = &init;
priv->regmap = dev_get_regmap(dev, NULL);
priv->reg = desc->reg;
clk_hw_register_clkdev(&priv->hw, desc->name, devname);
return devm_clk_register(dev, &priv->hw);
}
int aic32x4_register_clocks(struct device *dev, const char *mclk_name)
{
int i;
/*
* These lines are here to preserve the current functionality of
* the driver with regard to the DT. These should eventually be set
* by DT nodes so that the connections can be set up in configuration
* rather than code.
*/
aic32x4_clkdesc_array[0].parent_names =
(const char* []) { mclk_name, "bclk", "gpio", "din" };
aic32x4_clkdesc_array[1].parent_names =
(const char *[]) { mclk_name, "bclk", "gpio", "pll" };
for (i = 0; i < ARRAY_SIZE(aic32x4_clkdesc_array); ++i)
aic32x4_register_clk(dev, &aic32x4_clkdesc_array[i]);
return 0;
}
EXPORT_SYMBOL_GPL(aic32x4_register_clocks);

Ver ficheiro

@@ -1,21 +1,11 @@
/*
* linux/sound/soc/codecs/tlv320aic32x4-i2c.c
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright 2011 NW Digital Radio
* Copyright 2011-2019 NW Digital Radio
*
* Author: Annaliese McDermond <nh6z@nh6z.net>
*
* Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/i2c.h>

Ver ficheiro

@@ -1,21 +1,11 @@
/*
* linux/sound/soc/codecs/tlv320aic32x4-spi.c
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright 2011 NW Digital Radio
* Copyright 2011-2019 NW Digital Radio
*
* Author: Annaliese McDermond <nh6z@nh6z.net>
*
* Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/spi/spi.h>

Ver ficheiro

@@ -14,7 +14,7 @@
*
* 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
* 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
@@ -33,6 +33,7 @@
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/of_clk.h>
#include <linux/regulator/consumer.h>
#include <sound/tlv320aic32x4.h>
@@ -46,29 +47,13 @@
#include "tlv320aic32x4.h"
struct aic32x4_rate_divs {
u32 mclk;
u32 rate;
u8 p_val;
u8 pll_j;
u16 pll_d;
u16 dosr;
u8 ndac;
u8 mdac;
u8 aosr;
u8 nadc;
u8 madc;
u8 blck_N;
};
struct aic32x4_priv {
struct regmap *regmap;
u32 sysclk;
u32 power_cfg;
u32 micpga_routing;
bool swapdacs;
int rstn_gpio;
struct clk *mclk;
const char *mclk_name;
struct regulator *supply_ldo;
struct regulator *supply_iov;
@@ -257,9 +242,24 @@ static DECLARE_TLV_DB_SCALE(tlv_driver_gain, -600, 100, 0);
/* -12dB min, 0.5dB steps */
static DECLARE_TLV_DB_SCALE(tlv_adc_vol, -1200, 50, 0);
static const char * const lo_cm_text[] = {
"Full Chip", "1.65V",
};
static SOC_ENUM_SINGLE_DECL(lo_cm_enum, AIC32X4_CMMODE, 3, lo_cm_text);
static const char * const ptm_text[] = {
"P3", "P2", "P1",
};
static SOC_ENUM_SINGLE_DECL(l_ptm_enum, AIC32X4_LPLAYBACK, 2, ptm_text);
static SOC_ENUM_SINGLE_DECL(r_ptm_enum, AIC32X4_RPLAYBACK, 2, ptm_text);
static const struct snd_kcontrol_new aic32x4_snd_controls[] = {
SOC_DOUBLE_R_S_TLV("PCM Playback Volume", AIC32X4_LDACVOL,
AIC32X4_RDACVOL, 0, -0x7f, 0x30, 7, 0, tlv_pcm),
SOC_ENUM("DAC Left Playback PowerTune Switch", l_ptm_enum),
SOC_ENUM("DAC Right Playback PowerTune Switch", r_ptm_enum),
SOC_DOUBLE_R_S_TLV("HP Driver Gain Volume", AIC32X4_HPLGAIN,
AIC32X4_HPRGAIN, 0, -0x6, 0x1d, 5, 0,
tlv_driver_gain),
@@ -270,6 +270,7 @@ static const struct snd_kcontrol_new aic32x4_snd_controls[] = {
AIC32X4_HPRGAIN, 6, 0x01, 1),
SOC_DOUBLE_R("LO DAC Playback Switch", AIC32X4_LOLGAIN,
AIC32X4_LORGAIN, 6, 0x01, 1),
SOC_ENUM("LO Playback Common Mode Switch", lo_cm_enum),
SOC_DOUBLE_R("Mic PGA Switch", AIC32X4_LMICPGAVOL,
AIC32X4_RMICPGAVOL, 7, 0x01, 1),
@@ -305,38 +306,6 @@ static const struct snd_kcontrol_new aic32x4_snd_controls[] = {
0, 0x0F, 0),
};
static const struct aic32x4_rate_divs aic32x4_divs[] = {
/* 8k rate */
{12000000, 8000, 1, 7, 6800, 768, 5, 3, 128, 5, 18, 24},
{24000000, 8000, 2, 7, 6800, 768, 15, 1, 64, 45, 4, 24},
{25000000, 8000, 2, 7, 3728, 768, 15, 1, 64, 45, 4, 24},
/* 11.025k rate */
{12000000, 11025, 1, 7, 5264, 512, 8, 2, 128, 8, 8, 16},
{24000000, 11025, 2, 7, 5264, 512, 16, 1, 64, 32, 4, 16},
/* 16k rate */
{12000000, 16000, 1, 7, 6800, 384, 5, 3, 128, 5, 9, 12},
{24000000, 16000, 2, 7, 6800, 384, 15, 1, 64, 18, 5, 12},
{25000000, 16000, 2, 7, 3728, 384, 15, 1, 64, 18, 5, 12},
/* 22.05k rate */
{12000000, 22050, 1, 7, 5264, 256, 4, 4, 128, 4, 8, 8},
{24000000, 22050, 2, 7, 5264, 256, 16, 1, 64, 16, 4, 8},
{25000000, 22050, 2, 7, 2253, 256, 16, 1, 64, 16, 4, 8},
/* 32k rate */
{12000000, 32000, 1, 7, 1680, 192, 2, 7, 64, 2, 21, 6},
{24000000, 32000, 2, 7, 1680, 192, 7, 2, 64, 7, 6, 6},
/* 44.1k rate */
{12000000, 44100, 1, 7, 5264, 128, 2, 8, 128, 2, 8, 4},
{24000000, 44100, 2, 7, 5264, 128, 8, 2, 64, 8, 4, 4},
{25000000, 44100, 2, 7, 2253, 128, 8, 2, 64, 8, 4, 4},
/* 48k rate */
{12000000, 48000, 1, 8, 1920, 128, 2, 8, 128, 2, 8, 4},
{24000000, 48000, 2, 8, 1920, 128, 8, 2, 64, 8, 4, 4},
{25000000, 48000, 2, 7, 8643, 128, 8, 2, 64, 8, 4, 4},
/* 96k rate */
{25000000, 96000, 2, 7, 8643, 64, 4, 4, 64, 4, 4, 1},
};
static const struct snd_kcontrol_new hpl_output_mixer_controls[] = {
SOC_DAPM_SINGLE("L_DAC Switch", AIC32X4_HPLROUTE, 3, 1, 0),
SOC_DAPM_SINGLE("IN1_L Switch", AIC32X4_HPLROUTE, 2, 1, 0),
@@ -391,7 +360,7 @@ static const struct snd_kcontrol_new in3r_to_lmixer_controls[] = {
SOC_DAPM_ENUM("IN3_R L- Switch", in3r_lpga_n_enum),
};
/* Right mixer pins */
/* Right mixer pins */
static SOC_ENUM_SINGLE_DECL(in1r_rpga_p_enum, AIC32X4_RMICPGAPIN, 6, resistor_text);
static SOC_ENUM_SINGLE_DECL(in2r_rpga_p_enum, AIC32X4_RMICPGAPIN, 4, resistor_text);
static SOC_ENUM_SINGLE_DECL(in3r_rpga_p_enum, AIC32X4_RMICPGAPIN, 2, resistor_text);
@@ -595,7 +564,7 @@ static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = {
static const struct regmap_range_cfg aic32x4_regmap_pages[] = {
{
.selector_reg = 0,
.selector_mask = 0xff,
.selector_mask = 0xff,
.window_start = 0,
.window_len = 128,
.range_min = 0,
@@ -610,35 +579,17 @@ const struct regmap_config aic32x4_regmap_config = {
};
EXPORT_SYMBOL(aic32x4_regmap_config);
static inline int aic32x4_get_divs(int mclk, int rate)
{
int i;
for (i = 0; i < ARRAY_SIZE(aic32x4_divs); i++) {
if ((aic32x4_divs[i].rate == rate)
&& (aic32x4_divs[i].mclk == mclk)) {
return i;
}
}
printk(KERN_ERR "aic32x4: master clock and sample rate is not supported\n");
return -EINVAL;
}
static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_component *component = codec_dai->component;
struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component);
struct clk *mclk;
struct clk *pll;
switch (freq) {
case 12000000:
case 24000000:
case 25000000:
aic32x4->sysclk = freq;
return 0;
}
printk(KERN_ERR "aic32x4: invalid frequency to set DAI system clock\n");
return -EINVAL;
pll = devm_clk_get(component->dev, "pll");
mclk = clk_get_parent(pll);
return clk_set_rate(mclk, freq);
}
static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
@@ -688,103 +639,175 @@ static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
}
snd_soc_component_update_bits(component, AIC32X4_IFACE1,
AIC32X4_IFACE1_DATATYPE_MASK |
AIC32X4_IFACE1_MASTER_MASK, iface_reg_1);
AIC32X4_IFACE1_DATATYPE_MASK |
AIC32X4_IFACE1_MASTER_MASK, iface_reg_1);
snd_soc_component_update_bits(component, AIC32X4_IFACE2,
AIC32X4_DATA_OFFSET_MASK, iface_reg_2);
AIC32X4_DATA_OFFSET_MASK, iface_reg_2);
snd_soc_component_update_bits(component, AIC32X4_IFACE3,
AIC32X4_BCLKINV_MASK, iface_reg_3);
AIC32X4_BCLKINV_MASK, iface_reg_3);
return 0;
}
static int aic32x4_set_aosr(struct snd_soc_component *component, u8 aosr)
{
return snd_soc_component_write(component, AIC32X4_AOSR, aosr);
}
static int aic32x4_set_dosr(struct snd_soc_component *component, u16 dosr)
{
snd_soc_component_write(component, AIC32X4_DOSRMSB, dosr >> 8);
snd_soc_component_write(component, AIC32X4_DOSRLSB,
(dosr & 0xff));
return 0;
}
static int aic32x4_set_processing_blocks(struct snd_soc_component *component,
u8 r_block, u8 p_block)
{
if (r_block > 18 || p_block > 25)
return -EINVAL;
snd_soc_component_write(component, AIC32X4_ADCSPB, r_block);
snd_soc_component_write(component, AIC32X4_DACSPB, p_block);
return 0;
}
static int aic32x4_setup_clocks(struct snd_soc_component *component,
unsigned int sample_rate)
{
u8 aosr;
u16 dosr;
u8 adc_resource_class, dac_resource_class;
u8 madc, nadc, mdac, ndac, max_nadc, min_mdac, max_ndac;
u8 dosr_increment;
u16 max_dosr, min_dosr;
unsigned long adc_clock_rate, dac_clock_rate;
int ret;
struct clk_bulk_data clocks[] = {
{ .id = "pll" },
{ .id = "nadc" },
{ .id = "madc" },
{ .id = "ndac" },
{ .id = "mdac" },
{ .id = "bdiv" },
};
ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks);
if (ret)
return ret;
if (sample_rate <= 48000) {
aosr = 128;
adc_resource_class = 6;
dac_resource_class = 8;
dosr_increment = 8;
aic32x4_set_processing_blocks(component, 1, 1);
} else if (sample_rate <= 96000) {
aosr = 64;
adc_resource_class = 6;
dac_resource_class = 8;
dosr_increment = 4;
aic32x4_set_processing_blocks(component, 1, 9);
} else if (sample_rate == 192000) {
aosr = 32;
adc_resource_class = 3;
dac_resource_class = 4;
dosr_increment = 2;
aic32x4_set_processing_blocks(component, 13, 19);
} else {
dev_err(component->dev, "Sampling rate not supported\n");
return -EINVAL;
}
madc = DIV_ROUND_UP((32 * adc_resource_class), aosr);
max_dosr = (AIC32X4_MAX_DOSR_FREQ / sample_rate / dosr_increment) *
dosr_increment;
min_dosr = (AIC32X4_MIN_DOSR_FREQ / sample_rate / dosr_increment) *
dosr_increment;
max_nadc = AIC32X4_MAX_CODEC_CLKIN_FREQ / (madc * aosr * sample_rate);
for (nadc = max_nadc; nadc > 0; --nadc) {
adc_clock_rate = nadc * madc * aosr * sample_rate;
for (dosr = max_dosr; dosr >= min_dosr;
dosr -= dosr_increment) {
min_mdac = DIV_ROUND_UP((32 * dac_resource_class), dosr);
max_ndac = AIC32X4_MAX_CODEC_CLKIN_FREQ /
(min_mdac * dosr * sample_rate);
for (mdac = min_mdac; mdac <= 128; ++mdac) {
for (ndac = max_ndac; ndac > 0; --ndac) {
dac_clock_rate = ndac * mdac * dosr *
sample_rate;
if (dac_clock_rate == adc_clock_rate) {
if (clk_round_rate(clocks[0].clk, dac_clock_rate) == 0)
continue;
clk_set_rate(clocks[0].clk,
dac_clock_rate);
clk_set_rate(clocks[1].clk,
sample_rate * aosr *
madc);
clk_set_rate(clocks[2].clk,
sample_rate * aosr);
aic32x4_set_aosr(component,
aosr);
clk_set_rate(clocks[3].clk,
sample_rate * dosr *
mdac);
clk_set_rate(clocks[4].clk,
sample_rate * dosr);
aic32x4_set_dosr(component,
dosr);
clk_set_rate(clocks[5].clk,
sample_rate * 32);
return 0;
}
}
}
}
}
dev_err(component->dev,
"Could not set clocks to support sample rate.\n");
return -EINVAL;
}
static int aic32x4_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component);
u8 iface1_reg = 0;
u8 dacsetup_reg = 0;
int i;
i = aic32x4_get_divs(aic32x4->sysclk, params_rate(params));
if (i < 0) {
printk(KERN_ERR "aic32x4: sampling rate not supported\n");
return i;
}
/* MCLK as PLL_CLKIN */
snd_soc_component_update_bits(component, AIC32X4_CLKMUX, AIC32X4_PLL_CLKIN_MASK,
AIC32X4_PLL_CLKIN_MCLK << AIC32X4_PLL_CLKIN_SHIFT);
/* PLL as CODEC_CLKIN */
snd_soc_component_update_bits(component, AIC32X4_CLKMUX, AIC32X4_CODEC_CLKIN_MASK,
AIC32X4_CODEC_CLKIN_PLL << AIC32X4_CODEC_CLKIN_SHIFT);
/* DAC_MOD_CLK as BDIV_CLKIN */
snd_soc_component_update_bits(component, AIC32X4_IFACE3, AIC32X4_BDIVCLK_MASK,
AIC32X4_DACMOD2BCLK << AIC32X4_BDIVCLK_SHIFT);
/* We will fix R value to 1 and will make P & J=K.D as variable */
snd_soc_component_update_bits(component, AIC32X4_PLLPR, AIC32X4_PLL_R_MASK, 0x01);
/* PLL P value */
snd_soc_component_update_bits(component, AIC32X4_PLLPR, AIC32X4_PLL_P_MASK,
aic32x4_divs[i].p_val << AIC32X4_PLL_P_SHIFT);
/* PLL J value */
snd_soc_component_write(component, AIC32X4_PLLJ, aic32x4_divs[i].pll_j);
/* PLL D value */
snd_soc_component_write(component, AIC32X4_PLLDMSB, (aic32x4_divs[i].pll_d >> 8));
snd_soc_component_write(component, AIC32X4_PLLDLSB, (aic32x4_divs[i].pll_d & 0xff));
/* NDAC divider value */
snd_soc_component_update_bits(component, AIC32X4_NDAC,
AIC32X4_NDAC_MASK, aic32x4_divs[i].ndac);
/* MDAC divider value */
snd_soc_component_update_bits(component, AIC32X4_MDAC,
AIC32X4_MDAC_MASK, aic32x4_divs[i].mdac);
/* DOSR MSB & LSB values */
snd_soc_component_write(component, AIC32X4_DOSRMSB, aic32x4_divs[i].dosr >> 8);
snd_soc_component_write(component, AIC32X4_DOSRLSB, (aic32x4_divs[i].dosr & 0xff));
/* NADC divider value */
snd_soc_component_update_bits(component, AIC32X4_NADC,
AIC32X4_NADC_MASK, aic32x4_divs[i].nadc);
/* MADC divider value */
snd_soc_component_update_bits(component, AIC32X4_MADC,
AIC32X4_MADC_MASK, aic32x4_divs[i].madc);
/* AOSR value */
snd_soc_component_write(component, AIC32X4_AOSR, aic32x4_divs[i].aosr);
/* BCLK N divider */
snd_soc_component_update_bits(component, AIC32X4_BCLKN,
AIC32X4_BCLK_MASK, aic32x4_divs[i].blck_N);
aic32x4_setup_clocks(component, params_rate(params));
switch (params_width(params)) {
case 16:
iface1_reg |= (AIC32X4_WORD_LEN_16BITS <<
AIC32X4_IFACE1_DATALEN_SHIFT);
AIC32X4_IFACE1_DATALEN_SHIFT);
break;
case 20:
iface1_reg |= (AIC32X4_WORD_LEN_20BITS <<
AIC32X4_IFACE1_DATALEN_SHIFT);
AIC32X4_IFACE1_DATALEN_SHIFT);
break;
case 24:
iface1_reg |= (AIC32X4_WORD_LEN_24BITS <<
AIC32X4_IFACE1_DATALEN_SHIFT);
AIC32X4_IFACE1_DATALEN_SHIFT);
break;
case 32:
iface1_reg |= (AIC32X4_WORD_LEN_32BITS <<
AIC32X4_IFACE1_DATALEN_SHIFT);
AIC32X4_IFACE1_DATALEN_SHIFT);
break;
}
snd_soc_component_update_bits(component, AIC32X4_IFACE1,
AIC32X4_IFACE1_DATALEN_MASK, iface1_reg);
AIC32X4_IFACE1_DATALEN_MASK, iface1_reg);
if (params_channels(params) == 1) {
dacsetup_reg = AIC32X4_RDAC2LCHN | AIC32X4_LDAC2LCHN;
@@ -795,7 +818,7 @@ static int aic32x4_hw_params(struct snd_pcm_substream *substream,
dacsetup_reg = AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN;
}
snd_soc_component_update_bits(component, AIC32X4_DACSETUP,
AIC32X4_DAC_CHAN_MASK, dacsetup_reg);
AIC32X4_DAC_CHAN_MASK, dacsetup_reg);
return 0;
}
@@ -805,7 +828,7 @@ static int aic32x4_mute(struct snd_soc_dai *dai, int mute)
struct snd_soc_component *component = dai->component;
snd_soc_component_update_bits(component, AIC32X4_DACMUTE,
AIC32X4_MUTEON, mute ? AIC32X4_MUTEON : 0);
AIC32X4_MUTEON, mute ? AIC32X4_MUTEON : 0);
return 0;
}
@@ -813,41 +836,25 @@ static int aic32x4_mute(struct snd_soc_dai *dai, int mute)
static int aic32x4_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component);
int ret;
struct clk_bulk_data clocks[] = {
{ .id = "madc" },
{ .id = "mdac" },
{ .id = "bdiv" },
};
ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks);
if (ret)
return ret;
switch (level) {
case SND_SOC_BIAS_ON:
/* Switch on master clock */
ret = clk_prepare_enable(aic32x4->mclk);
ret = clk_bulk_prepare_enable(ARRAY_SIZE(clocks), clocks);
if (ret) {
dev_err(component->dev, "Failed to enable master clock\n");
dev_err(component->dev, "Failed to enable clocks\n");
return ret;
}
/* Switch on PLL */
snd_soc_component_update_bits(component, AIC32X4_PLLPR,
AIC32X4_PLLEN, AIC32X4_PLLEN);
/* Switch on NDAC Divider */
snd_soc_component_update_bits(component, AIC32X4_NDAC,
AIC32X4_NDACEN, AIC32X4_NDACEN);
/* Switch on MDAC Divider */
snd_soc_component_update_bits(component, AIC32X4_MDAC,
AIC32X4_MDACEN, AIC32X4_MDACEN);
/* Switch on NADC Divider */
snd_soc_component_update_bits(component, AIC32X4_NADC,
AIC32X4_NADCEN, AIC32X4_NADCEN);
/* Switch on MADC Divider */
snd_soc_component_update_bits(component, AIC32X4_MADC,
AIC32X4_MADCEN, AIC32X4_MADCEN);
/* Switch on BCLK_N Divider */
snd_soc_component_update_bits(component, AIC32X4_BCLKN,
AIC32X4_BCLKEN, AIC32X4_BCLKEN);
break;
case SND_SOC_BIAS_PREPARE:
break;
@@ -856,32 +863,7 @@ static int aic32x4_set_bias_level(struct snd_soc_component *component,
if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
break;
/* Switch off BCLK_N Divider */
snd_soc_component_update_bits(component, AIC32X4_BCLKN,
AIC32X4_BCLKEN, 0);
/* Switch off MADC Divider */
snd_soc_component_update_bits(component, AIC32X4_MADC,
AIC32X4_MADCEN, 0);
/* Switch off NADC Divider */
snd_soc_component_update_bits(component, AIC32X4_NADC,
AIC32X4_NADCEN, 0);
/* Switch off MDAC Divider */
snd_soc_component_update_bits(component, AIC32X4_MDAC,
AIC32X4_MDACEN, 0);
/* Switch off NDAC Divider */
snd_soc_component_update_bits(component, AIC32X4_NDAC,
AIC32X4_NDACEN, 0);
/* Switch off PLL */
snd_soc_component_update_bits(component, AIC32X4_PLLPR,
AIC32X4_PLLEN, 0);
/* Switch off master clock */
clk_disable_unprepare(aic32x4->mclk);
clk_bulk_disable_unprepare(ARRAY_SIZE(clocks), clocks);
break;
case SND_SOC_BIAS_OFF:
break;
@@ -889,8 +871,8 @@ static int aic32x4_set_bias_level(struct snd_soc_component *component,
return 0;
}
#define AIC32X4_RATES SNDRV_PCM_RATE_8000_96000
#define AIC32X4_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
#define AIC32X4_RATES SNDRV_PCM_RATE_8000_192000
#define AIC32X4_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
| SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
static const struct snd_soc_dai_ops aic32x4_ops = {
@@ -903,17 +885,17 @@ static const struct snd_soc_dai_ops aic32x4_ops = {
static struct snd_soc_dai_driver aic32x4_dai = {
.name = "tlv320aic32x4-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = AIC32X4_RATES,
.formats = AIC32X4_FORMATS,},
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = AIC32X4_RATES,
.formats = AIC32X4_FORMATS,},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = AIC32X4_RATES,
.formats = AIC32X4_FORMATS,},
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = AIC32X4_RATES,
.formats = AIC32X4_FORMATS,},
.ops = &aic32x4_ops,
.symmetric_rates = 1,
};
@@ -926,7 +908,7 @@ static void aic32x4_setup_gpios(struct snd_soc_component *component)
/* MFP1 */
if (aic32x4->setup->gpio_func[0] != AIC32X4_MFPX_DEFAULT_VALUE) {
snd_soc_component_write(component, AIC32X4_DINCTL,
aic32x4->setup->gpio_func[0]);
aic32x4->setup->gpio_func[0]);
snd_soc_add_component_controls(component, aic32x4_mfp1,
ARRAY_SIZE(aic32x4_mfp1));
}
@@ -934,7 +916,7 @@ static void aic32x4_setup_gpios(struct snd_soc_component *component)
/* MFP2 */
if (aic32x4->setup->gpio_func[1] != AIC32X4_MFPX_DEFAULT_VALUE) {
snd_soc_component_write(component, AIC32X4_DOUTCTL,
aic32x4->setup->gpio_func[1]);
aic32x4->setup->gpio_func[1]);
snd_soc_add_component_controls(component, aic32x4_mfp2,
ARRAY_SIZE(aic32x4_mfp2));
}
@@ -942,7 +924,7 @@ static void aic32x4_setup_gpios(struct snd_soc_component *component)
/* MFP3 */
if (aic32x4->setup->gpio_func[2] != AIC32X4_MFPX_DEFAULT_VALUE) {
snd_soc_component_write(component, AIC32X4_SCLKCTL,
aic32x4->setup->gpio_func[2]);
aic32x4->setup->gpio_func[2]);
snd_soc_add_component_controls(component, aic32x4_mfp3,
ARRAY_SIZE(aic32x4_mfp3));
}
@@ -950,7 +932,7 @@ static void aic32x4_setup_gpios(struct snd_soc_component *component)
/* MFP4 */
if (aic32x4->setup->gpio_func[3] != AIC32X4_MFPX_DEFAULT_VALUE) {
snd_soc_component_write(component, AIC32X4_MISOCTL,
aic32x4->setup->gpio_func[3]);
aic32x4->setup->gpio_func[3]);
snd_soc_add_component_controls(component, aic32x4_mfp4,
ARRAY_SIZE(aic32x4_mfp4));
}
@@ -958,7 +940,7 @@ static void aic32x4_setup_gpios(struct snd_soc_component *component)
/* MFP5 */
if (aic32x4->setup->gpio_func[4] != AIC32X4_MFPX_DEFAULT_VALUE) {
snd_soc_component_write(component, AIC32X4_GPIOCTL,
aic32x4->setup->gpio_func[4]);
aic32x4->setup->gpio_func[4]);
snd_soc_add_component_controls(component, aic32x4_mfp5,
ARRAY_SIZE(aic32x4_mfp5));
}
@@ -968,6 +950,18 @@ static int aic32x4_component_probe(struct snd_soc_component *component)
{
struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component);
u32 tmp_reg;
int ret;
struct clk_bulk_data clocks[] = {
{ .id = "codec_clkin" },
{ .id = "pll" },
{ .id = "bdiv" },
{ .id = "mdac" },
};
ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks);
if (ret)
return ret;
if (gpio_is_valid(aic32x4->rstn_gpio)) {
ndelay(10);
@@ -980,10 +974,13 @@ static int aic32x4_component_probe(struct snd_soc_component *component)
if (aic32x4->setup)
aic32x4_setup_gpios(component);
clk_set_parent(clocks[0].clk, clocks[1].clk);
clk_set_parent(clocks[2].clk, clocks[3].clk);
/* Power platform configuration */
if (aic32x4->power_cfg & AIC32X4_PWR_MICBIAS_2075_LDOIN) {
snd_soc_component_write(component, AIC32X4_MICBIAS, AIC32X4_MICBIAS_LDOIN |
AIC32X4_MICBIAS_2075V);
snd_soc_component_write(component, AIC32X4_MICBIAS,
AIC32X4_MICBIAS_LDOIN | AIC32X4_MICBIAS_2075V);
}
if (aic32x4->power_cfg & AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE)
snd_soc_component_write(component, AIC32X4_PWRCFG, AIC32X4_AVDDWEAKDISABLE);
@@ -1046,12 +1043,18 @@ static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4,
struct device_node *np)
{
struct aic32x4_setup_data *aic32x4_setup;
int ret;
aic32x4_setup = devm_kzalloc(aic32x4->dev, sizeof(*aic32x4_setup),
GFP_KERNEL);
if (!aic32x4_setup)
return -ENOMEM;
ret = of_property_match_string(np, "clock-names", "mclk");
if (ret < 0)
return -EINVAL;
aic32x4->mclk_name = of_clk_get_parent_name(np, ret);
aic32x4->swapdacs = false;
aic32x4->micpga_routing = 0;
aic32x4->rstn_gpio = of_get_named_gpio(np, "reset-gpios", 0);
@@ -1173,7 +1176,7 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap)
return PTR_ERR(regmap);
aic32x4 = devm_kzalloc(dev, sizeof(struct aic32x4_priv),
GFP_KERNEL);
GFP_KERNEL);
if (aic32x4 == NULL)
return -ENOMEM;
@@ -1185,6 +1188,7 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap)
aic32x4->swapdacs = pdata->swapdacs;
aic32x4->micpga_routing = pdata->micpga_routing;
aic32x4->rstn_gpio = pdata->rstn_gpio;
aic32x4->mclk_name = "mclk";
} else if (np) {
ret = aic32x4_parse_dt(aic32x4, np);
if (ret) {
@@ -1196,13 +1200,12 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap)
aic32x4->swapdacs = false;
aic32x4->micpga_routing = 0;
aic32x4->rstn_gpio = -1;
aic32x4->mclk_name = "mclk";
}
aic32x4->mclk = devm_clk_get(dev, "mclk");
if (IS_ERR(aic32x4->mclk)) {
dev_err(dev, "Failed getting the mclk. The current implementation does not support the usage of this codec without mclk\n");
return PTR_ERR(aic32x4->mclk);
}
ret = aic32x4_register_clocks(dev, aic32x4->mclk_name);
if (ret)
return ret;
if (gpio_is_valid(aic32x4->rstn_gpio)) {
ret = devm_gpio_request_one(dev, aic32x4->rstn_gpio,

Ver ficheiro

@@ -16,6 +16,7 @@ struct regmap_config;
extern const struct regmap_config aic32x4_regmap_config;
int aic32x4_probe(struct device *dev, struct regmap *regmap);
int aic32x4_remove(struct device *dev);
int aic32x4_register_clocks(struct device *dev, const char *mclk_name);
/* tlv320aic32x4 register space (in decimal to match datasheet) */
@@ -77,6 +78,8 @@ int aic32x4_remove(struct device *dev);
#define AIC32X4_PWRCFG AIC32X4_REG(1, 1)
#define AIC32X4_LDOCTL AIC32X4_REG(1, 2)
#define AIC32X4_LPLAYBACK AIC32X4_REG(1, 3)
#define AIC32X4_RPLAYBACK AIC32X4_REG(1, 4)
#define AIC32X4_OUTPWRCTL AIC32X4_REG(1, 9)
#define AIC32X4_CMMODE AIC32X4_REG(1, 10)
#define AIC32X4_HPLROUTE AIC32X4_REG(1, 12)
@@ -205,4 +208,14 @@ int aic32x4_remove(struct device *dev);
#define AIC32X4_RMICPGANIN_IN1L_10K 0x10
#define AIC32X4_RMICPGANIN_CM1R_10K 0x40
/* Common mask and enable for all of the dividers */
#define AIC32X4_DIVEN BIT(7)
#define AIC32X4_DIV_MASK GENMASK(6, 0)
/* Clock Limits */
#define AIC32X4_MAX_DOSR_FREQ 6200000
#define AIC32X4_MIN_DOSR_FREQ 2800000
#define AIC32X4_MAX_CODEC_CLKIN_FREQ 110000000
#define AIC32X4_MAX_PLL_CLKIN 20000000
#endif /* _TLV320AIC32X4_H */

Ver ficheiro

@@ -5188,6 +5188,7 @@ static int wcd9335_slim_status(struct slim_device *sdev,
wcd->slim = sdev;
wcd->slim_ifc_dev = of_slim_get_device(sdev->ctrl, ifc_dev_np);
of_node_put(ifc_dev_np);
if (!wcd->slim_ifc_dev) {
dev_err(dev, "Unable to get SLIM Interface device\n");
return -EINVAL;

Ver ficheiro

@@ -646,6 +646,8 @@ static int wm5102_adsp_power_ev(struct snd_soc_dapm_widget *w,
return ret;
}
}
wm_adsp2_set_dspclk(w, v);
break;
case SND_SOC_DAPM_POST_PMD:
@@ -659,7 +661,7 @@ static int wm5102_adsp_power_ev(struct snd_soc_dapm_widget *w,
break;
}
return wm_adsp2_early_event(w, kcontrol, event, v);
return wm_adsp_early_event(w, kcontrol, event);
}
static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol,

Ver ficheiro

@@ -211,7 +211,9 @@ static int wm5110_adsp_power_ev(struct snd_soc_dapm_widget *w,
v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT;
return wm_adsp2_early_event(w, kcontrol, event, v);
wm_adsp2_set_dspclk(w, v);
return wm_adsp_early_event(w, kcontrol, event);
}
static const struct reg_sequence wm5110_no_dre_left_enable[] = {

A apresentação das diferenças no ficheiro foi suprimida por ser demasiado grande Carregar diff

Ver ficheiro

@@ -54,6 +54,7 @@ struct wm_adsp_alg_region {
struct wm_adsp_compr;
struct wm_adsp_compr_buf;
struct wm_adsp_ops;
struct wm_adsp {
const char *part;
@@ -66,7 +67,10 @@ struct wm_adsp {
struct regmap *regmap;
struct snd_soc_component *component;
struct wm_adsp_ops *ops;
unsigned int base;
unsigned int base_sysinfo;
unsigned int sysclk_reg;
unsigned int sysclk_mask;
unsigned int sysclk_shift;
@@ -75,6 +79,7 @@ struct wm_adsp {
unsigned int fw_id;
unsigned int fw_id_version;
unsigned int fw_vendor_id;
const struct wm_adsp_region *mem;
int num_mems;
@@ -106,6 +111,32 @@ struct wm_adsp {
};
struct wm_adsp_ops {
unsigned int sys_config_size;
bool (*validate_version)(struct wm_adsp *dsp, unsigned int version);
unsigned int (*parse_sizes)(struct wm_adsp *dsp,
const char * const file,
unsigned int pos,
const struct firmware *firmware);
int (*setup_algs)(struct wm_adsp *dsp);
unsigned int (*region_to_reg)(struct wm_adsp_region const *mem,
unsigned int offset);
void (*show_fw_status)(struct wm_adsp *dsp);
void (*stop_watchdog)(struct wm_adsp *dsp);
int (*enable_memory)(struct wm_adsp *dsp);
void (*disable_memory)(struct wm_adsp *dsp);
int (*lock_memory)(struct wm_adsp *dsp, unsigned int lock_regions);
int (*enable_core)(struct wm_adsp *dsp);
void (*disable_core)(struct wm_adsp *dsp);
int (*start_core)(struct wm_adsp *dsp);
void (*stop_core)(struct wm_adsp *dsp);
};
#define WM_ADSP1(wname, num) \
SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \
wm_adsp1_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
@@ -121,7 +152,7 @@ struct wm_adsp {
.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD, \
.subseq = 100, /* Ensure we run after SYSCLK supply widget */ }, \
{ .id = snd_soc_dapm_out_drv, .name = wname, \
.reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_event, \
.reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp_event, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
#define WM_ADSP_FW_CONTROL(dspname, num) \
@@ -135,17 +166,22 @@ int wm_adsp2_init(struct wm_adsp *dsp);
void wm_adsp2_remove(struct wm_adsp *dsp);
int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *component);
int wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component);
int wm_halo_init(struct wm_adsp *dsp);
int wm_adsp1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event,
unsigned int freq);
int wm_adsp2_lock(struct wm_adsp *adsp, unsigned int regions);
int wm_adsp_early_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
irqreturn_t wm_adsp2_bus_error(struct wm_adsp *adsp);
irqreturn_t wm_halo_bus_error(struct wm_adsp *dsp);
irqreturn_t wm_halo_wdt_expire(int irq, void *data);
int wm_adsp2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
int wm_adsp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
int wm_adsp2_set_dspclk(struct snd_soc_dapm_widget *w, unsigned int freq);
int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);

Ver ficheiro

@@ -73,6 +73,14 @@ struct wmfw_id_hdr {
__be32 ver;
} __packed;
struct wmfw_v3_id_hdr {
__be32 core_id;
__be32 block_rev;
__be32 vendor_id;
__be32 id;
__be32 ver;
} __packed;
struct wmfw_adsp1_id_hdr {
struct wmfw_id_hdr fw;
__be32 zm;
@@ -88,6 +96,15 @@ struct wmfw_adsp2_id_hdr {
__be32 n_algs;
} __packed;
struct wmfw_halo_id_hdr {
struct wmfw_v3_id_hdr fw;
__be32 xm_base;
__be32 xm_size;
__be32 ym_base;
__be32 ym_size;
__be32 n_algs;
} __packed;
struct wmfw_alg_hdr {
__be32 id;
__be32 ver;
@@ -106,6 +123,14 @@ struct wmfw_adsp2_alg_hdr {
__be32 ym;
} __packed;
struct wmfw_halo_alg_hdr {
struct wmfw_alg_hdr alg;
__be32 xm_base;
__be32 xm_size;
__be32 ym_base;
__be32 ym_size;
} __packed;
struct wmfw_adsp_alg_data {
__le32 id;
u8 name[WMFW_MAX_ALG_NAME];
@@ -154,6 +179,7 @@ struct wmfw_coeff_item {
#define WMFW_ADSP1 1
#define WMFW_ADSP2 2
#define WMFW_HALO 4
#define WMFW_ABSOLUTE 0xf0
#define WMFW_ALGORITHM_DATA 0xf2
@@ -169,4 +195,8 @@ struct wmfw_coeff_item {
#define WMFW_ADSP2_XM 5
#define WMFW_ADSP2_YM 6
#define WMFW_HALO_PM_PACKED 0x10
#define WMFW_HALO_XM_PACKED 0x11
#define WMFW_HALO_YM_PACKED 0x12
#endif

Ver ficheiro

@@ -24,6 +24,13 @@ config SND_SOC_FSL_SAI
This option is only useful for out-of-tree drivers since
in-tree drivers select it automatically.
config SND_SOC_FSL_AUDMIX
tristate "Audio Mixer (AUDMIX) module support"
select REGMAP_MMIO
help
Say Y if you want to add Audio Mixer (AUDMIX)
support for the NXP iMX CPUs.
config SND_SOC_FSL_SSI
tristate "Synchronous Serial Interface module (SSI) support"
select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
@@ -182,16 +189,17 @@ config SND_MPC52xx_SOC_EFIKA
endif # SND_POWERPC_SOC
config SND_SOC_IMX_PCM_FIQ
tristate
default y if SND_SOC_IMX_SSI=y && (SND_SOC_FSL_SSI=m || SND_SOC_FSL_SPDIF=m) && (MXC_TZIC || MXC_AVIC)
select FIQ
if SND_IMX_SOC
config SND_SOC_IMX_SSI
tristate
select SND_SOC_FSL_UTILS
config SND_SOC_IMX_PCM_FIQ
tristate
select FIQ
comment "SoC Audio support for Freescale i.MX boards:"
config SND_MXC_SOC_WM1133_EV1
@@ -296,6 +304,15 @@ config SND_SOC_FSL_ASOC_CARD
CS4271, CS4272 and SGTL5000.
Say Y if you want to add support for Freescale Generic ASoC Sound Card.
config SND_SOC_IMX_AUDMIX
tristate "SoC Audio support for i.MX boards with AUDMIX"
select SND_SOC_FSL_AUDMIX
select SND_SOC_FSL_SAI
help
SoC Audio support for i.MX boards with Audio Mixer
Say Y if you want to add support for SoC audio on an i.MX board with
an Audio Mixer.
endif # SND_IMX_SOC
endmenu

Ver ficheiro

@@ -12,6 +12,7 @@ snd-soc-p1022-rdk-objs := p1022_rdk.o
obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
# Freescale SSI/DMA/SAI/SPDIF Support
snd-soc-fsl-audmix-objs := fsl_audmix.o
snd-soc-fsl-asoc-card-objs := fsl-asoc-card.o
snd-soc-fsl-asrc-objs := fsl_asrc.o fsl_asrc_dma.o
snd-soc-fsl-sai-objs := fsl_sai.o
@@ -22,6 +23,8 @@ snd-soc-fsl-esai-objs := fsl_esai.o
snd-soc-fsl-micfil-objs := fsl_micfil.o
snd-soc-fsl-utils-objs := fsl_utils.o
snd-soc-fsl-dma-objs := fsl_dma.o
obj-$(CONFIG_SND_SOC_FSL_AUDMIX) += snd-soc-fsl-audmix.o
obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o
obj-$(CONFIG_SND_SOC_FSL_ASRC) += snd-soc-fsl-asrc.o
obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o
@@ -59,6 +62,7 @@ snd-soc-imx-es8328-objs := imx-es8328.o
snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o
snd-soc-imx-spdif-objs := imx-spdif.o
snd-soc-imx-mc13783-objs := imx-mc13783.o
snd-soc-imx-audmix-objs := imx-audmix.o
obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
@@ -68,3 +72,4 @@ obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o
obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o
obj-$(CONFIG_SND_SOC_IMX_MC13783) += snd-soc-imx-mc13783.o
obj-$(CONFIG_SND_SOC_IMX_AUDMIX) += snd-soc-imx-audmix.o

Ver ficheiro

@@ -1,19 +1,13 @@
/*
* eukrea-tlv320.c -- SoC audio for eukrea_cpuimxXX in I2S mode
*
* Copyright 2010 Eric Bénard, Eukréa Electromatique <eric@eukrea.com>
*
* based on sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c
* which is Copyright 2009 Simtec Electronics
* and on sound/soc/imx/phycore-ac97.c which is
* Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
*
* 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.
*
*/
// SPDX-License-Identifier: GPL-2.0+
//
// eukrea-tlv320.c -- SoC audio for eukrea_cpuimxXX in I2S mode
//
// Copyright 2010 Eric Bénard, Eukréa Electromatique <eric@eukrea.com>
//
// based on sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c
// which is Copyright 2009 Simtec Electronics
// and on sound/soc/imx/phycore-ac97.c which is
// Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
#include <linux/errno.h>
#include <linux/module.h>
@@ -118,13 +112,13 @@ static int eukrea_tlv320_probe(struct platform_device *pdev)
if (ret) {
dev_err(&pdev->dev,
"fsl,mux-int-port node missing or invalid.\n");
return ret;
goto err;
}
ret = of_property_read_u32(np, "fsl,mux-ext-port", &ext_port);
if (ret) {
dev_err(&pdev->dev,
"fsl,mux-ext-port node missing or invalid.\n");
return ret;
goto err;
}
/*

578
sound/soc/fsl/fsl_audmix.c Ficheiro normal
Ver ficheiro

@@ -0,0 +1,578 @@
// SPDX-License-Identifier: GPL-2.0
/*
* NXP AUDMIX ALSA SoC Digital Audio Interface (DAI) driver
*
* Copyright 2017 NXP
*/
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/pm_runtime.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
#include "fsl_audmix.h"
#define SOC_ENUM_SINGLE_S(xreg, xshift, xtexts) \
SOC_ENUM_SINGLE(xreg, xshift, ARRAY_SIZE(xtexts), xtexts)
static const char
*tdm_sel[] = { "TDM1", "TDM2", },
*mode_sel[] = { "Disabled", "TDM1", "TDM2", "Mixed", },
*width_sel[] = { "16b", "18b", "20b", "24b", "32b", },
*endis_sel[] = { "Disabled", "Enabled", },
*updn_sel[] = { "Downward", "Upward", },
*mask_sel[] = { "Unmask", "Mask", };
static const struct soc_enum fsl_audmix_enum[] = {
/* FSL_AUDMIX_CTR enums */
SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_MIXCLK_SHIFT, tdm_sel),
SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_OUTSRC_SHIFT, mode_sel),
SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_OUTWIDTH_SHIFT, width_sel),
SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_MASKRTDF_SHIFT, mask_sel),
SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_MASKCKDF_SHIFT, mask_sel),
SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_SYNCMODE_SHIFT, endis_sel),
SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_SYNCSRC_SHIFT, tdm_sel),
/* FSL_AUDMIX_ATCR0 enums */
SOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR0, 0, endis_sel),
SOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR0, 1, updn_sel),
/* FSL_AUDMIX_ATCR1 enums */
SOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR1, 0, endis_sel),
SOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR1, 1, updn_sel),
};
struct fsl_audmix_state {
u8 tdms;
u8 clk;
char msg[64];
};
static const struct fsl_audmix_state prms[4][4] = {{
/* DIS->DIS, do nothing */
{ .tdms = 0, .clk = 0, .msg = "" },
/* DIS->TDM1*/
{ .tdms = 1, .clk = 1, .msg = "DIS->TDM1: TDM1 not started!\n" },
/* DIS->TDM2*/
{ .tdms = 2, .clk = 2, .msg = "DIS->TDM2: TDM2 not started!\n" },
/* DIS->MIX */
{ .tdms = 3, .clk = 0, .msg = "DIS->MIX: Please start both TDMs!\n" }
}, { /* TDM1->DIS */
{ .tdms = 1, .clk = 0, .msg = "TDM1->DIS: TDM1 not started!\n" },
/* TDM1->TDM1, do nothing */
{ .tdms = 0, .clk = 0, .msg = "" },
/* TDM1->TDM2 */
{ .tdms = 3, .clk = 2, .msg = "TDM1->TDM2: Please start both TDMs!\n" },
/* TDM1->MIX */
{ .tdms = 3, .clk = 0, .msg = "TDM1->MIX: Please start both TDMs!\n" }
}, { /* TDM2->DIS */
{ .tdms = 2, .clk = 0, .msg = "TDM2->DIS: TDM2 not started!\n" },
/* TDM2->TDM1 */
{ .tdms = 3, .clk = 1, .msg = "TDM2->TDM1: Please start both TDMs!\n" },
/* TDM2->TDM2, do nothing */
{ .tdms = 0, .clk = 0, .msg = "" },
/* TDM2->MIX */
{ .tdms = 3, .clk = 0, .msg = "TDM2->MIX: Please start both TDMs!\n" }
}, { /* MIX->DIS */
{ .tdms = 3, .clk = 0, .msg = "MIX->DIS: Please start both TDMs!\n" },
/* MIX->TDM1 */
{ .tdms = 3, .clk = 1, .msg = "MIX->TDM1: Please start both TDMs!\n" },
/* MIX->TDM2 */
{ .tdms = 3, .clk = 2, .msg = "MIX->TDM2: Please start both TDMs!\n" },
/* MIX->MIX, do nothing */
{ .tdms = 0, .clk = 0, .msg = "" }
}, };
static int fsl_audmix_state_trans(struct snd_soc_component *comp,
unsigned int *mask, unsigned int *ctr,
const struct fsl_audmix_state prm)
{
struct fsl_audmix *priv = snd_soc_component_get_drvdata(comp);
/* Enforce all required TDMs are started */
if ((priv->tdms & prm.tdms) != prm.tdms) {
dev_dbg(comp->dev, "%s", prm.msg);
return -EINVAL;
}
switch (prm.clk) {
case 1:
case 2:
/* Set mix clock */
(*mask) |= FSL_AUDMIX_CTR_MIXCLK_MASK;
(*ctr) |= FSL_AUDMIX_CTR_MIXCLK(prm.clk - 1);
break;
default:
break;
}
return 0;
}
static int fsl_audmix_put_mix_clk_src(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
struct fsl_audmix *priv = snd_soc_component_get_drvdata(comp);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int *item = ucontrol->value.enumerated.item;
unsigned int reg_val, val, mix_clk;
int ret = 0;
/* Get current state */
ret = snd_soc_component_read(comp, FSL_AUDMIX_CTR, &reg_val);
if (ret)
return ret;
mix_clk = ((reg_val & FSL_AUDMIX_CTR_MIXCLK_MASK)
>> FSL_AUDMIX_CTR_MIXCLK_SHIFT);
val = snd_soc_enum_item_to_val(e, item[0]);
dev_dbg(comp->dev, "TDMs=x%08x, val=x%08x\n", priv->tdms, val);
/**
* Ensure the current selected mixer clock is available
* for configuration propagation
*/
if (!(priv->tdms & BIT(mix_clk))) {
dev_err(comp->dev,
"Started TDM%d needed for config propagation!\n",
mix_clk + 1);
return -EINVAL;
}
if (!(priv->tdms & BIT(val))) {
dev_err(comp->dev,
"The selected clock source has no TDM%d enabled!\n",
val + 1);
return -EINVAL;
}
return snd_soc_put_enum_double(kcontrol, ucontrol);
}
static int fsl_audmix_put_out_src(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
struct fsl_audmix *priv = snd_soc_component_get_drvdata(comp);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int *item = ucontrol->value.enumerated.item;
u32 out_src, mix_clk;
unsigned int reg_val, val, mask = 0, ctr = 0;
int ret = 0;
/* Get current state */
ret = snd_soc_component_read(comp, FSL_AUDMIX_CTR, &reg_val);
if (ret)
return ret;
/* "From" state */
out_src = ((reg_val & FSL_AUDMIX_CTR_OUTSRC_MASK)
>> FSL_AUDMIX_CTR_OUTSRC_SHIFT);
mix_clk = ((reg_val & FSL_AUDMIX_CTR_MIXCLK_MASK)
>> FSL_AUDMIX_CTR_MIXCLK_SHIFT);
/* "To" state */
val = snd_soc_enum_item_to_val(e, item[0]);
dev_dbg(comp->dev, "TDMs=x%08x, val=x%08x\n", priv->tdms, val);
/* Check if state is changing ... */
if (out_src == val)
return 0;
/**
* Ensure the current selected mixer clock is available
* for configuration propagation
*/
if (!(priv->tdms & BIT(mix_clk))) {
dev_err(comp->dev,
"Started TDM%d needed for config propagation!\n",
mix_clk + 1);
return -EINVAL;
}
/* Check state transition constraints */
ret = fsl_audmix_state_trans(comp, &mask, &ctr, prms[out_src][val]);
if (ret)
return ret;
/* Complete transition to new state */
mask |= FSL_AUDMIX_CTR_OUTSRC_MASK;
ctr |= FSL_AUDMIX_CTR_OUTSRC(val);
return snd_soc_component_update_bits(comp, FSL_AUDMIX_CTR, mask, ctr);
}
static const struct snd_kcontrol_new fsl_audmix_snd_controls[] = {
/* FSL_AUDMIX_CTR controls */
SOC_ENUM_EXT("Mixing Clock Source", fsl_audmix_enum[0],
snd_soc_get_enum_double, fsl_audmix_put_mix_clk_src),
SOC_ENUM_EXT("Output Source", fsl_audmix_enum[1],
snd_soc_get_enum_double, fsl_audmix_put_out_src),
SOC_ENUM("Output Width", fsl_audmix_enum[2]),
SOC_ENUM("Frame Rate Diff Error", fsl_audmix_enum[3]),
SOC_ENUM("Clock Freq Diff Error", fsl_audmix_enum[4]),
SOC_ENUM("Sync Mode Config", fsl_audmix_enum[5]),
SOC_ENUM("Sync Mode Clk Source", fsl_audmix_enum[6]),
/* TDM1 Attenuation controls */
SOC_ENUM("TDM1 Attenuation", fsl_audmix_enum[7]),
SOC_ENUM("TDM1 Attenuation Direction", fsl_audmix_enum[8]),
SOC_SINGLE("TDM1 Attenuation Step Divider", FSL_AUDMIX_ATCR0,
2, 0x00fff, 0),
SOC_SINGLE("TDM1 Attenuation Initial Value", FSL_AUDMIX_ATIVAL0,
0, 0x3ffff, 0),
SOC_SINGLE("TDM1 Attenuation Step Up Factor", FSL_AUDMIX_ATSTPUP0,
0, 0x3ffff, 0),
SOC_SINGLE("TDM1 Attenuation Step Down Factor", FSL_AUDMIX_ATSTPDN0,
0, 0x3ffff, 0),
SOC_SINGLE("TDM1 Attenuation Step Target", FSL_AUDMIX_ATSTPTGT0,
0, 0x3ffff, 0),
/* TDM2 Attenuation controls */
SOC_ENUM("TDM2 Attenuation", fsl_audmix_enum[9]),
SOC_ENUM("TDM2 Attenuation Direction", fsl_audmix_enum[10]),
SOC_SINGLE("TDM2 Attenuation Step Divider", FSL_AUDMIX_ATCR1,
2, 0x00fff, 0),
SOC_SINGLE("TDM2 Attenuation Initial Value", FSL_AUDMIX_ATIVAL1,
0, 0x3ffff, 0),
SOC_SINGLE("TDM2 Attenuation Step Up Factor", FSL_AUDMIX_ATSTPUP1,
0, 0x3ffff, 0),
SOC_SINGLE("TDM2 Attenuation Step Down Factor", FSL_AUDMIX_ATSTPDN1,
0, 0x3ffff, 0),
SOC_SINGLE("TDM2 Attenuation Step Target", FSL_AUDMIX_ATSTPTGT1,
0, 0x3ffff, 0),
};
static int fsl_audmix_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct snd_soc_component *comp = dai->component;
u32 mask = 0, ctr = 0;
/* AUDMIX is working in DSP_A format only */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_A:
break;
default:
return -EINVAL;
}
/* For playback the AUDMIX is slave, and for record is master */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
case SND_SOC_DAIFMT_CBS_CFS:
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_IB_NF:
/* Output data will be written on positive edge of the clock */
ctr |= FSL_AUDMIX_CTR_OUTCKPOL(0);
break;
case SND_SOC_DAIFMT_NB_NF:
/* Output data will be written on negative edge of the clock */
ctr |= FSL_AUDMIX_CTR_OUTCKPOL(1);
break;
default:
return -EINVAL;
}
mask |= FSL_AUDMIX_CTR_OUTCKPOL_MASK;
return snd_soc_component_update_bits(comp, FSL_AUDMIX_CTR, mask, ctr);
}
static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct fsl_audmix *priv = snd_soc_dai_get_drvdata(dai);
/* Capture stream shall not be handled */
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
return 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
priv->tdms |= BIT(dai->driver->id);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
priv->tdms &= ~BIT(dai->driver->id);
break;
default:
return -EINVAL;
}
return 0;
}
static const struct snd_soc_dai_ops fsl_audmix_dai_ops = {
.set_fmt = fsl_audmix_dai_set_fmt,
.trigger = fsl_audmix_dai_trigger,
};
static struct snd_soc_dai_driver fsl_audmix_dai[] = {
{
.id = 0,
.name = "audmix-0",
.playback = {
.stream_name = "AUDMIX-Playback-0",
.channels_min = 8,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 96000,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = FSL_AUDMIX_FORMATS,
},
.capture = {
.stream_name = "AUDMIX-Capture-0",
.channels_min = 8,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 96000,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = FSL_AUDMIX_FORMATS,
},
.ops = &fsl_audmix_dai_ops,
},
{
.id = 1,
.name = "audmix-1",
.playback = {
.stream_name = "AUDMIX-Playback-1",
.channels_min = 8,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 96000,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = FSL_AUDMIX_FORMATS,
},
.capture = {
.stream_name = "AUDMIX-Capture-1",
.channels_min = 8,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 96000,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = FSL_AUDMIX_FORMATS,
},
.ops = &fsl_audmix_dai_ops,
},
};
static const struct snd_soc_component_driver fsl_audmix_component = {
.name = "fsl-audmix-dai",
.controls = fsl_audmix_snd_controls,
.num_controls = ARRAY_SIZE(fsl_audmix_snd_controls),
};
static bool fsl_audmix_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case FSL_AUDMIX_CTR:
case FSL_AUDMIX_STR:
case FSL_AUDMIX_ATCR0:
case FSL_AUDMIX_ATIVAL0:
case FSL_AUDMIX_ATSTPUP0:
case FSL_AUDMIX_ATSTPDN0:
case FSL_AUDMIX_ATSTPTGT0:
case FSL_AUDMIX_ATTNVAL0:
case FSL_AUDMIX_ATSTP0:
case FSL_AUDMIX_ATCR1:
case FSL_AUDMIX_ATIVAL1:
case FSL_AUDMIX_ATSTPUP1:
case FSL_AUDMIX_ATSTPDN1:
case FSL_AUDMIX_ATSTPTGT1:
case FSL_AUDMIX_ATTNVAL1:
case FSL_AUDMIX_ATSTP1:
return true;
default:
return false;
}
}
static bool fsl_audmix_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case FSL_AUDMIX_CTR:
case FSL_AUDMIX_ATCR0:
case FSL_AUDMIX_ATIVAL0:
case FSL_AUDMIX_ATSTPUP0:
case FSL_AUDMIX_ATSTPDN0:
case FSL_AUDMIX_ATSTPTGT0:
case FSL_AUDMIX_ATCR1:
case FSL_AUDMIX_ATIVAL1:
case FSL_AUDMIX_ATSTPUP1:
case FSL_AUDMIX_ATSTPDN1:
case FSL_AUDMIX_ATSTPTGT1:
return true;
default:
return false;
}
}
static const struct reg_default fsl_audmix_reg[] = {
{ FSL_AUDMIX_CTR, 0x00060 },
{ FSL_AUDMIX_STR, 0x00003 },
{ FSL_AUDMIX_ATCR0, 0x00000 },
{ FSL_AUDMIX_ATIVAL0, 0x3FFFF },
{ FSL_AUDMIX_ATSTPUP0, 0x2AAAA },
{ FSL_AUDMIX_ATSTPDN0, 0x30000 },
{ FSL_AUDMIX_ATSTPTGT0, 0x00010 },
{ FSL_AUDMIX_ATTNVAL0, 0x00000 },
{ FSL_AUDMIX_ATSTP0, 0x00000 },
{ FSL_AUDMIX_ATCR1, 0x00000 },
{ FSL_AUDMIX_ATIVAL1, 0x3FFFF },
{ FSL_AUDMIX_ATSTPUP1, 0x2AAAA },
{ FSL_AUDMIX_ATSTPDN1, 0x30000 },
{ FSL_AUDMIX_ATSTPTGT1, 0x00010 },
{ FSL_AUDMIX_ATTNVAL1, 0x00000 },
{ FSL_AUDMIX_ATSTP1, 0x00000 },
};
static const struct regmap_config fsl_audmix_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = FSL_AUDMIX_ATSTP1,
.reg_defaults = fsl_audmix_reg,
.num_reg_defaults = ARRAY_SIZE(fsl_audmix_reg),
.readable_reg = fsl_audmix_readable_reg,
.writeable_reg = fsl_audmix_writeable_reg,
.cache_type = REGCACHE_FLAT,
};
static const struct of_device_id fsl_audmix_ids[] = {
{
.compatible = "fsl,imx8qm-audmix",
.data = "imx-audmix",
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_audmix_ids);
static int fsl_audmix_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct fsl_audmix *priv;
struct resource *res;
const char *mdrv;
const struct of_device_id *of_id;
void __iomem *regs;
int ret;
of_id = of_match_device(fsl_audmix_ids, dev);
if (!of_id || !of_id->data)
return -EINVAL;
mdrv = of_id->data;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
/* Get the addresses */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
regs = devm_ioremap_resource(dev, res);
if (IS_ERR(regs))
return PTR_ERR(regs);
priv->regmap = devm_regmap_init_mmio_clk(dev, "ipg", regs,
&fsl_audmix_regmap_config);
if (IS_ERR(priv->regmap)) {
dev_err(dev, "failed to init regmap\n");
return PTR_ERR(priv->regmap);
}
priv->ipg_clk = devm_clk_get(dev, "ipg");
if (IS_ERR(priv->ipg_clk)) {
dev_err(dev, "failed to get ipg clock\n");
return PTR_ERR(priv->ipg_clk);
}
platform_set_drvdata(pdev, priv);
pm_runtime_enable(dev);
ret = devm_snd_soc_register_component(dev, &fsl_audmix_component,
fsl_audmix_dai,
ARRAY_SIZE(fsl_audmix_dai));
if (ret) {
dev_err(dev, "failed to register ASoC DAI\n");
return ret;
}
priv->pdev = platform_device_register_data(dev, mdrv, 0, NULL, 0);
if (IS_ERR(priv->pdev)) {
ret = PTR_ERR(priv->pdev);
dev_err(dev, "failed to register platform %s: %d\n", mdrv, ret);
}
return ret;
}
static int fsl_audmix_remove(struct platform_device *pdev)
{
struct fsl_audmix *priv = dev_get_drvdata(&pdev->dev);
if (priv->pdev)
platform_device_unregister(priv->pdev);
return 0;
}
#ifdef CONFIG_PM
static int fsl_audmix_runtime_resume(struct device *dev)
{
struct fsl_audmix *priv = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(priv->ipg_clk);
if (ret) {
dev_err(dev, "Failed to enable IPG clock: %d\n", ret);
return ret;
}
regcache_cache_only(priv->regmap, false);
regcache_mark_dirty(priv->regmap);
return regcache_sync(priv->regmap);
}
static int fsl_audmix_runtime_suspend(struct device *dev)
{
struct fsl_audmix *priv = dev_get_drvdata(dev);
regcache_cache_only(priv->regmap, true);
clk_disable_unprepare(priv->ipg_clk);
return 0;
}
#endif /* CONFIG_PM */
static const struct dev_pm_ops fsl_audmix_pm = {
SET_RUNTIME_PM_OPS(fsl_audmix_runtime_suspend,
fsl_audmix_runtime_resume,
NULL)
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
static struct platform_driver fsl_audmix_driver = {
.probe = fsl_audmix_probe,
.remove = fsl_audmix_remove,
.driver = {
.name = "fsl-audmix",
.of_match_table = fsl_audmix_ids,
.pm = &fsl_audmix_pm,
},
};
module_platform_driver(fsl_audmix_driver);
MODULE_DESCRIPTION("NXP AUDMIX ASoC DAI driver");
MODULE_AUTHOR("Viorel Suman <viorel.suman@nxp.com>");
MODULE_ALIAS("platform:fsl-audmix");
MODULE_LICENSE("GPL v2");

102
sound/soc/fsl/fsl_audmix.h Ficheiro normal
Ver ficheiro

@@ -0,0 +1,102 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* NXP AUDMIX ALSA SoC Digital Audio Interface (DAI) driver
*
* Copyright 2017 NXP
*/
#ifndef __FSL_AUDMIX_H
#define __FSL_AUDMIX_H
#define FSL_AUDMIX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
SNDRV_PCM_FMTBIT_S24_LE |\
SNDRV_PCM_FMTBIT_S32_LE)
/* AUDMIX Registers */
#define FSL_AUDMIX_CTR 0x200 /* Control */
#define FSL_AUDMIX_STR 0x204 /* Status */
#define FSL_AUDMIX_ATCR0 0x208 /* Attenuation Control */
#define FSL_AUDMIX_ATIVAL0 0x20c /* Attenuation Initial Value */
#define FSL_AUDMIX_ATSTPUP0 0x210 /* Attenuation step up factor */
#define FSL_AUDMIX_ATSTPDN0 0x214 /* Attenuation step down factor */
#define FSL_AUDMIX_ATSTPTGT0 0x218 /* Attenuation step target */
#define FSL_AUDMIX_ATTNVAL0 0x21c /* Attenuation Value */
#define FSL_AUDMIX_ATSTP0 0x220 /* Attenuation step number */
#define FSL_AUDMIX_ATCR1 0x228 /* Attenuation Control */
#define FSL_AUDMIX_ATIVAL1 0x22c /* Attenuation Initial Value */
#define FSL_AUDMIX_ATSTPUP1 0x230 /* Attenuation step up factor */
#define FSL_AUDMIX_ATSTPDN1 0x234 /* Attenuation step down factor */
#define FSL_AUDMIX_ATSTPTGT1 0x238 /* Attenuation step target */
#define FSL_AUDMIX_ATTNVAL1 0x23c /* Attenuation Value */
#define FSL_AUDMIX_ATSTP1 0x240 /* Attenuation step number */
/* AUDMIX Control Register */
#define FSL_AUDMIX_CTR_MIXCLK_SHIFT 0
#define FSL_AUDMIX_CTR_MIXCLK_MASK BIT(FSL_AUDMIX_CTR_MIXCLK_SHIFT)
#define FSL_AUDMIX_CTR_MIXCLK(i) ((i) << FSL_AUDMIX_CTR_MIXCLK_SHIFT)
#define FSL_AUDMIX_CTR_OUTSRC_SHIFT 1
#define FSL_AUDMIX_CTR_OUTSRC_MASK (0x3 << FSL_AUDMIX_CTR_OUTSRC_SHIFT)
#define FSL_AUDMIX_CTR_OUTSRC(i) (((i) << FSL_AUDMIX_CTR_OUTSRC_SHIFT)\
& FSL_AUDMIX_CTR_OUTSRC_MASK)
#define FSL_AUDMIX_CTR_OUTWIDTH_SHIFT 3
#define FSL_AUDMIX_CTR_OUTWIDTH_MASK (0x7 << FSL_AUDMIX_CTR_OUTWIDTH_SHIFT)
#define FSL_AUDMIX_CTR_OUTWIDTH(i) (((i) << FSL_AUDMIX_CTR_OUTWIDTH_SHIFT)\
& FSL_AUDMIX_CTR_OUTWIDTH_MASK)
#define FSL_AUDMIX_CTR_OUTCKPOL_SHIFT 6
#define FSL_AUDMIX_CTR_OUTCKPOL_MASK BIT(FSL_AUDMIX_CTR_OUTCKPOL_SHIFT)
#define FSL_AUDMIX_CTR_OUTCKPOL(i) ((i) << FSL_AUDMIX_CTR_OUTCKPOL_SHIFT)
#define FSL_AUDMIX_CTR_MASKRTDF_SHIFT 7
#define FSL_AUDMIX_CTR_MASKRTDF_MASK BIT(FSL_AUDMIX_CTR_MASKRTDF_SHIFT)
#define FSL_AUDMIX_CTR_MASKRTDF(i) ((i) << FSL_AUDMIX_CTR_MASKRTDF_SHIFT)
#define FSL_AUDMIX_CTR_MASKCKDF_SHIFT 8
#define FSL_AUDMIX_CTR_MASKCKDF_MASK BIT(FSL_AUDMIX_CTR_MASKCKDF_SHIFT)
#define FSL_AUDMIX_CTR_MASKCKDF(i) ((i) << FSL_AUDMIX_CTR_MASKCKDF_SHIFT)
#define FSL_AUDMIX_CTR_SYNCMODE_SHIFT 9
#define FSL_AUDMIX_CTR_SYNCMODE_MASK BIT(FSL_AUDMIX_CTR_SYNCMODE_SHIFT)
#define FSL_AUDMIX_CTR_SYNCMODE(i) ((i) << FSL_AUDMIX_CTR_SYNCMODE_SHIFT)
#define FSL_AUDMIX_CTR_SYNCSRC_SHIFT 10
#define FSL_AUDMIX_CTR_SYNCSRC_MASK BIT(FSL_AUDMIX_CTR_SYNCSRC_SHIFT)
#define FSL_AUDMIX_CTR_SYNCSRC(i) ((i) << FSL_AUDMIX_CTR_SYNCSRC_SHIFT)
/* AUDMIX Status Register */
#define FSL_AUDMIX_STR_RATEDIFF BIT(0)
#define FSL_AUDMIX_STR_CLKDIFF BIT(1)
#define FSL_AUDMIX_STR_MIXSTAT_SHIFT 2
#define FSL_AUDMIX_STR_MIXSTAT_MASK (0x3 << FSL_AUDMIX_STR_MIXSTAT_SHIFT)
#define FSL_AUDMIX_STR_MIXSTAT(i) (((i) & FSL_AUDMIX_STR_MIXSTAT_MASK) \
>> FSL_AUDMIX_STR_MIXSTAT_SHIFT)
/* AUDMIX Attenuation Control Register */
#define FSL_AUDMIX_ATCR_AT_EN BIT(0)
#define FSL_AUDMIX_ATCR_AT_UPDN BIT(1)
#define FSL_AUDMIX_ATCR_ATSTPDIF_SHIFT 2
#define FSL_AUDMIX_ATCR_ATSTPDFI_MASK \
(0xfff << FSL_AUDMIX_ATCR_ATSTPDIF_SHIFT)
/* AUDMIX Attenuation Initial Value Register */
#define FSL_AUDMIX_ATIVAL_ATINVAL_MASK 0x3FFFF
/* AUDMIX Attenuation Step Up Factor Register */
#define FSL_AUDMIX_ATSTPUP_ATSTEPUP_MASK 0x3FFFF
/* AUDMIX Attenuation Step Down Factor Register */
#define FSL_AUDMIX_ATSTPDN_ATSTEPDN_MASK 0x3FFFF
/* AUDMIX Attenuation Step Target Register */
#define FSL_AUDMIX_ATSTPTGT_ATSTPTG_MASK 0x3FFFF
/* AUDMIX Attenuation Value Register */
#define FSL_AUDMIX_ATTNVAL_ATCURVAL_MASK 0x3FFFF
/* AUDMIX Attenuation Step Number Register */
#define FSL_AUDMIX_ATSTP_STPCTR_MASK 0x3FFFF
#define FSL_AUDMIX_MAX_DAIS 2
struct fsl_audmix {
struct platform_device *pdev;
struct regmap *regmap;
struct clk *ipg_clk;
u8 tdms;
};
#endif /* __FSL_AUDMIX_H */

Ver ficheiro

@@ -1,18 +1,14 @@
/*
* Freescale DMA ALSA SoC PCM driver
*
* Author: Timur Tabi <timur@freescale.com>
*
* Copyright 2007-2010 Freescale Semiconductor, Inc.
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*
* This driver implements ASoC support for the Elo DMA controller, which is
* the DMA controller on Freescale 83xx, 85xx, and 86xx SOCs. In ALSA terms,
* the PCM driver is what handles the DMA buffer.
*/
// SPDX-License-Identifier: GPL-2.0
//
// Freescale DMA ALSA SoC PCM driver
//
// Author: Timur Tabi <timur@freescale.com>
//
// Copyright 2007-2010 Freescale Semiconductor, Inc.
//
// This driver implements ASoC support for the Elo DMA controller, which is
// the DMA controller on Freescale 83xx, 85xx, and 86xx SOCs. In ALSA terms,
// the PCM driver is what handles the DMA buffer.
#include <linux/module.h>
#include <linux/init.h>

Ver ficheiro

@@ -1,9 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* mpc8610-pcm.h - ALSA PCM interface for the Freescale MPC8610 SoC
*
* 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 _MPC8610_PCM_H

Ver ficheiro

@@ -218,7 +218,7 @@ static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
{
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
struct clk *clksrc = esai_priv->extalclk;
bool tx = clk_id <= ESAI_HCKT_EXTAL;
bool tx = (clk_id <= ESAI_HCKT_EXTAL || esai_priv->synchronous);
bool in = dir == SND_SOC_CLOCK_IN;
u32 ratio, ecr = 0;
unsigned long clk_rate;
@@ -251,9 +251,9 @@ static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
break;
case ESAI_HCKT_EXTAL:
ecr |= ESAI_ECR_ETI;
/* fall through */
break;
case ESAI_HCKR_EXTAL:
ecr |= ESAI_ECR_ERI;
ecr |= esai_priv->synchronous ? ESAI_ECR_ETI : ESAI_ECR_ERI;
break;
default:
return -EINVAL;
@@ -537,10 +537,18 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
bclk = params_rate(params) * slot_width * esai_priv->slots;
ret = fsl_esai_set_bclk(dai, tx, bclk);
ret = fsl_esai_set_bclk(dai, esai_priv->synchronous || tx, bclk);
if (ret)
return ret;
mask = ESAI_xCR_xSWS_MASK;
val = ESAI_xCR_xSWS(slot_width, width);
regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val);
/* Recording in synchronous mode needs to set TCR also */
if (!tx && esai_priv->synchronous)
regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, mask, val);
/* Use Normal mode to support monaural audio */
regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
ESAI_xCR_xMOD_MASK, params_channels(params) > 1 ?
@@ -556,10 +564,9 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), mask, val);
mask = ESAI_xCR_xSWS_MASK | (tx ? ESAI_xCR_PADC : 0);
val = ESAI_xCR_xSWS(slot_width, width) | (tx ? ESAI_xCR_PADC : 0);
regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val);
if (tx)
regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR,
ESAI_xCR_PADC, ESAI_xCR_PADC);
/* Remove ESAI personal reset by configuring ESAI_PCRC and ESAI_PRRC */
regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC,

Ver ficheiro

@@ -151,12 +151,9 @@ static inline int get_clk_div(struct fsl_micfil *micfil,
{
u32 ctrl2_reg;
long mclk_rate;
int osr;
int clk_div;
regmap_read(micfil->regmap, REG_MICFIL_CTRL2, &ctrl2_reg);
osr = 16 - ((ctrl2_reg & MICFIL_CTRL2_CICOSR_MASK)
>> MICFIL_CTRL2_CICOSR_SHIFT);
mclk_rate = clk_get_rate(micfil->mclk);

Ver ficheiro

@@ -9,6 +9,7 @@
#include <linux/dmaengine.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/time.h>
@@ -268,12 +269,14 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
case SND_SOC_DAIFMT_CBS_CFS:
val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
sai->is_slave_mode = false;
break;
case SND_SOC_DAIFMT_CBM_CFM:
sai->is_slave_mode = true;
break;
case SND_SOC_DAIFMT_CBS_CFM:
val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
sai->is_slave_mode = false;
break;
case SND_SOC_DAIFMT_CBM_CFS:
val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
@@ -899,6 +902,8 @@ static int fsl_sai_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, sai);
pm_runtime_enable(&pdev->dev);
ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component,
&fsl_sai_dai, 1);
if (ret)
@@ -910,6 +915,13 @@ static int fsl_sai_probe(struct platform_device *pdev)
return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
}
static int fsl_sai_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
return 0;
}
static const struct of_device_id fsl_sai_ids[] = {
{ .compatible = "fsl,vf610-sai", },
{ .compatible = "fsl,imx6sx-sai", },
@@ -918,8 +930,8 @@ static const struct of_device_id fsl_sai_ids[] = {
};
MODULE_DEVICE_TABLE(of, fsl_sai_ids);
#ifdef CONFIG_PM_SLEEP
static int fsl_sai_suspend(struct device *dev)
#ifdef CONFIG_PM
static int fsl_sai_runtime_suspend(struct device *dev)
{
struct fsl_sai *sai = dev_get_drvdata(dev);
@@ -929,7 +941,7 @@ static int fsl_sai_suspend(struct device *dev)
return 0;
}
static int fsl_sai_resume(struct device *dev)
static int fsl_sai_runtime_resume(struct device *dev)
{
struct fsl_sai *sai = dev_get_drvdata(dev);
@@ -941,14 +953,18 @@ static int fsl_sai_resume(struct device *dev)
regmap_write(sai->regmap, FSL_SAI_RCSR, 0);
return regcache_sync(sai->regmap);
}
#endif /* CONFIG_PM_SLEEP */
#endif /* CONFIG_PM */
static const struct dev_pm_ops fsl_sai_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(fsl_sai_suspend, fsl_sai_resume)
SET_RUNTIME_PM_OPS(fsl_sai_runtime_suspend,
fsl_sai_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
static struct platform_driver fsl_sai_driver = {
.probe = fsl_sai_probe,
.remove = fsl_sai_remove,
.driver = {
.name = "fsl-sai",
.pm = &fsl_sai_pm_ops,

Ver ficheiro

@@ -71,6 +71,7 @@ int fsl_asoc_get_dma_channel(struct device_node *ssi_np,
iprop = of_get_property(dma_np, "cell-index", NULL);
if (!iprop) {
of_node_put(dma_np);
of_node_put(dma_channel_np);
return -EINVAL;
}
*dma_id = be32_to_cpup(iprop);

331
sound/soc/fsl/imx-audmix.c Ficheiro normal
Ver ficheiro

@@ -0,0 +1,331 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2017 NXP
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/clk.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <linux/pm_runtime.h>
#include "fsl_sai.h"
#include "fsl_audmix.h"
struct imx_audmix {
struct platform_device *pdev;
struct snd_soc_card card;
struct platform_device *audmix_pdev;
struct platform_device *out_pdev;
struct clk *cpu_mclk;
int num_dai;
struct snd_soc_dai_link *dai;
int num_dai_conf;
struct snd_soc_codec_conf *dai_conf;
int num_dapm_routes;
struct snd_soc_dapm_route *dapm_routes;
};
static const u32 imx_audmix_rates[] = {
8000, 12000, 16000, 24000, 32000, 48000, 64000, 96000,
};
static const struct snd_pcm_hw_constraint_list imx_audmix_rate_constraints = {
.count = ARRAY_SIZE(imx_audmix_rates),
.list = imx_audmix_rates,
};
static int imx_audmix_fe_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct imx_audmix *priv = snd_soc_card_get_drvdata(rtd->card);
struct snd_pcm_runtime *runtime = substream->runtime;
struct device *dev = rtd->card->dev;
unsigned long clk_rate = clk_get_rate(priv->cpu_mclk);
int ret;
if (clk_rate % 24576000 == 0) {
ret = snd_pcm_hw_constraint_list(runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
&imx_audmix_rate_constraints);
if (ret < 0)
return ret;
} else {
dev_warn(dev, "mclk may be not supported %lu\n", clk_rate);
}
ret = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS,
1, 8);
if (ret < 0)
return ret;
return snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT,
FSL_AUDMIX_FORMATS);
}
static int imx_audmix_fe_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct device *dev = rtd->card->dev;
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
unsigned int fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF;
u32 channels = params_channels(params);
int ret, dir;
/* For playback the AUDMIX is slave, and for record is master */
fmt |= tx ? SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBM_CFM;
dir = tx ? SND_SOC_CLOCK_OUT : SND_SOC_CLOCK_IN;
/* set DAI configuration */
ret = snd_soc_dai_set_fmt(rtd->cpu_dai, fmt);
if (ret) {
dev_err(dev, "failed to set cpu dai fmt: %d\n", ret);
return ret;
}
ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, FSL_SAI_CLK_MAST1, 0, dir);
if (ret) {
dev_err(dev, "failed to set cpu sysclk: %d\n", ret);
return ret;
}
/*
* Per datasheet, AUDMIX expects 8 slots and 32 bits
* for every slot in TDM mode.
*/
ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, BIT(channels) - 1,
BIT(channels) - 1, 8, 32);
if (ret)
dev_err(dev, "failed to set cpu dai tdm slot: %d\n", ret);
return ret;
}
static int imx_audmix_be_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct device *dev = rtd->card->dev;
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
unsigned int fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF;
int ret;
if (!tx)
return 0;
/* For playback the AUDMIX is slave */
fmt |= SND_SOC_DAIFMT_CBM_CFM;
/* set AUDMIX DAI configuration */
ret = snd_soc_dai_set_fmt(rtd->cpu_dai, fmt);
if (ret)
dev_err(dev, "failed to set AUDMIX DAI fmt: %d\n", ret);
return ret;
}
static struct snd_soc_ops imx_audmix_fe_ops = {
.startup = imx_audmix_fe_startup,
.hw_params = imx_audmix_fe_hw_params,
};
static struct snd_soc_ops imx_audmix_be_ops = {
.hw_params = imx_audmix_be_hw_params,
};
static int imx_audmix_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device_node *audmix_np = NULL, *out_cpu_np = NULL;
struct platform_device *audmix_pdev = NULL;
struct platform_device *cpu_pdev;
struct of_phandle_args args;
struct imx_audmix *priv;
int i, num_dai, ret;
const char *fe_name_pref = "HiFi-AUDMIX-FE-";
char *be_name, *be_pb, *be_cp, *dai_name, *capture_dai_name;
if (pdev->dev.parent) {
audmix_np = pdev->dev.parent->of_node;
} else {
dev_err(&pdev->dev, "Missing parent device.\n");
return -EINVAL;
}
if (!audmix_np) {
dev_err(&pdev->dev, "Missing DT node for parent device.\n");
return -EINVAL;
}
audmix_pdev = of_find_device_by_node(audmix_np);
if (!audmix_pdev) {
dev_err(&pdev->dev, "Missing AUDMIX platform device for %s\n",
np->full_name);
return -EINVAL;
}
put_device(&audmix_pdev->dev);
num_dai = of_count_phandle_with_args(audmix_np, "dais", NULL);
if (num_dai != FSL_AUDMIX_MAX_DAIS) {
dev_err(&pdev->dev, "Need 2 dais to be provided for %s\n",
audmix_np->full_name);
return -EINVAL;
}
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->num_dai = 2 * num_dai;
priv->dai = devm_kzalloc(&pdev->dev, priv->num_dai *
sizeof(struct snd_soc_dai_link), GFP_KERNEL);
if (!priv->dai)
return -ENOMEM;
priv->num_dai_conf = num_dai;
priv->dai_conf = devm_kzalloc(&pdev->dev, priv->num_dai_conf *
sizeof(struct snd_soc_codec_conf),
GFP_KERNEL);
if (!priv->dai_conf)
return -ENOMEM;
priv->num_dapm_routes = 3 * num_dai;
priv->dapm_routes = devm_kzalloc(&pdev->dev, priv->num_dapm_routes *
sizeof(struct snd_soc_dapm_route),
GFP_KERNEL);
if (!priv->dapm_routes)
return -ENOMEM;
for (i = 0; i < num_dai; i++) {
ret = of_parse_phandle_with_args(audmix_np, "dais", NULL, i,
&args);
if (ret < 0) {
dev_err(&pdev->dev, "of_parse_phandle_with_args failed\n");
return ret;
}
cpu_pdev = of_find_device_by_node(args.np);
if (!cpu_pdev) {
dev_err(&pdev->dev, "failed to find SAI platform device\n");
return -EINVAL;
}
put_device(&cpu_pdev->dev);
dai_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s%s",
fe_name_pref, args.np->full_name + 1);
dev_info(pdev->dev.parent, "DAI FE name:%s\n", dai_name);
if (i == 0) {
out_cpu_np = args.np;
capture_dai_name =
devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s",
dai_name, "CPU-Capture");
}
priv->dai[i].name = dai_name;
priv->dai[i].stream_name = "HiFi-AUDMIX-FE";
priv->dai[i].codec_dai_name = "snd-soc-dummy-dai";
priv->dai[i].codec_name = "snd-soc-dummy";
priv->dai[i].cpu_of_node = args.np;
priv->dai[i].cpu_dai_name = dev_name(&cpu_pdev->dev);
priv->dai[i].platform_of_node = args.np;
priv->dai[i].dynamic = 1;
priv->dai[i].dpcm_playback = 1;
priv->dai[i].dpcm_capture = (i == 0 ? 1 : 0);
priv->dai[i].ignore_pmdown_time = 1;
priv->dai[i].ops = &imx_audmix_fe_ops;
/* Add AUDMIX Backend */
be_name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
"audmix-%d", i);
be_pb = devm_kasprintf(&pdev->dev, GFP_KERNEL,
"AUDMIX-Playback-%d", i);
be_cp = devm_kasprintf(&pdev->dev, GFP_KERNEL,
"AUDMIX-Capture-%d", i);
priv->dai[num_dai + i].name = be_name;
priv->dai[num_dai + i].codec_dai_name = "snd-soc-dummy-dai";
priv->dai[num_dai + i].codec_name = "snd-soc-dummy";
priv->dai[num_dai + i].cpu_of_node = audmix_np;
priv->dai[num_dai + i].cpu_dai_name = be_name;
priv->dai[num_dai + i].platform_name = "snd-soc-dummy";
priv->dai[num_dai + i].no_pcm = 1;
priv->dai[num_dai + i].dpcm_playback = 1;
priv->dai[num_dai + i].dpcm_capture = 1;
priv->dai[num_dai + i].ignore_pmdown_time = 1;
priv->dai[num_dai + i].ops = &imx_audmix_be_ops;
priv->dai_conf[i].of_node = args.np;
priv->dai_conf[i].name_prefix = dai_name;
priv->dapm_routes[i].source =
devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s",
dai_name, "CPU-Playback");
priv->dapm_routes[i].sink = be_pb;
priv->dapm_routes[num_dai + i].source = be_pb;
priv->dapm_routes[num_dai + i].sink = be_cp;
priv->dapm_routes[2 * num_dai + i].source = be_cp;
priv->dapm_routes[2 * num_dai + i].sink = capture_dai_name;
}
cpu_pdev = of_find_device_by_node(out_cpu_np);
if (!cpu_pdev) {
dev_err(&pdev->dev, "failed to find SAI platform device\n");
return -EINVAL;
}
put_device(&cpu_pdev->dev);
priv->cpu_mclk = devm_clk_get(&cpu_pdev->dev, "mclk1");
if (IS_ERR(priv->cpu_mclk)) {
ret = PTR_ERR(priv->cpu_mclk);
dev_err(&cpu_pdev->dev, "failed to get DAI mclk1: %d\n", ret);
return -EINVAL;
}
priv->audmix_pdev = audmix_pdev;
priv->out_pdev = cpu_pdev;
priv->card.dai_link = priv->dai;
priv->card.num_links = priv->num_dai;
priv->card.codec_conf = priv->dai_conf;
priv->card.num_configs = priv->num_dai_conf;
priv->card.dapm_routes = priv->dapm_routes;
priv->card.num_dapm_routes = priv->num_dapm_routes;
priv->card.dev = pdev->dev.parent;
priv->card.owner = THIS_MODULE;
priv->card.name = "imx-audmix";
platform_set_drvdata(pdev, &priv->card);
snd_soc_card_set_drvdata(&priv->card, priv);
ret = devm_snd_soc_register_card(pdev->dev.parent, &priv->card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card failed\n");
return ret;
}
return ret;
}
static struct platform_driver imx_audmix_driver = {
.probe = imx_audmix_probe,
.driver = {
.name = "imx-audmix",
.pm = &snd_soc_pm_ops,
},
};
module_platform_driver(imx_audmix_driver);
MODULE_DESCRIPTION("NXP AUDMIX ASoC machine driver");
MODULE_AUTHOR("Viorel Suman <viorel.suman@nxp.com>");
MODULE_ALIAS("platform:imx-audmix");
MODULE_LICENSE("GPL v2");

Ver ficheiro

@@ -1,21 +1,11 @@
/*
* Copyright 2012 Freescale Semiconductor, Inc.
* Copyright 2012 Linaro Ltd.
* Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
*
* Initial development of this code was funded by
* Phytec Messtechnik GmbH, http://www.phytec.de
*
* 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.
*/
// SPDX-License-Identifier: GPL-2.0+
//
// Copyright 2012 Freescale Semiconductor, Inc.
// Copyright 2012 Linaro Ltd.
// Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
//
// Initial development of this code was funded by
// Phytec Messtechnik GmbH, http://www.phytec.de
#include <linux/clk.h>
#include <linux/debugfs.h>

Ver ficheiro

@@ -1,14 +1,7 @@
/*
* Copyright 2012 Freescale Semiconductor, Inc.
* Copyright 2012 Linaro Ltd.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
// SPDX-License-Identifier: GPL-2.0+
//
// Copyright 2012 Freescale Semiconductor, Inc.
// Copyright 2012 Linaro Ltd.
#include <linux/gpio.h>
#include <linux/module.h>

Ver ficheiro

@@ -1,17 +1,11 @@
/*
* imx-mc13783.c -- SoC audio for imx based boards with mc13783 codec
*
* Copyright 2012 Philippe Retornaz, <philippe.retornaz@epfl.ch>
*
* Heavly based on phycore-mc13783:
* Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
*
* 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.
*
*/
// SPDX-License-Identifier: GPL-2.0+
//
// imx-mc13783.c -- SoC audio for imx based boards with mc13783 codec
//
// Copyright 2012 Philippe Retornaz, <philippe.retornaz@epfl.ch>
//
// Heavly based on phycore-mc13783:
// Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
#include <linux/module.h>
#include <linux/moduleparam.h>

Ver ficheiro

@@ -1,16 +1,11 @@
/*
* imx-pcm-fiq.c -- ALSA Soc Audio Layer
*
* Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
*
* This code is based on code copyrighted by Freescale,
* Liam Girdwood, Javier Martin and probably others.
*
* 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.
*/
// SPDX-License-Identifier: GPL-2.0+
// imx-pcm-fiq.c -- ALSA Soc Audio Layer
//
// Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
//
// This code is based on code copyrighted by Freescale,
// Liam Girdwood, Javier Martin and probably others.
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>

Ver ficheiro

@@ -1,13 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
*
* This code is based on code copyrighted by Freescale,
* Liam Girdwood, Javier Martin and probably others.
*
* 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.
*/
#ifndef _IMX_PCM_H

Ver ficheiro

@@ -1,13 +1,6 @@
/*
* Copyright (C) 2013 Freescale Semiconductor, Inc.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
// SPDX-License-Identifier: GPL-2.0+
//
// Copyright (C) 2013 Freescale Semiconductor, Inc.
#include <linux/module.h>
#include <linux/of_platform.h>

Alguns ficheiros não foram mostrados porque foram modificados demasiados ficheiros neste diff Mostrar mais