From 0944cc392e9a41dd36b65b2f8cb31dff437a9fdb Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Thu, 19 May 2011 13:45:29 +0100 Subject: ASoC: soc-cache: Block based rbtree compression This patch prepares the ground for the actual rbtree optimization patch which will save a pointer to the last accessed rbnode that was used in either the read() or write() functions. Each rbnode manages a variable length block of registers. There can be no two nodes with overlapping blocks. Each block has a base register and a currently top register, all the other registers, if any, lie in between these two and in ascending order. The reasoning behind the construction of this rbtree is simple. In the snd_soc_rbtree_cache_init() function, we iterate over the register defaults provided by the driver. For each register value that is non-zero we insert it in the rbtree. In order to determine in which rbnode we need to add the register, we first look if there is another register already added that is adjacent to the one we are about to add. If that is the case we append it in that rbnode block, otherwise we create a new rbnode with a single register in its block and add it to the tree. In the next patch, where a cached rbnode is used by both the write() and the read() functions, we also check if the register we are about to add is in the cached rbnode (the least recently accessed one) and if so we append it in that rbnode block. Signed-off-by: Dimitris Papastamos Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index 687beec..78c9869 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -595,31 +595,85 @@ static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx, } struct snd_soc_rbtree_node { - struct rb_node node; - unsigned int reg; - unsigned int value; - unsigned int defval; + struct rb_node node; /* the actual rbtree node holding this block */ + unsigned int base_reg; /* base register handled by this block */ + unsigned int word_size; /* number of bytes needed to represent the register index */ + void *block; /* block of adjacent registers */ + unsigned int blklen; /* number of registers available in the block */ } __attribute__ ((packed)); struct snd_soc_rbtree_ctx { struct rb_root root; }; +static inline void snd_soc_rbtree_get_base_top_reg( + struct snd_soc_rbtree_node *rbnode, + unsigned int *base, unsigned int *top) +{ + *base = rbnode->base_reg; + *top = rbnode->base_reg + rbnode->blklen - 1; +} + +static unsigned int snd_soc_rbtree_get_register( + struct snd_soc_rbtree_node *rbnode, unsigned int idx) +{ + unsigned int val; + + switch (rbnode->word_size) { + case 1: { + u8 *p = rbnode->block; + val = p[idx]; + return val; + } + case 2: { + u16 *p = rbnode->block; + val = p[idx]; + return val; + } + default: + BUG(); + break; + } + return -1; +} + +static void snd_soc_rbtree_set_register(struct snd_soc_rbtree_node *rbnode, + unsigned int idx, unsigned int val) +{ + switch (rbnode->word_size) { + case 1: { + u8 *p = rbnode->block; + p[idx] = val; + break; + } + case 2: { + u16 *p = rbnode->block; + p[idx] = val; + break; + } + default: + BUG(); + break; + } +} + static struct snd_soc_rbtree_node *snd_soc_rbtree_lookup( struct rb_root *root, unsigned int reg) { struct rb_node *node; struct snd_soc_rbtree_node *rbnode; + unsigned int base_reg, top_reg; node = root->rb_node; while (node) { rbnode = container_of(node, struct snd_soc_rbtree_node, node); - if (rbnode->reg < reg) - node = node->rb_left; - else if (rbnode->reg > reg) - node = node->rb_right; - else + snd_soc_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg); + if (reg >= base_reg && reg <= top_reg) return rbnode; + else if (reg > top_reg) + node = node->rb_right; + else if (reg < base_reg) + node = node->rb_left; } return NULL; @@ -630,19 +684,28 @@ static int snd_soc_rbtree_insert(struct rb_root *root, { struct rb_node **new, *parent; struct snd_soc_rbtree_node *rbnode_tmp; + unsigned int base_reg_tmp, top_reg_tmp; + unsigned int base_reg; parent = NULL; new = &root->rb_node; while (*new) { rbnode_tmp = container_of(*new, struct snd_soc_rbtree_node, node); + /* base and top registers of the current rbnode */ + snd_soc_rbtree_get_base_top_reg(rbnode_tmp, &base_reg_tmp, + &top_reg_tmp); + /* base register of the rbnode to be added */ + base_reg = rbnode->base_reg; parent = *new; - if (rbnode_tmp->reg < rbnode->reg) - new = &((*new)->rb_left); - else if (rbnode_tmp->reg > rbnode->reg) - new = &((*new)->rb_right); - else + /* if this register has already been inserted, just return */ + if (base_reg >= base_reg_tmp && + base_reg <= top_reg_tmp) return 0; + else if (base_reg > top_reg_tmp) + new = &((*new)->rb_right); + else if (base_reg < base_reg_tmp) + new = &((*new)->rb_left); } /* insert the node into the rbtree */ @@ -657,57 +720,120 @@ static int snd_soc_rbtree_cache_sync(struct snd_soc_codec *codec) struct snd_soc_rbtree_ctx *rbtree_ctx; struct rb_node *node; struct snd_soc_rbtree_node *rbnode; + unsigned int regtmp; unsigned int val; int ret; + int i; rbtree_ctx = codec->reg_cache; for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) { rbnode = rb_entry(node, struct snd_soc_rbtree_node, node); - if (rbnode->value == rbnode->defval) - continue; - WARN_ON(codec->writable_register && - codec->writable_register(codec, rbnode->reg)); - ret = snd_soc_cache_read(codec, rbnode->reg, &val); - if (ret) - return ret; - codec->cache_bypass = 1; - ret = snd_soc_write(codec, rbnode->reg, val); - codec->cache_bypass = 0; - if (ret) - return ret; - dev_dbg(codec->dev, "Synced register %#x, value = %#x\n", - rbnode->reg, val); + for (i = 0; i < rbnode->blklen; ++i) { + regtmp = rbnode->base_reg + i; + WARN_ON(codec->writable_register && + codec->writable_register(codec, regtmp)); + val = snd_soc_rbtree_get_register(rbnode, i); + codec->cache_bypass = 1; + ret = snd_soc_write(codec, regtmp, val); + codec->cache_bypass = 0; + if (ret) + return ret; + dev_dbg(codec->dev, "Synced register %#x, value = %#x\n", + regtmp, val); + } } return 0; } +static int snd_soc_rbtree_insert_to_block(struct snd_soc_rbtree_node *rbnode, + unsigned int pos, unsigned int reg, + unsigned int value) +{ + u8 *blk; + + blk = krealloc(rbnode->block, + (rbnode->blklen + 1) * rbnode->word_size, GFP_KERNEL); + if (!blk) + return -ENOMEM; + + /* insert the register value in the correct place in the rbnode block */ + memmove(blk + (pos + 1) * rbnode->word_size, + blk + pos * rbnode->word_size, + (rbnode->blklen - pos) * rbnode->word_size); + + /* update the rbnode block, its size and the base register */ + rbnode->block = blk; + rbnode->blklen++; + if (!pos) + rbnode->base_reg = reg; + + snd_soc_rbtree_set_register(rbnode, pos, value); + return 0; +} + static int snd_soc_rbtree_cache_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { struct snd_soc_rbtree_ctx *rbtree_ctx; - struct snd_soc_rbtree_node *rbnode; + struct snd_soc_rbtree_node *rbnode, *rbnode_tmp; + struct rb_node *node; + unsigned int val; + unsigned int reg_tmp; + unsigned int pos; + int i; + int ret; rbtree_ctx = codec->reg_cache; rbnode = snd_soc_rbtree_lookup(&rbtree_ctx->root, reg); if (rbnode) { - if (rbnode->value == value) + reg_tmp = reg - rbnode->base_reg; + val = snd_soc_rbtree_get_register(rbnode, reg_tmp); + if (val == value) return 0; - rbnode->value = value; + snd_soc_rbtree_set_register(rbnode, reg_tmp, value); } else { /* bail out early, no need to create the rbnode yet */ if (!value) return 0; - /* - * for uninitialized registers whose value is changed - * from the default zero, create an rbnode and insert - * it into the tree. + /* look for an adjacent register to the one we are about to add */ + for (node = rb_first(&rbtree_ctx->root); node; + node = rb_next(node)) { + rbnode_tmp = rb_entry(node, struct snd_soc_rbtree_node, node); + for (i = 0; i < rbnode_tmp->blklen; ++i) { + reg_tmp = rbnode_tmp->base_reg + i; + if (abs(reg_tmp - reg) != 1) + continue; + /* decide where in the block to place our register */ + if (reg_tmp + 1 == reg) + pos = i + 1; + else + pos = i; + ret = snd_soc_rbtree_insert_to_block(rbnode_tmp, pos, + reg, value); + if (ret) + return ret; + return 0; + } + } + /* we did not manage to find a place to insert it in an existing + * block so create a new rbnode with a single register in its block. + * This block will get populated further if any other adjacent + * registers get modified in the future. */ rbnode = kzalloc(sizeof *rbnode, GFP_KERNEL); if (!rbnode) return -ENOMEM; - rbnode->reg = reg; - rbnode->value = value; + rbnode->blklen = 1; + rbnode->base_reg = reg; + rbnode->word_size = codec->driver->reg_word_size; + rbnode->block = kmalloc(rbnode->blklen * rbnode->word_size, + GFP_KERNEL); + if (!rbnode->block) { + kfree(rbnode); + return -ENOMEM; + } + snd_soc_rbtree_set_register(rbnode, 0, value); snd_soc_rbtree_insert(&rbtree_ctx->root, rbnode); } @@ -719,11 +845,13 @@ static int snd_soc_rbtree_cache_read(struct snd_soc_codec *codec, { struct snd_soc_rbtree_ctx *rbtree_ctx; struct snd_soc_rbtree_node *rbnode; + unsigned int reg_tmp; rbtree_ctx = codec->reg_cache; rbnode = snd_soc_rbtree_lookup(&rbtree_ctx->root, reg); if (rbnode) { - *value = rbnode->value; + reg_tmp = reg - rbnode->base_reg; + *value = snd_soc_rbtree_get_register(rbnode, reg_tmp); } else { /* uninitialized registers default to 0 */ *value = 0; @@ -749,6 +877,7 @@ static int snd_soc_rbtree_cache_exit(struct snd_soc_codec *codec) rbtree_node = rb_entry(next, struct snd_soc_rbtree_node, node); next = rb_next(&rbtree_node->node); rb_erase(&rbtree_node->node, &rbtree_ctx->root); + kfree(rbtree_node->block); kfree(rbtree_node); } @@ -761,10 +890,9 @@ static int snd_soc_rbtree_cache_exit(struct snd_soc_codec *codec) static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec) { - struct snd_soc_rbtree_node *rbtree_node; struct snd_soc_rbtree_ctx *rbtree_ctx; - unsigned int val; unsigned int word_size; + unsigned int val; int i; int ret; @@ -778,28 +906,22 @@ static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec) if (!codec->reg_def_copy) return 0; - /* - * populate the rbtree with the initialized registers. All other - * registers will be inserted when they are first modified. - */ word_size = codec->driver->reg_word_size; for (i = 0; i < codec->driver->reg_cache_size; ++i) { - val = snd_soc_get_cache_val(codec->reg_def_copy, i, word_size); + val = snd_soc_get_cache_val(codec->reg_def_copy, i, + word_size); if (!val) continue; - rbtree_node = kzalloc(sizeof *rbtree_node, GFP_KERNEL); - if (!rbtree_node) { - ret = -ENOMEM; - snd_soc_cache_exit(codec); - break; - } - rbtree_node->reg = i; - rbtree_node->value = val; - rbtree_node->defval = val; - snd_soc_rbtree_insert(&rbtree_ctx->root, rbtree_node); + ret = snd_soc_rbtree_cache_write(codec, i, val); + if (ret) + goto err; } return 0; + +err: + snd_soc_cache_exit(codec); + return ret; } #ifdef CONFIG_SND_SOC_CACHE_LZO -- cgit v0.10.2 From 7e146b55866d1176f80439fdc2be2915147468b3 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Thu, 19 May 2011 13:45:30 +0100 Subject: ASoC: soc-cache: Cache a pointer to the last accessed rbnode Whenever we are doing a read or a write through the rbtree code, we'll cache a pointer to the rbnode. To avoid looking up the register everytime we do a read or a write, we first check if it can be found in the cached register block, otherwise we traverse the rbtree and finally cache the rbnode for future use. Signed-off-by: Dimitris Papastamos Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index 78c9869..5cd23da 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -604,6 +604,7 @@ struct snd_soc_rbtree_node { struct snd_soc_rbtree_ctx { struct rb_root root; + struct snd_soc_rbtree_node *cached_rbnode; }; static inline void snd_soc_rbtree_get_base_top_reg( @@ -780,11 +781,28 @@ static int snd_soc_rbtree_cache_write(struct snd_soc_codec *codec, struct rb_node *node; unsigned int val; unsigned int reg_tmp; + unsigned int base_reg, top_reg; unsigned int pos; int i; int ret; rbtree_ctx = codec->reg_cache; + /* look up the required register in the cached rbnode */ + rbnode = rbtree_ctx->cached_rbnode; + if (rbnode) { + snd_soc_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg); + if (reg >= base_reg && reg <= top_reg) { + reg_tmp = reg - base_reg; + val = snd_soc_rbtree_get_register(rbnode, reg_tmp); + if (val == value) + return 0; + snd_soc_rbtree_set_register(rbnode, reg_tmp, value); + return 0; + } + } + /* if we can't locate it in the cached rbnode we'll have + * to traverse the rbtree looking for it. + */ rbnode = snd_soc_rbtree_lookup(&rbtree_ctx->root, reg); if (rbnode) { reg_tmp = reg - rbnode->base_reg; @@ -792,6 +810,7 @@ static int snd_soc_rbtree_cache_write(struct snd_soc_codec *codec, if (val == value) return 0; snd_soc_rbtree_set_register(rbnode, reg_tmp, value); + rbtree_ctx->cached_rbnode = rbnode; } else { /* bail out early, no need to create the rbnode yet */ if (!value) @@ -813,6 +832,7 @@ static int snd_soc_rbtree_cache_write(struct snd_soc_codec *codec, reg, value); if (ret) return ret; + rbtree_ctx->cached_rbnode = rbnode_tmp; return 0; } } @@ -835,6 +855,7 @@ static int snd_soc_rbtree_cache_write(struct snd_soc_codec *codec, } snd_soc_rbtree_set_register(rbnode, 0, value); snd_soc_rbtree_insert(&rbtree_ctx->root, rbnode); + rbtree_ctx->cached_rbnode = rbnode; } return 0; @@ -845,13 +866,28 @@ static int snd_soc_rbtree_cache_read(struct snd_soc_codec *codec, { struct snd_soc_rbtree_ctx *rbtree_ctx; struct snd_soc_rbtree_node *rbnode; + unsigned int base_reg, top_reg; unsigned int reg_tmp; rbtree_ctx = codec->reg_cache; + /* look up the required register in the cached rbnode */ + rbnode = rbtree_ctx->cached_rbnode; + if (rbnode) { + snd_soc_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg); + if (reg >= base_reg && reg <= top_reg) { + reg_tmp = reg - base_reg; + *value = snd_soc_rbtree_get_register(rbnode, reg_tmp); + return 0; + } + } + /* if we can't locate it in the cached rbnode we'll have + * to traverse the rbtree looking for it. + */ rbnode = snd_soc_rbtree_lookup(&rbtree_ctx->root, reg); if (rbnode) { reg_tmp = reg - rbnode->base_reg; *value = snd_soc_rbtree_get_register(rbnode, reg_tmp); + rbtree_ctx->cached_rbnode = rbnode; } else { /* uninitialized registers default to 0 */ *value = 0; @@ -902,6 +938,7 @@ static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec) rbtree_ctx = codec->reg_cache; rbtree_ctx->root = RB_ROOT; + rbtree_ctx->cached_rbnode = NULL; if (!codec->reg_def_copy) return 0; -- cgit v0.10.2 From 60c655e62f1ee85b9144fa259b3d1064ddbbe847 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 11 May 2011 01:02:35 +0200 Subject: ASoC: Convert 16x16 write to use cpu_to_be16() Make it clear what we're doing. Signed-off-by: Mark Brown Acked-by: Liam Girdwood diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index 5cd23da..6d6395f 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -366,14 +366,12 @@ static unsigned int snd_soc_16_16_read(struct snd_soc_codec *codec, static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { - u8 data[4]; + u16 data[2]; - data[0] = (reg >> 8) & 0xff; - data[1] = reg & 0xff; - data[2] = (value >> 8) & 0xff; - data[3] = value & 0xff; + data[0] = cpu_to_be16(reg); + data[1] = cpu_to_be16(value); - return do_hw_write(codec, reg, value, data, 4); + return do_hw_write(codec, reg, value, data, sizeof(data)); } #if defined(CONFIG_SPI_MASTER) -- cgit v0.10.2 From 0ffe296addcfb8414ebad3d399859f9bf8f955d2 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 23 May 2011 20:45:57 +0900 Subject: ASoC: sh: fsi: tidyup parameter of fsi_stream_push It is possible to create buff_len and period_len from substream->runtime. This patch is preparation of tidyup unclear variable naming patch. Signed-off-by: Kuninori Morimoto Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 4a9da6b..7025885 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -344,16 +344,15 @@ static u32 fsi_get_port_shift(struct fsi_priv *fsi, int is_play) static void fsi_stream_push(struct fsi_priv *fsi, int is_play, - struct snd_pcm_substream *substream, - u32 buffer_len, - u32 period_len) + struct snd_pcm_substream *substream) { struct fsi_stream *io = fsi_get_stream(fsi, is_play); + struct snd_pcm_runtime *runtime = substream->runtime; io->substream = substream; - io->buff_len = buffer_len; + io->buff_len = frames_to_bytes(runtime, runtime->buffer_size); io->buff_offset = 0; - io->period_len = period_len; + io->period_len = frames_to_bytes(runtime, runtime->period_size); io->period_num = 0; io->oerr_num = -1; /* ignore 1st err */ io->uerr_num = -1; /* ignore 1st err */ @@ -844,15 +843,12 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct fsi_priv *fsi = fsi_get_priv(substream); - struct snd_pcm_runtime *runtime = substream->runtime; int is_play = fsi_is_play(substream); int ret = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_START: - fsi_stream_push(fsi, is_play, substream, - frames_to_bytes(runtime, runtime->buffer_size), - frames_to_bytes(runtime, runtime->period_size)); + fsi_stream_push(fsi, is_play, substream); ret = is_play ? fsi_data_push(fsi) : fsi_data_pop(fsi); fsi_irq_enable(fsi, is_play); fsi_port_start(fsi); -- cgit v0.10.2 From 4f56cde17e3373219b56d2e9a91dbcd0ad228af7 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 23 May 2011 20:46:18 +0900 Subject: ASoC: sh: fsi: add fsi_set_master_clk Current FSI driver is using set_rate call back function which is for master mode. By this patch, it is used from fsi_set_master_clk. This patch is preparation of cleanup suspend/resume patch. Signed-off-by: Kuninori Morimoto Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 7025885..83f6fdc 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -558,6 +558,82 @@ static void fsi_spdif_clk_ctrl(struct fsi_priv *fsi, int enable) /* * clock function */ +static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi, + long rate, int enable) +{ + struct fsi_master *master = fsi_get_master(fsi); + set_rate_func set_rate = fsi_get_info_set_rate(master); + int fsi_ver = master->core->ver; + int ret; + + ret = set_rate(dev, fsi_is_port_a(fsi), rate, enable); + if (ret < 0) /* error */ + return ret; + + if (!enable) + return 0; + + if (ret > 0) { + u32 data = 0; + + switch (ret & SH_FSI_ACKMD_MASK) { + default: + /* FALL THROUGH */ + case SH_FSI_ACKMD_512: + data |= (0x0 << 12); + break; + case SH_FSI_ACKMD_256: + data |= (0x1 << 12); + break; + case SH_FSI_ACKMD_128: + data |= (0x2 << 12); + break; + case SH_FSI_ACKMD_64: + data |= (0x3 << 12); + break; + case SH_FSI_ACKMD_32: + if (fsi_ver < 2) + dev_err(dev, "unsupported ACKMD\n"); + else + data |= (0x4 << 12); + break; + } + + switch (ret & SH_FSI_BPFMD_MASK) { + default: + /* FALL THROUGH */ + case SH_FSI_BPFMD_32: + data |= (0x0 << 8); + break; + case SH_FSI_BPFMD_64: + data |= (0x1 << 8); + break; + case SH_FSI_BPFMD_128: + data |= (0x2 << 8); + break; + case SH_FSI_BPFMD_256: + data |= (0x3 << 8); + break; + case SH_FSI_BPFMD_512: + data |= (0x4 << 8); + break; + case SH_FSI_BPFMD_16: + if (fsi_ver < 2) + dev_err(dev, "unsupported ACKMD\n"); + else + data |= (0x7 << 8); + break; + } + + fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data); + udelay(10); + ret = 0; + } + + return ret; + +} + #define fsi_module_init(m, d) __fsi_module_clk_ctrl(m, d, 1) #define fsi_module_kill(m, d) __fsi_module_clk_ctrl(m, d, 0) static void __fsi_module_clk_ctrl(struct fsi_master *master, @@ -826,13 +902,11 @@ static void fsi_dai_shutdown(struct snd_pcm_substream *substream, { struct fsi_priv *fsi = fsi_get_priv(substream); int is_play = fsi_is_play(substream); - struct fsi_master *master = fsi_get_master(fsi); - set_rate_func set_rate = fsi_get_info_set_rate(master); fsi_irq_disable(fsi, is_play); if (fsi_is_clk_master(fsi)) - set_rate(dai->dev, fsi_is_port_a(fsi), fsi->rate, 0); + fsi_set_master_clk(dai->dev, fsi, fsi->rate, 0); fsi->rate = 0; @@ -960,79 +1034,19 @@ static int fsi_dai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct fsi_priv *fsi = fsi_get_priv(substream); - struct fsi_master *master = fsi_get_master(fsi); - set_rate_func set_rate = fsi_get_info_set_rate(master); - int fsi_ver = master->core->ver; long rate = params_rate(params); int ret; if (!fsi_is_clk_master(fsi)) return 0; - ret = set_rate(dai->dev, fsi_is_port_a(fsi), rate, 1); - if (ret < 0) /* error */ + ret = fsi_set_master_clk(dai->dev, fsi, rate, 1); + if (ret < 0) return ret; fsi->rate = rate; - if (ret > 0) { - u32 data = 0; - - switch (ret & SH_FSI_ACKMD_MASK) { - default: - /* FALL THROUGH */ - case SH_FSI_ACKMD_512: - data |= (0x0 << 12); - break; - case SH_FSI_ACKMD_256: - data |= (0x1 << 12); - break; - case SH_FSI_ACKMD_128: - data |= (0x2 << 12); - break; - case SH_FSI_ACKMD_64: - data |= (0x3 << 12); - break; - case SH_FSI_ACKMD_32: - if (fsi_ver < 2) - dev_err(dai->dev, "unsupported ACKMD\n"); - else - data |= (0x4 << 12); - break; - } - - switch (ret & SH_FSI_BPFMD_MASK) { - default: - /* FALL THROUGH */ - case SH_FSI_BPFMD_32: - data |= (0x0 << 8); - break; - case SH_FSI_BPFMD_64: - data |= (0x1 << 8); - break; - case SH_FSI_BPFMD_128: - data |= (0x2 << 8); - break; - case SH_FSI_BPFMD_256: - data |= (0x3 << 8); - break; - case SH_FSI_BPFMD_512: - data |= (0x4 << 8); - break; - case SH_FSI_BPFMD_16: - if (fsi_ver < 2) - dev_err(dai->dev, "unsupported ACKMD\n"); - else - data |= (0x7 << 8); - break; - } - - fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data); - udelay(10); - ret = 0; - } return ret; - } static struct snd_soc_dai_ops fsi_dai_ops = { @@ -1301,8 +1315,7 @@ static int fsi_remove(struct platform_device *pdev) } static void __fsi_suspend(struct fsi_priv *fsi, - struct device *dev, - set_rate_func set_rate) + struct device *dev) { fsi->saved_do_fmt = fsi_reg_read(fsi, DO_FMT); fsi->saved_di_fmt = fsi_reg_read(fsi, DI_FMT); @@ -1311,12 +1324,11 @@ static void __fsi_suspend(struct fsi_priv *fsi, fsi->saved_out_sel = fsi_reg_read(fsi, OUT_SEL); if (fsi_is_clk_master(fsi)) - set_rate(dev, fsi_is_port_a(fsi), fsi->rate, 0); + fsi_set_master_clk(dev, fsi, fsi->rate, 0); } static void __fsi_resume(struct fsi_priv *fsi, - struct device *dev, - set_rate_func set_rate) + struct device *dev) { fsi_reg_write(fsi, DO_FMT, fsi->saved_do_fmt); fsi_reg_write(fsi, DI_FMT, fsi->saved_di_fmt); @@ -1325,18 +1337,17 @@ static void __fsi_resume(struct fsi_priv *fsi, fsi_reg_write(fsi, OUT_SEL, fsi->saved_out_sel); if (fsi_is_clk_master(fsi)) - set_rate(dev, fsi_is_port_a(fsi), fsi->rate, 1); + fsi_set_master_clk(dev, fsi, fsi->rate, 1); } static int fsi_suspend(struct device *dev) { struct fsi_master *master = dev_get_drvdata(dev); - set_rate_func set_rate = fsi_get_info_set_rate(master); pm_runtime_get_sync(dev); - __fsi_suspend(&master->fsia, dev, set_rate); - __fsi_suspend(&master->fsib, dev, set_rate); + __fsi_suspend(&master->fsia, dev); + __fsi_suspend(&master->fsib, dev); master->saved_a_mclk = fsi_core_read(master, a_mclk); master->saved_b_mclk = fsi_core_read(master, b_mclk); @@ -1355,7 +1366,6 @@ static int fsi_suspend(struct device *dev) static int fsi_resume(struct device *dev) { struct fsi_master *master = dev_get_drvdata(dev); - set_rate_func set_rate = fsi_get_info_set_rate(master); pm_runtime_get_sync(dev); @@ -1368,8 +1378,8 @@ static int fsi_resume(struct device *dev) fsi_core_mask_set(master, iemsk, 0xffff, master->saved_iemsk); fsi_core_mask_set(master, imsk, 0xffff, master->saved_imsk); - __fsi_resume(&master->fsia, dev, set_rate); - __fsi_resume(&master->fsib, dev, set_rate); + __fsi_resume(&master->fsia, dev); + __fsi_resume(&master->fsib, dev); pm_runtime_put_sync(dev); -- cgit v0.10.2 From 1ddddd36353c40fbf8faad955fcc26e05f656121 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 23 May 2011 20:46:23 +0900 Subject: ASoC: sh: fsi: irq control moves to fsi_port_start/stop Using fsi_irq_enable/disable in fsi_port_start/stop is very natural. This patch is preparation of cleanup suspend/resume patch. Signed-off-by: Kuninori Morimoto Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 83f6fdc..643d256 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -654,15 +654,20 @@ static void __fsi_module_clk_ctrl(struct fsi_master *master, pm_runtime_put_sync(dev); } -#define fsi_port_start(f) __fsi_port_clk_ctrl(f, 1) -#define fsi_port_stop(f) __fsi_port_clk_ctrl(f, 0) -static void __fsi_port_clk_ctrl(struct fsi_priv *fsi, int enable) +#define fsi_port_start(f, i) __fsi_port_clk_ctrl(f, i, 1) +#define fsi_port_stop(f, i) __fsi_port_clk_ctrl(f, i, 0) +static void __fsi_port_clk_ctrl(struct fsi_priv *fsi, int is_play, int enable) { struct fsi_master *master = fsi_get_master(fsi); u32 soft = fsi_is_port_a(fsi) ? PASR : PBSR; u32 clk = fsi_is_port_a(fsi) ? CRA : CRB; int is_master = fsi_is_clk_master(fsi); + if (enable) + fsi_irq_enable(fsi, is_play); + else + fsi_irq_disable(fsi, is_play); + fsi_master_mask_set(master, SOFT_RST, soft, (enable) ? soft : 0); if (is_master) fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0); @@ -901,9 +906,6 @@ static void fsi_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct fsi_priv *fsi = fsi_get_priv(substream); - int is_play = fsi_is_play(substream); - - fsi_irq_disable(fsi, is_play); if (fsi_is_clk_master(fsi)) fsi_set_master_clk(dai->dev, fsi, fsi->rate, 0); @@ -924,12 +926,10 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_START: fsi_stream_push(fsi, is_play, substream); ret = is_play ? fsi_data_push(fsi) : fsi_data_pop(fsi); - fsi_irq_enable(fsi, is_play); - fsi_port_start(fsi); + fsi_port_start(fsi, is_play); break; case SNDRV_PCM_TRIGGER_STOP: - fsi_port_stop(fsi); - fsi_irq_disable(fsi, is_play); + fsi_port_stop(fsi, is_play); fsi_stream_pop(fsi, is_play); break; } -- cgit v0.10.2 From 2e651bafa959c6e2620601c2c2e9b7c26f6a9c1a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 23 May 2011 20:46:03 +0900 Subject: ASoC: sh: fsi: tidyup unclear variable naming Some variables on this driver were a unclear naming, and were different unit (byte, frame, sample). And some functions had wrong name (ex. it returned "sample width" but name was "fsi_get_frame_width"). This patch tidy-up this issue, and the minimum unit become "sample". Special thanks to Takashi YOSHII. Signed-off-by: Kuninori Morimoto Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 643d256..fec1a7d 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -118,10 +118,38 @@ typedef int (*set_rate_func)(struct device *dev, int is_porta, int rate, int ena /* * FSI driver use below type name for variable * - * xxx_len : data length - * xxx_width : data width - * xxx_offset : data offset * xxx_num : number of data + * xxx_pos : position of data + * xxx_capa : capacity of data + */ + +/* + * period/frame/sample image + * + * ex) PCM (2ch) + * + * period pos period pos + * [n] [n + 1] + * |<-------------------- period--------------------->| + * ==|============================================ ... =|== + * | | + * ||<----- frame ----->|<------ frame ----->| ... | + * |+--------------------+--------------------+- ... | + * ||[ sample ][ sample ]|[ sample ][ sample ]| ... | + * |+--------------------+--------------------+- ... | + * ==|============================================ ... =|== + */ + +/* + * FSI FIFO image + * + * | | + * | | + * | [ sample ] | + * | [ sample ] | + * | [ sample ] | + * | [ sample ] | + * --> go to codecs */ /* @@ -131,12 +159,11 @@ typedef int (*set_rate_func)(struct device *dev, int is_porta, int rate, int ena struct fsi_stream { struct snd_pcm_substream *substream; - int fifo_max_num; - - int buff_offset; - int buff_len; - int period_len; - int period_num; + int fifo_sample_capa; /* sample capacity of FSI FIFO */ + int buff_sample_capa; /* sample capacity of ALSA buffer */ + int buff_sample_pos; /* sample position of ALSA buffer */ + int period_samples; /* sample number / 1 period */ + int period_pos; /* current period position */ int uerr_num; int oerr_num; @@ -342,6 +369,16 @@ static u32 fsi_get_port_shift(struct fsi_priv *fsi, int is_play) return shift; } +static int fsi_frame2sample(struct fsi_priv *fsi, int frames) +{ + return frames * fsi->chan_num; +} + +static int fsi_sample2frame(struct fsi_priv *fsi, int samples) +{ + return samples / fsi->chan_num; +} + static void fsi_stream_push(struct fsi_priv *fsi, int is_play, struct snd_pcm_substream *substream) @@ -350,10 +387,10 @@ static void fsi_stream_push(struct fsi_priv *fsi, struct snd_pcm_runtime *runtime = substream->runtime; io->substream = substream; - io->buff_len = frames_to_bytes(runtime, runtime->buffer_size); - io->buff_offset = 0; - io->period_len = frames_to_bytes(runtime, runtime->period_size); - io->period_num = 0; + io->buff_sample_capa = fsi_frame2sample(fsi, runtime->buffer_size); + io->buff_sample_pos = 0; + io->period_samples = fsi_frame2sample(fsi, runtime->period_size); + io->period_pos = 0; io->oerr_num = -1; /* ignore 1st err */ io->uerr_num = -1; /* ignore 1st err */ } @@ -371,47 +408,26 @@ static void fsi_stream_pop(struct fsi_priv *fsi, int is_play) dev_err(dai->dev, "under_run = %d\n", io->uerr_num); io->substream = NULL; - io->buff_len = 0; - io->buff_offset = 0; - io->period_len = 0; - io->period_num = 0; + io->buff_sample_capa = 0; + io->buff_sample_pos = 0; + io->period_samples = 0; + io->period_pos = 0; io->oerr_num = 0; io->uerr_num = 0; } -static int fsi_get_fifo_data_num(struct fsi_priv *fsi, int is_play) +static int fsi_get_current_fifo_samples(struct fsi_priv *fsi, int is_play) { u32 status; - int data_num; + int frames; status = is_play ? fsi_reg_read(fsi, DOFF_ST) : fsi_reg_read(fsi, DIFF_ST); - data_num = 0x1ff & (status >> 8); - data_num *= fsi->chan_num; - - return data_num; -} - -static int fsi_len2num(int len, int width) -{ - return len / width; -} + frames = 0x1ff & (status >> 8); -#define fsi_num2offset(a, b) fsi_num2len(a, b) -static int fsi_num2len(int num, int width) -{ - return num * width; -} - -static int fsi_get_frame_width(struct fsi_priv *fsi, int is_play) -{ - struct fsi_stream *io = fsi_get_stream(fsi, is_play); - struct snd_pcm_substream *substream = io->substream; - struct snd_pcm_runtime *runtime = substream->runtime; - - return frames_to_bytes(runtime, 1) / fsi->chan_num; + return fsi_frame2sample(fsi, frames); } static void fsi_count_fifo_err(struct fsi_priv *fsi) @@ -443,8 +459,10 @@ static u8 *fsi_dma_get_area(struct fsi_priv *fsi, int stream) { int is_play = fsi_stream_is_play(stream); struct fsi_stream *io = fsi_get_stream(fsi, is_play); + struct snd_pcm_runtime *runtime = io->substream->runtime; - return io->substream->runtime->dma_area + io->buff_offset; + return runtime->dma_area + + samples_to_bytes(runtime, io->buff_sample_pos); } static void fsi_dma_soft_push16(struct fsi_priv *fsi, int num) @@ -683,13 +701,14 @@ static void fsi_fifo_init(struct fsi_priv *fsi, struct fsi_master *master = fsi_get_master(fsi); struct fsi_stream *io = fsi_get_stream(fsi, is_play); u32 shift, i; + int frame_capa; /* get on-chip RAM capacity */ shift = fsi_master_read(master, FIFO_SZ); shift >>= fsi_get_port_shift(fsi, is_play); shift &= FIFO_SZ_MASK; - io->fifo_max_num = 256 << shift; - dev_dbg(dai->dev, "fifo = %d words\n", io->fifo_max_num); + frame_capa = 256 << shift; + dev_dbg(dai->dev, "fifo = %d words\n", frame_capa); /* * The maximum number of sample data varies depending @@ -711,9 +730,11 @@ static void fsi_fifo_init(struct fsi_priv *fsi, * 8 channels: 32 ( 32 x 8 = 256) */ for (i = 1; i < fsi->chan_num; i <<= 1) - io->fifo_max_num >>= 1; + frame_capa >>= 1; dev_dbg(dai->dev, "%d channel %d store\n", - fsi->chan_num, io->fifo_max_num); + fsi->chan_num, frame_capa); + + io->fifo_sample_capa = fsi_frame2sample(fsi, frame_capa); /* * set interrupt generation factor @@ -734,10 +755,10 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream) struct snd_pcm_substream *substream = NULL; int is_play = fsi_stream_is_play(stream); struct fsi_stream *io = fsi_get_stream(fsi, is_play); - int data_residue_num; - int data_num; - int data_num_max; - int ch_width; + int sample_residues; + int sample_width; + int samples; + int samples_max; int over_period; void (*fn)(struct fsi_priv *fsi, int size); @@ -753,36 +774,35 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream) /* FSI FIFO has limit. * So, this driver can not send periods data at a time */ - if (io->buff_offset >= - fsi_num2offset(io->period_num + 1, io->period_len)) { + if (io->buff_sample_pos >= + io->period_samples * (io->period_pos + 1)) { over_period = 1; - io->period_num = (io->period_num + 1) % runtime->periods; + io->period_pos = (io->period_pos + 1) % runtime->periods; - if (0 == io->period_num) - io->buff_offset = 0; + if (0 == io->period_pos) + io->buff_sample_pos = 0; } - /* get 1 channel data width */ - ch_width = fsi_get_frame_width(fsi, is_play); + /* get 1 sample data width */ + sample_width = samples_to_bytes(runtime, 1); - /* get residue data number of alsa */ - data_residue_num = fsi_len2num(io->buff_len - io->buff_offset, - ch_width); + /* get number of residue samples */ + sample_residues = io->buff_sample_capa - io->buff_sample_pos; if (is_play) { /* * for play-back * - * data_num_max : number of FSI fifo free space - * data_num : number of ALSA residue data + * samples_max : number of FSI fifo free samples space + * samples : number of ALSA residue samples */ - data_num_max = io->fifo_max_num * fsi->chan_num; - data_num_max -= fsi_get_fifo_data_num(fsi, is_play); + samples_max = io->fifo_sample_capa; + samples_max -= fsi_get_current_fifo_samples(fsi, is_play); - data_num = data_residue_num; + samples = sample_residues; - switch (ch_width) { + switch (sample_width) { case 2: fn = fsi_dma_soft_push16; break; @@ -796,13 +816,13 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream) /* * for capture * - * data_num_max : number of ALSA free space - * data_num : number of data in FSI fifo + * samples_max : number of ALSA free samples space + * samples : number of samples in FSI fifo */ - data_num_max = data_residue_num; - data_num = fsi_get_fifo_data_num(fsi, is_play); + samples_max = sample_residues; + samples = fsi_get_current_fifo_samples(fsi, is_play); - switch (ch_width) { + switch (sample_width) { case 2: fn = fsi_dma_soft_pop16; break; @@ -814,12 +834,12 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream) } } - data_num = min(data_num, data_num_max); + samples = min(samples, samples_max); - fn(fsi, data_num); + fn(fsi, samples); - /* update buff_offset */ - io->buff_offset += fsi_num2offset(data_num, ch_width); + /* update buff_sample_pos */ + io->buff_sample_pos += samples; if (over_period) snd_pcm_period_elapsed(substream); @@ -1107,16 +1127,14 @@ static int fsi_hw_free(struct snd_pcm_substream *substream) static snd_pcm_uframes_t fsi_pointer(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; struct fsi_priv *fsi = fsi_get_priv(substream); struct fsi_stream *io = fsi_get_stream(fsi, fsi_is_play(substream)); - long location; + int samples_pos = io->buff_sample_pos - 1; - location = (io->buff_offset - 1); - if (location < 0) - location = 0; + if (samples_pos < 0) + samples_pos = 0; - return bytes_to_frames(runtime, location); + return fsi_sample2frame(fsi, samples_pos); } static struct snd_pcm_ops fsi_pcm_ops = { -- cgit v0.10.2 From 9478e0b60fb4a7adde72d4a86b826d396b607a61 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 23 May 2011 20:46:07 +0900 Subject: ASoC: sh: fsi: remove pm_runtime from fsi_dai_set_fmt. pm_runtime_get/put_sync were used to access FSI register in fsi_dai_set_fmt which is called when ALSA probe. But this register value will disappear after pm_runtime_put_sync if platform is supporting RuntimePM. To solve this issue, this patch adds new variable for format, and remove pm_runtime_get/put_sync from fsi_dai_set_fmt. Signed-off-by: Kuninori Morimoto Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index fec1a7d..a1081c7 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -176,8 +176,12 @@ struct fsi_priv { struct fsi_stream playback; struct fsi_stream capture; + u32 do_fmt; + u32 di_fmt; + int chan_num:16; int clk_master:1; + int spdif:1; long rate; @@ -298,6 +302,11 @@ static int fsi_is_port_a(struct fsi_priv *fsi) return fsi->master->base == fsi->base; } +static int fsi_is_spdif(struct fsi_priv *fsi) +{ + return fsi->spdif; +} + static struct snd_soc_dai *fsi_get_dai(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -893,11 +902,16 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, { struct fsi_priv *fsi = fsi_get_priv(substream); u32 flags = fsi_get_info_flags(fsi); - u32 data; + u32 data = 0; int is_play = fsi_is_play(substream); pm_runtime_get_sync(dai->dev); + /* clock setting */ + if (fsi_is_clk_master(fsi)) + data = DIMD | DOMD; + + fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data); /* clock inversion (CKG2) */ data = 0; @@ -912,6 +926,16 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, fsi_reg_write(fsi, CKG2, data); + /* set format */ + fsi_reg_write(fsi, DO_FMT, fsi->do_fmt); + fsi_reg_write(fsi, DI_FMT, fsi->di_fmt); + + /* spdif ? */ + if (fsi_is_spdif(fsi)) { + fsi_spdif_clk_ctrl(fsi, 1); + fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD); + } + /* irq clear */ fsi_irq_disable(fsi, is_play); fsi_irq_clear_status(fsi); @@ -974,8 +998,8 @@ static int fsi_set_fmt_dai(struct fsi_priv *fsi, unsigned int fmt) return -EINVAL; } - fsi_reg_write(fsi, DO_FMT, data); - fsi_reg_write(fsi, DI_FMT, data); + fsi->do_fmt = data; + fsi->di_fmt = data; return 0; } @@ -990,11 +1014,10 @@ static int fsi_set_fmt_spdif(struct fsi_priv *fsi) data = CR_BWS_16 | CR_DTMD_SPDIF_PCM | CR_PCM; fsi->chan_num = 2; - fsi_spdif_clk_ctrl(fsi, 1); - fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD); + fsi->spdif = 1; - fsi_reg_write(fsi, DO_FMT, data); - fsi_reg_write(fsi, DI_FMT, data); + fsi->do_fmt = data; + fsi->di_fmt = data; return 0; } @@ -1005,32 +1028,24 @@ static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) struct fsi_master *master = fsi_get_master(fsi); set_rate_func set_rate = fsi_get_info_set_rate(master); u32 flags = fsi_get_info_flags(fsi); - u32 data = 0; int ret; - pm_runtime_get_sync(dai->dev); - /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: - data = DIMD | DOMD; fsi->clk_master = 1; break; case SND_SOC_DAIFMT_CBS_CFS: break; default: - ret = -EINVAL; - goto set_fmt_exit; + return -EINVAL; } if (fsi_is_clk_master(fsi) && !set_rate) { dev_err(dai->dev, "platform doesn't have set_rate\n"); - ret = -EINVAL; - goto set_fmt_exit; + return -EINVAL; } - fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data); - /* set format */ switch (flags & SH_FSI_FMT_MASK) { case SH_FSI_FMT_DAI: @@ -1043,9 +1058,6 @@ static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) ret = -EINVAL; } -set_fmt_exit: - pm_runtime_put_sync(dai->dev); - return ret; } -- cgit v0.10.2 From 2da658927c9e28425ecb6b6a7a03094a012e8620 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 23 May 2011 20:46:13 +0900 Subject: ASoC: sh: fsi: make sure fsi_stream_push/pop access by spin lock fsi_stream_push/pop might be called in same time. This patch protect it. Signed-off-by: Kuninori Morimoto Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index a1081c7..a00fe37 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -394,7 +394,10 @@ static void fsi_stream_push(struct fsi_priv *fsi, { struct fsi_stream *io = fsi_get_stream(fsi, is_play); struct snd_pcm_runtime *runtime = substream->runtime; + struct fsi_master *master = fsi_get_master(fsi); + unsigned long flags; + spin_lock_irqsave(&master->lock, flags); io->substream = substream; io->buff_sample_capa = fsi_frame2sample(fsi, runtime->buffer_size); io->buff_sample_pos = 0; @@ -402,13 +405,17 @@ static void fsi_stream_push(struct fsi_priv *fsi, io->period_pos = 0; io->oerr_num = -1; /* ignore 1st err */ io->uerr_num = -1; /* ignore 1st err */ + spin_unlock_irqrestore(&master->lock, flags); } static void fsi_stream_pop(struct fsi_priv *fsi, int is_play) { struct fsi_stream *io = fsi_get_stream(fsi, is_play); struct snd_soc_dai *dai = fsi_get_dai(io->substream); + struct fsi_master *master = fsi_get_master(fsi); + unsigned long flags; + spin_lock_irqsave(&master->lock, flags); if (io->oerr_num > 0) dev_err(dai->dev, "over_run = %d\n", io->oerr_num); @@ -423,6 +430,7 @@ static void fsi_stream_pop(struct fsi_priv *fsi, int is_play) io->period_pos = 0; io->oerr_num = 0; io->uerr_num = 0; + spin_unlock_irqrestore(&master->lock, flags); } static int fsi_get_current_fifo_samples(struct fsi_priv *fsi, int is_play) -- cgit v0.10.2 From 4c481253311dd5940ae7c26eaff6c6f63bd41fd8 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 23 May 2011 20:46:30 +0900 Subject: ASoC: sh: fsi: remove fsi_module_init/kill FSIA/B ports is enabled by default when power-on, and current FSI is supporting RuntimePM. In addition, current fsi_module_init/kill doesn't care simultaneous playback/recorde. This mean FSI port control is not needed. This patch remove fsi_module_init/kill Signed-off-by: Kuninori Morimoto Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index a00fe37..c608546 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -669,32 +669,11 @@ static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi, } -#define fsi_module_init(m, d) __fsi_module_clk_ctrl(m, d, 1) -#define fsi_module_kill(m, d) __fsi_module_clk_ctrl(m, d, 0) -static void __fsi_module_clk_ctrl(struct fsi_master *master, - struct device *dev, - int enable) -{ - pm_runtime_get_sync(dev); - - if (enable) { - /* enable only SR */ - fsi_master_mask_set(master, SOFT_RST, FSISR, FSISR); - fsi_master_mask_set(master, SOFT_RST, PASR | PBSR, 0); - } else { - /* clear all registers */ - fsi_master_mask_set(master, SOFT_RST, FSISR, 0); - } - - pm_runtime_put_sync(dev); -} - #define fsi_port_start(f, i) __fsi_port_clk_ctrl(f, i, 1) #define fsi_port_stop(f, i) __fsi_port_clk_ctrl(f, i, 0) static void __fsi_port_clk_ctrl(struct fsi_priv *fsi, int is_play, int enable) { struct fsi_master *master = fsi_get_master(fsi); - u32 soft = fsi_is_port_a(fsi) ? PASR : PBSR; u32 clk = fsi_is_port_a(fsi) ? CRA : CRB; int is_master = fsi_is_clk_master(fsi); @@ -703,7 +682,6 @@ static void __fsi_port_clk_ctrl(struct fsi_priv *fsi, int is_play, int enable) else fsi_irq_disable(fsi, is_play); - fsi_master_mask_set(master, SOFT_RST, soft, (enable) ? soft : 0); if (is_master) fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0); } @@ -1294,8 +1272,6 @@ static int fsi_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); dev_set_drvdata(&pdev->dev, master); - fsi_module_init(master, &pdev->dev); - ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED, id_entry->name, master); if (ret) { @@ -1338,8 +1314,6 @@ static int fsi_remove(struct platform_device *pdev) master = dev_get_drvdata(&pdev->dev); - fsi_module_kill(master, &pdev->dev); - free_irq(master->irq, master); pm_runtime_disable(&pdev->dev); @@ -1394,8 +1368,6 @@ static int fsi_suspend(struct device *dev) master->saved_clk_rst = fsi_master_read(master, CLK_RST); master->saved_soft_rst = fsi_master_read(master, SOFT_RST); - fsi_module_kill(master, dev); - pm_runtime_put_sync(dev); return 0; @@ -1407,8 +1379,6 @@ static int fsi_resume(struct device *dev) pm_runtime_get_sync(dev); - fsi_module_init(master, dev); - fsi_master_mask_set(master, SOFT_RST, 0xffff, master->saved_soft_rst); fsi_master_mask_set(master, CLK_RST, 0xffff, master->saved_clk_rst); fsi_core_mask_set(master, a_mclk, 0xffff, master->saved_a_mclk); -- cgit v0.10.2 From cda828cafe9df9a8b0687f1b8a17be2cd9cf1950 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 23 May 2011 20:46:35 +0900 Subject: ASoC: sh: fsi: cleanup suspend/resume Current FSI driver was using saved_xxx variable for suspend/resume. OTOH, the start and stop of power/clock are controlled by fsi_hw_startup/fsi_hw_shutdown in current FSI driver. The all necessary registers value are set by fsi_hw_startup. So, if fsi_hw_shutdown is called when "suspend" is generated, and fsi_hw_startup is called at "resume", the saved_xxx are not needed. Signed-off-by: Kuninori Morimoto Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index c608546..507c02b 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -184,13 +184,6 @@ struct fsi_priv { int spdif:1; long rate; - - /* for suspend/resume */ - u32 saved_do_fmt; - u32 saved_di_fmt; - u32 saved_ckg1; - u32 saved_ckg2; - u32 saved_out_sel; }; struct fsi_core { @@ -211,14 +204,6 @@ struct fsi_master { struct fsi_core *core; struct sh_fsi_platform_info *info; spinlock_t lock; - - /* for suspend/resume */ - u32 saved_a_mclk; - u32 saved_b_mclk; - u32 saved_iemsk; - u32 saved_imsk; - u32 saved_clk_rst; - u32 saved_soft_rst; }; /* @@ -388,6 +373,21 @@ static int fsi_sample2frame(struct fsi_priv *fsi, int samples) return samples / fsi->chan_num; } +static int fsi_stream_is_working(struct fsi_priv *fsi, + int is_play) +{ + struct fsi_stream *io = fsi_get_stream(fsi, is_play); + struct fsi_master *master = fsi_get_master(fsi); + unsigned long flags; + int ret; + + spin_lock_irqsave(&master->lock, flags); + ret = !!io->substream; + spin_unlock_irqrestore(&master->lock, flags); + + return ret; +} + static void fsi_stream_push(struct fsi_priv *fsi, int is_play, struct snd_pcm_substream *substream) @@ -666,7 +666,6 @@ static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi, } return ret; - } #define fsi_port_start(f, i) __fsi_port_clk_ctrl(f, i, 1) @@ -675,14 +674,13 @@ static void __fsi_port_clk_ctrl(struct fsi_priv *fsi, int is_play, int enable) { struct fsi_master *master = fsi_get_master(fsi); u32 clk = fsi_is_port_a(fsi) ? CRA : CRB; - int is_master = fsi_is_clk_master(fsi); if (enable) fsi_irq_enable(fsi, is_play); else fsi_irq_disable(fsi, is_play); - if (is_master) + if (fsi_is_clk_master(fsi)) fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0); } @@ -1327,48 +1325,43 @@ static int fsi_remove(struct platform_device *pdev) } static void __fsi_suspend(struct fsi_priv *fsi, + int is_play, struct device *dev) { - fsi->saved_do_fmt = fsi_reg_read(fsi, DO_FMT); - fsi->saved_di_fmt = fsi_reg_read(fsi, DI_FMT); - fsi->saved_ckg1 = fsi_reg_read(fsi, CKG1); - fsi->saved_ckg2 = fsi_reg_read(fsi, CKG2); - fsi->saved_out_sel = fsi_reg_read(fsi, OUT_SEL); + if (!fsi_stream_is_working(fsi, is_play)) + return; - if (fsi_is_clk_master(fsi)) - fsi_set_master_clk(dev, fsi, fsi->rate, 0); + fsi_port_stop(fsi, is_play); + fsi_hw_shutdown(fsi, is_play, dev); } static void __fsi_resume(struct fsi_priv *fsi, + int is_play, struct device *dev) { - fsi_reg_write(fsi, DO_FMT, fsi->saved_do_fmt); - fsi_reg_write(fsi, DI_FMT, fsi->saved_di_fmt); - fsi_reg_write(fsi, CKG1, fsi->saved_ckg1); - fsi_reg_write(fsi, CKG2, fsi->saved_ckg2); - fsi_reg_write(fsi, OUT_SEL, fsi->saved_out_sel); + if (!fsi_stream_is_working(fsi, is_play)) + return; - if (fsi_is_clk_master(fsi)) + fsi_hw_startup(fsi, is_play, dev); + + if (fsi_is_clk_master(fsi) && fsi->rate) fsi_set_master_clk(dev, fsi, fsi->rate, 1); + + fsi_port_start(fsi, is_play); + } static int fsi_suspend(struct device *dev) { struct fsi_master *master = dev_get_drvdata(dev); + struct fsi_priv *fsia = &master->fsia; + struct fsi_priv *fsib = &master->fsib; - pm_runtime_get_sync(dev); - - __fsi_suspend(&master->fsia, dev); - __fsi_suspend(&master->fsib, dev); + __fsi_suspend(fsia, 1, dev); + __fsi_suspend(fsia, 0, dev); - master->saved_a_mclk = fsi_core_read(master, a_mclk); - master->saved_b_mclk = fsi_core_read(master, b_mclk); - master->saved_iemsk = fsi_core_read(master, iemsk); - master->saved_imsk = fsi_core_read(master, imsk); - master->saved_clk_rst = fsi_master_read(master, CLK_RST); - master->saved_soft_rst = fsi_master_read(master, SOFT_RST); - - pm_runtime_put_sync(dev); + __fsi_suspend(fsib, 1, dev); + __fsi_suspend(fsib, 0, dev); return 0; } @@ -1376,20 +1369,14 @@ static int fsi_suspend(struct device *dev) static int fsi_resume(struct device *dev) { struct fsi_master *master = dev_get_drvdata(dev); + struct fsi_priv *fsia = &master->fsia; + struct fsi_priv *fsib = &master->fsib; - pm_runtime_get_sync(dev); - - fsi_master_mask_set(master, SOFT_RST, 0xffff, master->saved_soft_rst); - fsi_master_mask_set(master, CLK_RST, 0xffff, master->saved_clk_rst); - fsi_core_mask_set(master, a_mclk, 0xffff, master->saved_a_mclk); - fsi_core_mask_set(master, b_mclk, 0xffff, master->saved_b_mclk); - fsi_core_mask_set(master, iemsk, 0xffff, master->saved_iemsk); - fsi_core_mask_set(master, imsk, 0xffff, master->saved_imsk); - - __fsi_resume(&master->fsia, dev); - __fsi_resume(&master->fsib, dev); + __fsi_resume(fsia, 1, dev); + __fsi_resume(fsia, 0, dev); - pm_runtime_put_sync(dev); + __fsi_resume(fsib, 1, dev); + __fsi_resume(fsib, 0, dev); return 0; } -- cgit v0.10.2 From 23ca853392aebdaa56c8138746deb2002e03d827 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 23 May 2011 20:46:26 +0900 Subject: ASoC: sh: fsi: add fsi_hw_startup/shutdown This patch is preparation of cleanup suspend/resume patch. Signed-off-by: Kuninori Morimoto Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 507c02b..d2f17ce 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -689,7 +689,7 @@ static void __fsi_port_clk_ctrl(struct fsi_priv *fsi, int is_play, int enable) */ static void fsi_fifo_init(struct fsi_priv *fsi, int is_play, - struct snd_soc_dai *dai) + struct device *dev) { struct fsi_master *master = fsi_get_master(fsi); struct fsi_stream *io = fsi_get_stream(fsi, is_play); @@ -701,7 +701,7 @@ static void fsi_fifo_init(struct fsi_priv *fsi, shift >>= fsi_get_port_shift(fsi, is_play); shift &= FIFO_SZ_MASK; frame_capa = 256 << shift; - dev_dbg(dai->dev, "fifo = %d words\n", frame_capa); + dev_dbg(dev, "fifo = %d words\n", frame_capa); /* * The maximum number of sample data varies depending @@ -724,7 +724,7 @@ static void fsi_fifo_init(struct fsi_priv *fsi, */ for (i = 1; i < fsi->chan_num; i <<= 1) frame_capa >>= 1; - dev_dbg(dai->dev, "%d channel %d store\n", + dev_dbg(dev, "%d channel %d store\n", fsi->chan_num, frame_capa); io->fifo_sample_capa = fsi_frame2sample(fsi, frame_capa); @@ -881,15 +881,14 @@ static irqreturn_t fsi_interrupt(int irq, void *data) * dai ops */ -static int fsi_dai_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) +static int fsi_hw_startup(struct fsi_priv *fsi, + int is_play, + struct device *dev) { - struct fsi_priv *fsi = fsi_get_priv(substream); u32 flags = fsi_get_info_flags(fsi); u32 data = 0; - int is_play = fsi_is_play(substream); - pm_runtime_get_sync(dai->dev); + pm_runtime_get_sync(dev); /* clock setting */ if (fsi_is_clk_master(fsi)) @@ -925,22 +924,38 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, fsi_irq_clear_status(fsi); /* fifo init */ - fsi_fifo_init(fsi, is_play, dai); + fsi_fifo_init(fsi, is_play, dev); return 0; } +static void fsi_hw_shutdown(struct fsi_priv *fsi, + int is_play, + struct device *dev) +{ + if (fsi_is_clk_master(fsi)) + fsi_set_master_clk(dev, fsi, fsi->rate, 0); + + pm_runtime_put_sync(dev); +} + +static int fsi_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct fsi_priv *fsi = fsi_get_priv(substream); + int is_play = fsi_is_play(substream); + + return fsi_hw_startup(fsi, is_play, dai->dev); +} + static void fsi_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct fsi_priv *fsi = fsi_get_priv(substream); + int is_play = fsi_is_play(substream); - if (fsi_is_clk_master(fsi)) - fsi_set_master_clk(dai->dev, fsi, fsi->rate, 0); - + fsi_hw_shutdown(fsi, is_play, dai->dev); fsi->rate = 0; - - pm_runtime_put_sync(dai->dev); } static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, -- cgit v0.10.2 From fb1e9703af618c7ae0ee43f19778264c1804a50d Mon Sep 17 00:00:00 2001 From: Ben Gardiner Date: Tue, 24 May 2011 14:50:15 -0400 Subject: ASoC: davinci-pcm: trivial: make ping-pong params setup symmetrical The setup of the pong channel uses EDMA_CHAN_SLOT instead of & 0x3f as the setup of the ping channel does. Make the setup of ping and pong symmetric. There is no functional change introduced by this patch. Signed-off-by: Ben Gardiner Reviewed-by: Steven Faludi Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index 9d35b8c..0d04e0c 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -425,7 +425,8 @@ static int request_ping_pong(struct snd_pcm_substream *substream, edma_read_slot(link, &prtd->asp_params); prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f) | TCINTEN); - prtd->asp_params.opt |= TCCHEN | EDMA_TCC(prtd->ram_channel & 0x3f); + prtd->asp_params.opt |= TCCHEN | + EDMA_TCC(prtd->ram_channel & 0x3f); edma_write_slot(link, &prtd->asp_params); /* pong */ @@ -439,7 +440,7 @@ static int request_ping_pong(struct snd_pcm_substream *substream, prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f)); /* interrupt after every pong completion */ prtd->asp_params.opt |= TCINTEN | TCCHEN | - EDMA_TCC(EDMA_CHAN_SLOT(prtd->ram_channel)); + EDMA_TCC(prtd->ram_channel & 0x3f); edma_write_slot(link, &prtd->asp_params); /* ram */ -- cgit v0.10.2 From 8e56d5b834610504e232641565236ac60740a858 Mon Sep 17 00:00:00 2001 From: Ben Gardiner Date: Tue, 24 May 2011 14:50:16 -0400 Subject: ASoC: davinci-pcm: expand the .formats Based on the data_type test in ping_pong_dma_setup, davinci-pcm is capable of handling data of width up to and including 32bits. " if ((data_type == 0) || (data_type > 4)) { printk(KERN_ERR "%s: data_type=%i\n", __func__, data_type); return -EINVAL; } " Update the .format member of the snd_pcm_hardware instances it registers to reflect this capability. Signed-off-by: Ben Gardiner Reviewed-by: Steven Faludi Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index 0d04e0c..1e47267 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -46,11 +46,27 @@ static void print_buf_info(int slot, char *name) } #endif +#define DAVINCI_PCM_FMTBITS (\ + SNDRV_PCM_FMTBIT_S8 |\ + SNDRV_PCM_FMTBIT_U8 |\ + SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S16_BE |\ + SNDRV_PCM_FMTBIT_U16_LE |\ + SNDRV_PCM_FMTBIT_U16_BE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_BE |\ + SNDRV_PCM_FMTBIT_U24_LE |\ + SNDRV_PCM_FMTBIT_U24_BE |\ + SNDRV_PCM_FMTBIT_S32_LE |\ + SNDRV_PCM_FMTBIT_S32_BE |\ + SNDRV_PCM_FMTBIT_U32_LE |\ + SNDRV_PCM_FMTBIT_U32_BE) + static struct snd_pcm_hardware pcm_hardware_playback = { .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), - .formats = (SNDRV_PCM_FMTBIT_S16_LE), + .formats = DAVINCI_PCM_FMTBITS, .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | @@ -72,7 +88,7 @@ static struct snd_pcm_hardware pcm_hardware_capture = { .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), - .formats = (SNDRV_PCM_FMTBIT_S16_LE), + .formats = DAVINCI_PCM_FMTBITS, .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | -- cgit v0.10.2 From acb8e2666eba7417e2fab783f86dbe067c3e815f Mon Sep 17 00:00:00 2001 From: Ben Gardiner Date: Tue, 24 May 2011 14:50:17 -0400 Subject: ASoC: davinci-pcm: increase the maximum channels Based on the registration of davinci-mcasp.1 in the davinci-evm platform setup for da830 and dm6467, davinci-pcm can handle more than the currently reported maximum channels of 2. Increase the maximum channels to 384 to match the maximum reported by davinci-mcasp.1. Signed-off-by: Ben Gardiner Reviewed-by: Steven Faludi Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index 1e47267..9b5a9bf 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -75,7 +75,7 @@ static struct snd_pcm_hardware pcm_hardware_playback = { .rate_min = 8000, .rate_max = 96000, .channels_min = 2, - .channels_max = 2, + .channels_max = 384, .buffer_bytes_max = 128 * 1024, .period_bytes_min = 32, .period_bytes_max = 8 * 1024, @@ -97,7 +97,7 @@ static struct snd_pcm_hardware pcm_hardware_capture = { .rate_min = 8000, .rate_max = 96000, .channels_min = 2, - .channels_max = 2, + .channels_max = 384, .buffer_bytes_max = 128 * 1024, .period_bytes_min = 32, .period_bytes_max = 8 * 1024, -- cgit v0.10.2 From ef39eb6f212996ede8da47ef45e6dffff1121ec7 Mon Sep 17 00:00:00 2001 From: Ben Gardiner Date: Tue, 24 May 2011 14:50:18 -0400 Subject: ASoC: davinci-pcm: fix audible glitch on 2nd ping-pong playback The release of the dma channels was being performed in prepare and there was a edma_resume call for the asp-channel only being executed on START, RESUME and PAUSE_RELEASE. The mcasp on da850evm with ping-pong buffers enabled was exhibiting an audible glitch on every playback after the first. It was determined through trial and error that the following two changes fix this problem: 1) Move the edma_start calls from prepare to trigger and 2) reverse the order of starting the asp and ram channels. Signed-off-by: Ben Gardiner Reviewed-by: Steven Faludi Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index 9b5a9bf..5d9269a 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -544,6 +544,13 @@ static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd) switch (cmd) { case SNDRV_PCM_TRIGGER_START: + edma_start(prtd->asp_channel); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + prtd->ram_channel >= 0) { + /* copy 1st iram buffer */ + edma_start(prtd->ram_channel); + } + break; case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: edma_resume(prtd->asp_channel); @@ -582,11 +589,6 @@ static int davinci_pcm_prepare(struct snd_pcm_substream *substream) print_buf_info(prtd->asp_link[0], "asp_link[0]"); print_buf_info(prtd->asp_link[1], "asp_link[1]"); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - /* copy 1st iram buffer */ - edma_start(prtd->ram_channel); - } - edma_start(prtd->asp_channel); return 0; } prtd->period = 0; @@ -596,7 +598,6 @@ static int davinci_pcm_prepare(struct snd_pcm_substream *substream) edma_read_slot(prtd->asp_link[0], &prtd->asp_params); edma_write_slot(prtd->asp_channel, &prtd->asp_params); davinci_pcm_enqueue_dma(substream); - edma_start(prtd->asp_channel); return 0; } -- cgit v0.10.2 From 10ab3bfda41ea21419f6a8d1e5a645521fea4b32 Mon Sep 17 00:00:00 2001 From: Ben Gardiner Date: Tue, 24 May 2011 14:50:19 -0400 Subject: ASoC: davinci-pcm: extract period elapsed functions Extract functions that modify the prtd->period member in preparation for conversion to BATCH mode playback. Signed-off-by: Ben Gardiner Reviewed-by: Steven Faludi Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index 5d9269a..fedca81 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -155,6 +155,22 @@ struct davinci_runtime_data { struct edmacc_param ram_params; }; +static void davinci_pcm_period_elapsed(struct snd_pcm_substream *substream) +{ + struct davinci_runtime_data *prtd = substream->runtime->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + + prtd->period++; + if (unlikely(prtd->period >= runtime->periods)) + prtd->period = 0; +} + +static void davinci_pcm_period_reset(struct snd_pcm_substream *substream) +{ + struct davinci_runtime_data *prtd = substream->runtime->private_data; + + prtd->period = 0; +} /* * Not used with ping/pong */ @@ -216,9 +232,7 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream) edma_set_transfer_params(link, acnt, fifo_level, count, fifo_level, ABSYNC); - prtd->period++; - if (unlikely(prtd->period >= runtime->periods)) - prtd->period = 0; + davinci_pcm_period_elapsed(substream); } static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data) @@ -591,7 +605,7 @@ static int davinci_pcm_prepare(struct snd_pcm_substream *substream) return 0; } - prtd->period = 0; + davinci_pcm_period_reset(substream); davinci_pcm_enqueue_dma(substream); /* Copy self-linked parameter RAM entry into master channel */ -- cgit v0.10.2 From 52e2c5d38ea6f13a19c29da7ba5183e6fac55400 Mon Sep 17 00:00:00 2001 From: Ben Gardiner Date: Tue, 24 May 2011 14:50:20 -0400 Subject: ASoC: davinci-pcm: convert to BATCH mode The davinci-pcm driver's snd_pcm_ops pointer function currently calls into the edma controller driver to read the current positions of the edma channels to determine pos to return to the ALSA framework. In particular, davinci_pcm_pointer() calls edma_get_position() and the latter has a comment indicating that "Its channel should not be active when this is called" whereas the channel is surely active when snd_pcm_ops.pointer is called. The operation of davinci-pcm in capture and playback appears to follow close the other pcm drivers who export SNDRV_PCM_INFO_BATCH except that davinci-pcm does not report it's positions from pointer() using the last transferred chunk. Instead it peeks directly into the edma controller to determine the current position as discussed above. Convert the davinci-pcm driver to BATCH mode: count the periods elapsed in the prtd->period member and use its value to report the 'pos' to the alsa framework in the davinci_pcm_pointer function. There is a phase offset of 2 periods between the position used by dma setup and the position reported in the pointer function. Either +2 in the dma setup or -2 in the pointer function (with wrapping, both) accounts for this offset -- I opted for the latter since it makes the first-time setup clearer. Signed-off-by: Ben Gardiner Reviewed-by: Steven Faludi Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index fedca81..fa8fc61 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -65,7 +65,8 @@ static void print_buf_info(int slot, char *name) static struct snd_pcm_hardware pcm_hardware_playback = { .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME| + SNDRV_PCM_INFO_BATCH), .formats = DAVINCI_PCM_FMTBITS, .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | @@ -87,7 +88,8 @@ static struct snd_pcm_hardware pcm_hardware_playback = { static struct snd_pcm_hardware pcm_hardware_capture = { .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE), + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_BATCH), .formats = DAVINCI_PCM_FMTBITS, .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | @@ -231,8 +233,6 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream) else edma_set_transfer_params(link, acnt, fifo_level, count, fifo_level, ABSYNC); - - davinci_pcm_period_elapsed(substream); } static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data) @@ -247,12 +247,13 @@ static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data) return; if (snd_pcm_running(substream)) { + spin_lock(&prtd->lock); if (prtd->ram_channel < 0) { /* No ping/pong must fix up link dma data*/ - spin_lock(&prtd->lock); davinci_pcm_enqueue_dma(substream); - spin_unlock(&prtd->lock); } + davinci_pcm_period_elapsed(substream); + spin_unlock(&prtd->lock); snd_pcm_period_elapsed(substream); } } @@ -588,6 +589,7 @@ static int davinci_pcm_prepare(struct snd_pcm_substream *substream) { struct davinci_runtime_data *prtd = substream->runtime->private_data; + davinci_pcm_period_reset(substream); if (prtd->ram_channel >= 0) { int ret = ping_pong_dma_setup(substream); if (ret < 0) @@ -603,15 +605,19 @@ static int davinci_pcm_prepare(struct snd_pcm_substream *substream) print_buf_info(prtd->asp_link[0], "asp_link[0]"); print_buf_info(prtd->asp_link[1], "asp_link[1]"); + davinci_pcm_period_elapsed(substream); + davinci_pcm_period_elapsed(substream); + return 0; } - davinci_pcm_period_reset(substream); davinci_pcm_enqueue_dma(substream); + davinci_pcm_period_elapsed(substream); /* Copy self-linked parameter RAM entry into master channel */ edma_read_slot(prtd->asp_link[0], &prtd->asp_params); edma_write_slot(prtd->asp_channel, &prtd->asp_params); davinci_pcm_enqueue_dma(substream); + davinci_pcm_period_elapsed(substream); return 0; } @@ -623,51 +629,16 @@ davinci_pcm_pointer(struct snd_pcm_substream *substream) struct davinci_runtime_data *prtd = runtime->private_data; unsigned int offset; int asp_count; - dma_addr_t asp_src, asp_dst; + unsigned int period_size = snd_pcm_lib_period_bytes(substream); spin_lock(&prtd->lock); - if (prtd->ram_channel >= 0) { - int ram_count; - int mod_ram; - dma_addr_t ram_src, ram_dst; - unsigned int period_size = snd_pcm_lib_period_bytes(substream); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - /* reading ram before asp should be safe - * as long as the asp transfers less than a ping size - * of bytes between the 2 reads - */ - edma_get_position(prtd->ram_channel, - &ram_src, &ram_dst); - edma_get_position(prtd->asp_channel, - &asp_src, &asp_dst); - asp_count = asp_src - prtd->asp_params.src; - ram_count = ram_src - prtd->ram_params.src; - mod_ram = ram_count % period_size; - mod_ram -= asp_count; - if (mod_ram < 0) - mod_ram += period_size; - else if (mod_ram == 0) { - if (snd_pcm_running(substream)) - mod_ram += period_size; - } - ram_count -= mod_ram; - if (ram_count < 0) - ram_count += period_size * runtime->periods; - } else { - edma_get_position(prtd->ram_channel, - &ram_src, &ram_dst); - ram_count = ram_dst - prtd->ram_params.dst; - } - asp_count = ram_count; - } else { - edma_get_position(prtd->asp_channel, &asp_src, &asp_dst); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - asp_count = asp_src - runtime->dma_addr; - else - asp_count = asp_dst - runtime->dma_addr; - } + asp_count = prtd->period - 2; spin_unlock(&prtd->lock); + if (asp_count < 0) + asp_count += runtime->periods; + asp_count *= period_size; + offset = bytes_to_frames(runtime, asp_count); if (offset >= runtime->buffer_size) offset = 0; -- cgit v0.10.2 From bb5b5fd4d49e5902840874cf60b51714bd51a30a Mon Sep 17 00:00:00 2001 From: Ben Gardiner Date: Wed, 25 May 2011 09:27:22 -0400 Subject: ASoC: davinci-pcm: comments for the conversion to BATCH mode In the previous commit 'ASoC: davinci-pcm: convert to BATCH mode', the phase offset of 2 was mentioned in the commit message but not well commented in the source. Add descriptive comments of the phase offset with and without ping-pong buffers enabled. Signed-off-by: Ben Gardiner Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index fa8fc61..c9e0320 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -605,6 +605,18 @@ static int davinci_pcm_prepare(struct snd_pcm_substream *substream) print_buf_info(prtd->asp_link[0], "asp_link[0]"); print_buf_info(prtd->asp_link[1], "asp_link[1]"); + /* + * There is a phase offset of 2 periods between the position + * used by dma setup and the position reported in the pointer + * function. + * + * The phase offset, when not using ping-pong buffers, is due to + * the two consecutive calls to davinci_pcm_enqueue_dma() below. + * + * Whereas here, with ping-pong buffers, the phase is due to + * there being an entire buffer transfer complete before the + * first dma completion event triggers davinci_pcm_dma_irq(). + */ davinci_pcm_period_elapsed(substream); davinci_pcm_period_elapsed(substream); @@ -631,6 +643,13 @@ davinci_pcm_pointer(struct snd_pcm_substream *substream) int asp_count; unsigned int period_size = snd_pcm_lib_period_bytes(substream); + /* + * There is a phase offset of 2 periods between the position used by dma + * setup and the position reported in the pointer function. Either +2 in + * the dma setup or -2 here in the pointer function (with wrapping, + * both) accounts for this offset -- choose the latter since it makes + * the first-time setup clearer. + */ spin_lock(&prtd->lock); asp_count = prtd->period - 2; spin_unlock(&prtd->lock); -- cgit v0.10.2 From b6f7d7c8bf40800ac68e16302bb7627c59ea9168 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 25 May 2011 09:53:12 +0200 Subject: ASoC: Fix comment in cs4270 codec driver The comment does not reflect reality anymore since the multi-component monster patch landed. Things are matched by names now, and not by exporting and referencing a struct. Fix it to avoid confusion. Signed-off-by: Daniel Mack Acked-by: Liam Girdwood Acked-by: Timur Tabi Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 0206a17..6cc8678 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -636,10 +636,7 @@ static int cs4270_soc_resume(struct snd_soc_codec *codec) #endif /* CONFIG_PM */ /* - * ASoC codec device structure - * - * Assign this variable to the codec_dev field of the machine driver's - * snd_soc_device structure. + * ASoC codec driver structure */ static const struct snd_soc_codec_driver soc_codec_device_cs4270 = { .probe = cs4270_probe, -- cgit v0.10.2 From 82e14e8bdd88b69018fe757192b01dd98582905e Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 25 May 2011 14:06:41 -0600 Subject: ASoC: core: Don't schedule deferred_resume_work twice For cards that have two or more DAIs, snd_soc_resume's loop over all DAIs ends up calling schedule_work(deferred_resume_work) once per DAI. Since this is the same work item each time, the 2nd and subsequent calls return 0 (work item already queued), and trigger the dev_err message below stating that a work item may have been lost. Solve this by adjusting the loop to simply calculate whether to run the resume work immediately or defer it, and then call schedule work (or not) one time based on that. Note: This has not been tested in mainline, but only in chromeos-2.6.38; mainline doesn't support suspend/resume on Tegra, nor does the mainline Tegra ASoC driver contain multiple DAIs. It has been compile-checked in mainline. Signed-off-by: Stephen Warren Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index a477e21..c261eeb 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1256,7 +1256,7 @@ static void soc_resume_deferred(struct work_struct *work) int snd_soc_resume(struct device *dev) { struct snd_soc_card *card = dev_get_drvdata(dev); - int i; + int i, ac97_control = 0; /* AC97 devices might have other drivers hanging off them so * need to resume immediately. Other drivers don't have that @@ -1265,14 +1265,15 @@ int snd_soc_resume(struct device *dev) */ for (i = 0; i < card->num_rtd; i++) { struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; - if (cpu_dai->driver->ac97_control) { - dev_dbg(dev, "Resuming AC97 immediately\n"); - soc_resume_deferred(&card->deferred_resume_work); - } else { - dev_dbg(dev, "Scheduling resume work\n"); - if (!schedule_work(&card->deferred_resume_work)) - dev_err(dev, "resume work item may be lost\n"); - } + ac97_control |= cpu_dai->driver->ac97_control; + } + if (ac97_control) { + dev_dbg(dev, "Resuming AC97 immediately\n"); + soc_resume_deferred(&card->deferred_resume_work); + } else { + dev_dbg(dev, "Scheduling resume work\n"); + if (!schedule_work(&card->deferred_resume_work)) + dev_err(dev, "resume work item may be lost\n"); } return 0; -- cgit v0.10.2 From 7309d2e28d150748c23db11ac4837898aa04fc81 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Thu, 26 May 2011 13:44:10 +0200 Subject: ASoC: trivial: typo in debug comment Signed-off-by: Nicolas Ferre Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/atmel/atmel-pcm.c b/sound/soc/atmel/atmel-pcm.c index d0e7532..51dde4e 100644 --- a/sound/soc/atmel/atmel-pcm.c +++ b/sound/soc/atmel/atmel-pcm.c @@ -382,7 +382,7 @@ static int atmel_pcm_new(struct snd_card *card, } if (dai->driver->capture.channels_min) { - pr_debug("at32-pcm:" + pr_debug("atmel-pcm:" "Allocating PCM capture DMA buffer\n"); ret = atmel_pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE); -- cgit v0.10.2 From 97b4fc3c445bf10b6fe0d8543137a6ad30a0fdab Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Thu, 26 May 2011 13:44:11 +0200 Subject: ASoC: trivial: typo in atmel_pcm_dma_params strucutre comment Signed-off-by: Nicolas Ferre Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/atmel/atmel-pcm.h b/sound/soc/atmel/atmel-pcm.h index 2597329..5e0a95e 100644 --- a/sound/soc/atmel/atmel-pcm.h +++ b/sound/soc/atmel/atmel-pcm.h @@ -60,7 +60,7 @@ struct atmel_ssc_mask { * This structure, shared between the PCM driver and the interface, * contains all information required by the PCM driver to perform the * PDC DMA operation. All fields except dma_intr_handler() are initialized - * by the interface. The dms_intr_handler() pointer is set by the PCM + * by the interface. The dma_intr_handler() pointer is set by the PCM * driver and called by the interface SSC interrupt handler if it is * non-NULL. */ -- cgit v0.10.2 From 2cdcd951c456fecbeb11fc42aa7d90f172dc58ef Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Thu, 26 May 2011 13:44:12 +0200 Subject: ASoC: atmel_ssc_dai: fix ssc error path We do not have to free a resource that is not allocated yet. Signed-off-by: Nicolas Ferre Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 7fbfa05..a7a7bbc 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -838,10 +838,8 @@ int atmel_ssc_set_audio(int ssc_id) } ssc_pdev = platform_device_alloc("atmel-ssc-dai", ssc_id); - if (!ssc_pdev) { - ssc_free(ssc); + if (!ssc_pdev) return -ENOMEM; - } /* If we can grab the SSC briefly to parent the DAI device off it */ ssc = ssc_request(ssc_id); -- cgit v0.10.2 From f06f136fe0217174657e325279672d4b73ed4d87 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 11 May 2011 00:56:13 +0200 Subject: ASoC: Convert 7x9 write to use cpu_to_be16() Run the data through cpu_to_be16() so it's at least clear what we're up to. Signed-off-by: Mark Brown Acked-by: Liam Girdwood diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index 6d6395f..dab109f 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -133,12 +133,11 @@ static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec, static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { - u8 data[2]; + u16 data; - data[0] = (reg << 1) | ((value >> 8) & 0x0001); - data[1] = value & 0x00ff; + data = cpu_to_be16((reg << 9) | (value & 0x1ff)); - return do_hw_write(codec, reg, value, data, 2); + return do_hw_write(codec, reg, value, &data, 2); } #if defined(CONFIG_SPI_MASTER) -- cgit v0.10.2 From 94228bcf8c85cfb824e15485285ceb4d5cf080fe Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 11 May 2011 11:18:00 +0200 Subject: ASoC: Use cpu_to_be16() in 8x16 write Signed-off-by: Mark Brown Acked-by: Liam Girdwood diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index dab109f..9c688b8 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -192,10 +192,10 @@ static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { u8 data[3]; + u16 val = cpu_to_be16(value); data[0] = reg; - data[1] = (value >> 8) & 0xff; - data[2] = value & 0xff; + memcpy(&data[1], &val, sizeof(val)); return do_hw_write(codec, reg, value, data, 3); } -- cgit v0.10.2 From 2ac8b6f41a6886dacc88c15c8742e7cd2e40ca7e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 11 May 2011 15:15:56 +0200 Subject: ASoC: Use explicit endianness conversion in snd_soc_16_8_write() Signed-off-by: Mark Brown Acked-by: Liam Girdwood diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index 9c688b8..d1d4059 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -314,9 +314,9 @@ static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { u8 data[3]; + u16 rval = cpu_to_be16(reg); - data[0] = (reg >> 8) & 0xff; - data[1] = reg & 0xff; + memcpy(data, &rval, sizeof(rval)); data[2] = value; reg &= 0xff; -- cgit v0.10.2 From a5fe6be42e5cadac4c2aef2b3714cfa8fadb6092 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Thu, 26 May 2011 12:46:50 -0600 Subject: ASoC: Tegra: Enable Kaen HP_MUTE at boot We want the default state of the HP_MUTE signal to be asserted, so that the headphones are muted before the first audio playback. Without this, the headphones are left unmuted until shortly after the first audio playback completes. Signed-off-by: Stephen Warren Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c index 0d6738a..a42e9ac 100644 --- a/sound/soc/tegra/tegra_wm8903.c +++ b/sound/soc/tegra/tegra_wm8903.c @@ -267,7 +267,7 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd) } machine->gpio_requested |= GPIO_HP_MUTE; - gpio_direction_output(pdata->gpio_hp_mute, 0); + gpio_direction_output(pdata->gpio_hp_mute, 1); } if (gpio_is_valid(pdata->gpio_int_mic_en)) { -- cgit v0.10.2 From 74ab24af4fe165de5af01d0507250dd099f096b0 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 27 May 2011 23:30:53 +0800 Subject: ASoC: Remove redundant freq assignment for max98095->sysclk/max98088->sysclk Current implementation set max98095->sysclk/max98088->sysclk to freq twice. Set it once is enough, this patch removes the first assignment in case we may set invalid clock frequency to max98095->sysclk/max98088->sysclk. Signed-off-by: Axel Lin Acked-by: Peter Hsiang Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index bb58bdb..93255ff 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -1401,8 +1401,6 @@ static int max98088_dai_set_sysclk(struct snd_soc_dai *dai, if (freq == max98088->sysclk) return 0; - max98088->sysclk = freq; /* remember current sysclk */ - /* Setup clocks for slave mode, and using the PLL * PSCLK = 0x01 (when master clk is 10MHz to 20MHz) * 0x02 (when master clk is 20MHz to 30MHz).. diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index a6cc94e..fe19677 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -1517,8 +1517,6 @@ static int max98095_dai_set_sysclk(struct snd_soc_dai *dai, if (freq == max98095->sysclk) return 0; - max98095->sysclk = freq; /* remember current sysclk */ - /* Setup clocks for slave mode, and using the PLL * PSCLK = 0x01 (when master clk is 10MHz to 20MHz) * 0x02 (when master clk is 20MHz to 40MHz).. -- cgit v0.10.2 From 37aa716a57f7c1fe5deaedff242e04f5a0f26b54 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 1 Jun 2011 10:10:50 +0100 Subject: ASoC: Staticize ak4641_dai Signed-off-by: Mark Brown Acked-by: Liam Girdwood diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c index ed96f247c..7a64e58 100644 --- a/sound/soc/codecs/ak4641.c +++ b/sound/soc/codecs/ak4641.c @@ -457,7 +457,7 @@ static struct snd_soc_dai_ops ak4641_pcm_dai_ops = { .set_sysclk = ak4641_set_dai_sysclk, }; -struct snd_soc_dai_driver ak4641_dai[] = { +static struct snd_soc_dai_driver ak4641_dai[] = { { .name = "ak4641-hifi", .id = 1, -- cgit v0.10.2 From a1e9adc00e722b8ec7d9b3d68e6f9564b9455d2f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 1 Jun 2011 14:45:58 +0100 Subject: ASoC: Support edge triggered IRQs for WM8915 Really this should be something the IRQ core can cope with for us but since it doesn't currently do so (at least for threaded interrupts like this) do so in the driver. This allows us to run with interrupt controllers that only support edge triggered interrupts. Signed-off-by: Mark Brown Acked-by: Liam Girdwood diff --git a/sound/soc/codecs/wm8915.c b/sound/soc/codecs/wm8915.c index a0b1a72..bb1ff2c 100644 --- a/sound/soc/codecs/wm8915.c +++ b/sound/soc/codecs/wm8915.c @@ -2382,6 +2382,20 @@ static irqreturn_t wm8915_irq(int irq, void *data) } } +static irqreturn_t wm8915_edge_irq(int irq, void *data) +{ + irqreturn_t ret = IRQ_NONE; + irqreturn_t val; + + do { + val = wm8915_irq(irq, data); + if (val != IRQ_NONE) + ret = val; + } while (val != IRQ_NONE); + + return ret; +} + static void wm8915_retune_mobile_pdata(struct snd_soc_codec *codec) { struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec); @@ -2708,8 +2722,14 @@ static int wm8915_probe(struct snd_soc_codec *codec) irq_flags |= IRQF_ONESHOT; - ret = request_threaded_irq(i2c->irq, NULL, wm8915_irq, - irq_flags, "wm8915", codec); + if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) + ret = request_threaded_irq(i2c->irq, NULL, + wm8915_edge_irq, + irq_flags, "wm8915", codec); + else + ret = request_threaded_irq(i2c->irq, NULL, wm8915_irq, + irq_flags, "wm8915", codec); + if (ret == 0) { /* Unmask the interrupt */ snd_soc_update_bits(codec, WM8915_INTERRUPT_CONTROL, -- cgit v0.10.2 From a2dc56c8a0009ba3879a26dfe416fc453fa2e32f Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 1 Jun 2011 19:10:05 +0200 Subject: ASoC: add missing clk_put to nuc900-ac97 This goto is after the call to clk_get, so it should go to the label that includes a call to clk_put. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @r exists@ expression e1,e2; statement S; @@ e1 = clk_get@p1(...); ... when != e1 = e2 when != clk_put(e1) when any if (...) { ... when != clk_put(e1) when != if (...) { ... clk_put(e1) ... } * return@p3 ...; } else S // Signed-off-by: Julia Lawall Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/nuc900/nuc900-ac97.c b/sound/soc/nuc900/nuc900-ac97.c index dac6732..9c0edad 100644 --- a/sound/soc/nuc900/nuc900-ac97.c +++ b/sound/soc/nuc900/nuc900-ac97.c @@ -356,7 +356,7 @@ static int __devinit nuc900_ac97_drvprobe(struct platform_device *pdev) nuc900_audio->irq_num = platform_get_irq(pdev, 0); if (!nuc900_audio->irq_num) { ret = -EBUSY; - goto out2; + goto out3; } nuc900_ac97_data = nuc900_audio; -- cgit v0.10.2 From cf4a39105ab7d73583f142c492f2880247f520f9 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 1 Jun 2011 19:17:02 +0100 Subject: ASoC: Remove internally generated WM8915 supplies DCVDD and MICVDD are intended to be (and almost always are) generated by on-board LDOs which are transparently controlled by the driver so we shouldn't really be requesting them from the regulator API. If the driver is updated to support external supply of these then we will need to change the way we handle this. Signed-off-by: Mark Brown Acked-by: Liam Girdwood diff --git a/sound/soc/codecs/wm8915.c b/sound/soc/codecs/wm8915.c index bb1ff2c..5ff6a77 100644 --- a/sound/soc/codecs/wm8915.c +++ b/sound/soc/codecs/wm8915.c @@ -41,14 +41,12 @@ #define HPOUT2L 4 #define HPOUT2R 8 -#define WM8915_NUM_SUPPLIES 6 +#define WM8915_NUM_SUPPLIES 4 static const char *wm8915_supply_names[WM8915_NUM_SUPPLIES] = { - "DCVDD", "DBVDD", "AVDD1", "AVDD2", "CPVDD", - "MICVDD", }; struct wm8915_priv { @@ -113,8 +111,6 @@ WM8915_REGULATOR_EVENT(0) WM8915_REGULATOR_EVENT(1) WM8915_REGULATOR_EVENT(2) WM8915_REGULATOR_EVENT(3) -WM8915_REGULATOR_EVENT(4) -WM8915_REGULATOR_EVENT(5) static const u16 wm8915_reg[WM8915_MAX_REGISTER] = { [WM8915_SOFTWARE_RESET] = 0x8915, @@ -2495,8 +2491,6 @@ static int wm8915_probe(struct snd_soc_codec *codec) wm8915->disable_nb[1].notifier_call = wm8915_regulator_event_1; wm8915->disable_nb[2].notifier_call = wm8915_regulator_event_2; wm8915->disable_nb[3].notifier_call = wm8915_regulator_event_3; - wm8915->disable_nb[4].notifier_call = wm8915_regulator_event_4; - wm8915->disable_nb[5].notifier_call = wm8915_regulator_event_5; /* This should really be moved into the regulator core */ for (i = 0; i < ARRAY_SIZE(wm8915->supplies); i++) { -- cgit v0.10.2 From 1e025a3692014e7a29a0b0b01de5cdc2b6ade3cf Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 1 Jun 2011 19:32:22 +0100 Subject: ASoC: Update speyside audio driver for hardware revision 2 Revision 2 of the Speyside platform supplies a 32kHz clock on MCLK2 rather than MCLK1. Signed-off-by: Mark Brown Acked-by: Liam Girdwood diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c index 360a333..93078b1 100644 --- a/sound/soc/samsung/speyside.c +++ b/sound/soc/samsung/speyside.c @@ -27,12 +27,12 @@ static int speyside_set_bias_level(struct snd_soc_card *card, switch (level) { case SND_SOC_BIAS_STANDBY: - ret = snd_soc_dai_set_sysclk(codec_dai, WM8915_SYSCLK_MCLK1, + ret = snd_soc_dai_set_sysclk(codec_dai, WM8915_SYSCLK_MCLK2, 32768, SND_SOC_CLOCK_IN); if (ret < 0) return ret; - ret = snd_soc_dai_set_pll(codec_dai, WM8915_FLL_MCLK1, + ret = snd_soc_dai_set_pll(codec_dai, WM8915_FLL_MCLK2, 0, 0, 0); if (ret < 0) { pr_err("Failed to stop FLL\n"); @@ -66,7 +66,7 @@ static int speyside_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; - ret = snd_soc_dai_set_pll(codec_dai, 0, WM8915_FLL_MCLK1, + ret = snd_soc_dai_set_pll(codec_dai, 0, WM8915_FLL_MCLK2, 32768, 256 * 48000); if (ret < 0) return ret; @@ -127,7 +127,7 @@ static int speyside_wm8915_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_codec *codec = rtd->codec; int ret; - ret = snd_soc_dai_set_sysclk(dai, WM8915_SYSCLK_MCLK1, 32768, 0); + ret = snd_soc_dai_set_sysclk(dai, WM8915_SYSCLK_MCLK2, 32768, 0); if (ret < 0) return ret; -- cgit v0.10.2 From e6a9be0bb018466896632969ba4b496d1a7caea9 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 1 Jun 2011 20:16:40 +0100 Subject: ASoC: Use a lower detection rate when monitoring headphones on WM8915 We only need to increase the detection rate to maximum if we're monitoring for button presses as the response times needed for user interaction there are much lower. Signed-off-by: Mark Brown Acked-by: Liam Girdwood diff --git a/sound/soc/codecs/wm8915.c b/sound/soc/codecs/wm8915.c index 5ff6a77..5a59ef7 100644 --- a/sound/soc/codecs/wm8915.c +++ b/sound/soc/codecs/wm8915.c @@ -2288,6 +2288,12 @@ static void wm8915_micd(struct snd_soc_codec *codec) SND_JACK_HEADSET | SND_JACK_BTN_0); wm8915->jack_mic = true; wm8915->detecting = false; + + /* Increase poll rate to give better responsiveness + * for buttons */ + snd_soc_update_bits(codec, WM8915_MIC_DETECT_1, + WM8915_MICD_RATE_MASK, + 5 << WM8915_MICD_RATE_SHIFT); } /* If we detected a lower impedence during initial startup @@ -2328,15 +2334,17 @@ static void wm8915_micd(struct snd_soc_codec *codec) SND_JACK_HEADPHONE, SND_JACK_HEADSET | SND_JACK_BTN_0); + + /* Increase the detection rate a bit for + * responsiveness. + */ + snd_soc_update_bits(codec, WM8915_MIC_DETECT_1, + WM8915_MICD_RATE_MASK, + 7 << WM8915_MICD_RATE_SHIFT); + wm8915->detecting = false; } } - - /* Increase poll rate to give better responsiveness for buttons */ - if (!wm8915->detecting) - snd_soc_update_bits(codec, WM8915_MIC_DETECT_1, - WM8915_MICD_RATE_MASK, - 5 << WM8915_MICD_RATE_SHIFT); } static irqreturn_t wm8915_irq(int irq, void *data) -- cgit v0.10.2 From bca2e41d31cf891db535f99a2a2dfe4911b96c15 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Thu, 2 Jun 2011 15:44:45 -0500 Subject: ASoC: OMAP: Add CPU DAI driver for HDMI Addition of the HDMI CPU DAI driver for OMAP4. This driver is in charge of configuring DMA settings for HDMI. Also, it finds the HDMI video device and determines if audio playback can proceed. Signed-off-by: Ricardo Neri Acked-by: Mark Brown Signed-off-by: Liam Girdwood diff --git a/sound/soc/omap/omap-hdmi.c b/sound/soc/omap/omap-hdmi.c new file mode 100644 index 0000000..36c6eae --- /dev/null +++ b/sound/soc/omap/omap-hdmi.c @@ -0,0 +1,158 @@ +/* + * omap-hdmi.c + * + * OMAP ALSA SoC DAI driver for HDMI audio on OMAP4 processors. + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ + * Authors: Jorge Candelaria + * Ricardo Neri + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "omap-pcm.h" +#include "omap-hdmi.h" + +#define DRV_NAME "hdmi-audio-dai" + +static struct omap_pcm_dma_data omap_hdmi_dai_dma_params = { + .name = "HDMI playback", + .sync_mode = OMAP_DMA_SYNC_PACKET, +}; + +static int omap_hdmi_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int err; + /* + * Make sure that the period bytes are multiple of the DMA packet size. + * Largest packet size we use is 32 32-bit words = 128 bytes + */ + err = snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128); + if (err < 0) + return err; + + return 0; +} + +static int omap_hdmi_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int err = 0; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + omap_hdmi_dai_dma_params.packet_size = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + omap_hdmi_dai_dma_params.packet_size = 32; + break; + default: + err = -EINVAL; + } + + omap_hdmi_dai_dma_params.data_type = OMAP_DMA_DATA_TYPE_S32; + + snd_soc_dai_set_dma_data(dai, substream, + &omap_hdmi_dai_dma_params); + + return err; +} + +static struct snd_soc_dai_ops omap_hdmi_dai_ops = { + .startup = omap_hdmi_dai_startup, + .hw_params = omap_hdmi_dai_hw_params, +}; + +static struct snd_soc_dai_driver omap_hdmi_dai = { + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = OMAP_HDMI_RATES, + .formats = OMAP_HDMI_FORMATS, + }, + .ops = &omap_hdmi_dai_ops, +}; + +static __devinit int omap_hdmi_probe(struct platform_device *pdev) +{ + int ret; + struct resource *hdmi_rsrc; + + hdmi_rsrc = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!hdmi_rsrc) { + dev_err(&pdev->dev, "Cannot obtain IORESOURCE_MEM HDMI\n"); + return -EINVAL; + } + + omap_hdmi_dai_dma_params.port_addr = hdmi_rsrc->start + + OMAP_HDMI_AUDIO_DMA_PORT; + + hdmi_rsrc = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!hdmi_rsrc) { + dev_err(&pdev->dev, "Cannot obtain IORESOURCE_DMA HDMI\n"); + return -EINVAL; + } + + omap_hdmi_dai_dma_params.dma_req = hdmi_rsrc->start; + + ret = snd_soc_register_dai(&pdev->dev, &omap_hdmi_dai); + return ret; +} + +static int __devexit omap_hdmi_remove(struct platform_device *pdev) +{ + snd_soc_unregister_dai(&pdev->dev); + return 0; +} + +static struct platform_driver hdmi_dai_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = omap_hdmi_probe, + .remove = __devexit_p(omap_hdmi_remove), +}; + +static int __init hdmi_dai_init(void) +{ + return platform_driver_register(&hdmi_dai_driver); +} +module_init(hdmi_dai_init); + +static void __exit hdmi_dai_exit(void) +{ + platform_driver_unregister(&hdmi_dai_driver); +} +module_exit(hdmi_dai_exit); + +MODULE_AUTHOR("Jorge Candelaria "); +MODULE_AUTHOR("Ricardo Neri "); +MODULE_DESCRIPTION("OMAP HDMI SoC Interface"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/omap/omap-hdmi.h b/sound/soc/omap/omap-hdmi.h new file mode 100644 index 0000000..34c298d --- /dev/null +++ b/sound/soc/omap/omap-hdmi.h @@ -0,0 +1,36 @@ +/* + * omap-hdmi.h + * + * Definitions for OMAP ALSA SoC DAI driver for HDMI audio on OMAP4 processors. + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ + * Authors: Jorge Candelaria + * Ricardo Neri + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __OMAP_HDMI_H__ +#define __OMAP_HDMI_H__ + +#define OMAP_HDMI_AUDIO_DMA_PORT 0x8c + +#define OMAP_HDMI_RATES (SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) + +#define OMAP_HDMI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +#endif -- cgit v0.10.2 From 55b95e0e60588ebf0d61d828149092e8e2ec7aec Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Thu, 2 Jun 2011 15:44:46 -0500 Subject: ASoC: OMAP4: Add HDMI Audio machine driver for OMAP4 boards Add machine driver for HDMI audio on OMAP4 boards. This driver is in charge of putting together the HDMI audio codec and the CPU DAI and register the HDMI sound card with ALSA. Signed-off-by: Ricardo Neri Acked-by: Mark Brown Signed-off-by: Liam Girdwood diff --git a/sound/soc/omap/omap4-hdmi-card.c b/sound/soc/omap/omap4-hdmi-card.c new file mode 100644 index 0000000..9f32615 --- /dev/null +++ b/sound/soc/omap/omap4-hdmi-card.c @@ -0,0 +1,129 @@ +/* + * omap4-hdmi-card.c + * + * OMAP ALSA SoC machine driver for TI OMAP4 HDMI + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Ricardo Neri + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include