From 6f3ab6c1c022e7a4877d38940cd45ae7804a15e2 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 23 Jan 2014 18:39:23 -0800 Subject: ASoC: rsnd: remove pin sync option Renesas Chip is supporting multi pin sound, but the HW setting is very difficult and confusable. But driver is supporting it halfway. Remove SYNC option at this point. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/rcar_snd.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 6add6ccc811e..1d19bfc2486d 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -35,6 +35,7 @@ */ #define RSND_SSI_CLK_PIN_SHARE (1 << 31) #define RSND_SSI_SYNC (1 << 29) /* SSI34_sync etc */ +#define RSND_SSI_CLK_FROM_ADG (1 << 30) /* clock parent is master */ #define RSND_SSI_PLAY (1 << 24) -- cgit v1.2.3 From 629509c5bc478c0343d94c8c70812396f44447fb Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 23 Jan 2014 18:42:00 -0800 Subject: ASoC: rsnd: add Gen2 SRC and DMAEngine support Renesas sound Gen2 has SRC (= Sampling Rate Converter) which needs 2 DMAC. The data path image when you use SRC on Gen2 is [mem] -> Audio-DMAC -> SRC -> Audio-DMAC-peri-peri -> SSIU -> SSI This patch support SRC and DMAEnine. It is tested on R-Car H2 Lager board Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/rcar_snd.h | 6 +++ sound/soc/sh/rcar/adg.c | 138 +++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/sh/rcar/gen.c | 26 +++++++++ sound/soc/sh/rcar/rsnd.h | 25 +++++++++ sound/soc/sh/rcar/scu.c | 117 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 312 insertions(+) (limited to 'include') diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 1d19bfc2486d..2be05aea54f9 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -56,9 +56,15 @@ struct rsnd_ssi_platform_info { */ #define RSND_SCU_USE_HPBIF (1 << 31) /* it needs RSND_SSI_DEPENDENT */ +#define RSND_SCU_SET(rate, _dma_id) \ + { .flags = RSND_SCU_USE_HPBIF, .convert_rate = rate, .dma_id = _dma_id, } +#define RSND_SCU_UNUSED \ + { .flags = 0, .convert_rate = 0, .dma_id = 0, } + struct rsnd_scu_platform_info { u32 flags; u32 convert_rate; /* sampling rate convert */ + int dma_id; /* for Gen2 SCU */ }; /* diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 5bdffa480245..821791e15d04 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -30,6 +30,144 @@ struct rsnd_adg { i++, (pos) = adg->clk[i]) #define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg) + +static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_mod *mod) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + int id = rsnd_mod_id(mod); + int ws = id; + + if (rsnd_ssi_is_pin_sharing(rsnd_ssi_mod_get(priv, id))) { + switch (id) { + case 1: + case 2: + ws = 0; + break; + case 4: + ws = 3; + break; + case 8: + ws = 7; + break; + } + } + + return (0x6 + ws) << 8; +} + +static int rsnd_adg_set_src_timsel_gen2(struct rsnd_dai *rdai, + struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + u32 timsel) +{ + int is_play = rsnd_dai_is_play(rdai, io); + int id = rsnd_mod_id(mod); + int shift = (id % 2) ? 16 : 0; + u32 mask, ws; + u32 in, out; + + ws = rsnd_adg_ssi_ws_timing_gen2(mod); + + in = (is_play) ? timsel : ws; + out = (is_play) ? ws : timsel; + + in = in << shift; + out = out << shift; + mask = 0xffff << shift; + + switch (id / 2) { + case 0: + rsnd_mod_bset(mod, SRCIN_TIMSEL0, mask, in); + rsnd_mod_bset(mod, SRCOUT_TIMSEL0, mask, out); + break; + case 1: + rsnd_mod_bset(mod, SRCIN_TIMSEL1, mask, in); + rsnd_mod_bset(mod, SRCOUT_TIMSEL1, mask, out); + break; + case 2: + rsnd_mod_bset(mod, SRCIN_TIMSEL2, mask, in); + rsnd_mod_bset(mod, SRCOUT_TIMSEL2, mask, out); + break; + case 3: + rsnd_mod_bset(mod, SRCIN_TIMSEL3, mask, in); + rsnd_mod_bset(mod, SRCOUT_TIMSEL3, mask, out); + break; + case 4: + rsnd_mod_bset(mod, SRCIN_TIMSEL4, mask, in); + rsnd_mod_bset(mod, SRCOUT_TIMSEL4, mask, out); + break; + } + + return 0; +} + +int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io, + unsigned int src_rate, + unsigned int dst_rate) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct device *dev = rsnd_priv_to_dev(priv); + int idx, sel, div, step; + u32 val; + unsigned int min, diff; + unsigned int sel_rate [] = { + clk_get_rate(adg->clk[CLKA]), /* 0000: CLKA */ + clk_get_rate(adg->clk[CLKB]), /* 0001: CLKB */ + clk_get_rate(adg->clk[CLKC]), /* 0010: CLKC */ + adg->rbga_rate_for_441khz_div_6,/* 0011: RBGA */ + adg->rbgb_rate_for_48khz_div_6, /* 0100: RBGB */ + }; + + min = ~0; + val = 0; + for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) { + idx = 0; + step = 2; + + if (!sel_rate[sel]) + continue; + + for (div = 2; div <= 98304; div += step) { + diff = abs(src_rate - sel_rate[sel] / div); + if (min > diff) { + val = (sel << 8) | idx; + min = diff; + } + + /* + * step of 0_0000 / 0_0001 / 0_1101 + * are out of order + */ + if ((idx > 2) && (idx % 2)) + step *= 2; + if (idx == 0x1c) { + div += step; + step *= 2; + } + idx++; + } + } + + if (min == ~0) { + dev_err(dev, "no Input clock\n"); + return -EIO; + } + + return rsnd_adg_set_src_timsel_gen2(rdai, mod, io, val); +} + +int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + u32 val = rsnd_adg_ssi_ws_timing_gen2(mod); + + return rsnd_adg_set_src_timsel_gen2(rdai, mod, io, val); +} + int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv, struct rsnd_mod *mod, unsigned int src_rate, diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index db486aae6b8b..3e03a8bc4f75 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -229,14 +229,40 @@ static int rsnd_gen2_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen) RSND_GEN2_S_REG(gen, SSIU, SSI_MODE0, 0x800), RSND_GEN2_S_REG(gen, SSIU, SSI_MODE1, 0x804), /* FIXME: it needs SSI_MODE2/3 in the future */ + RSND_GEN2_S_REG(gen, SSIU, SSI_CONTROL, 0x810), + RSND_GEN2_M_REG(gen, SSIU, SSI_BUSIF_MODE, 0x0, 0x80), + RSND_GEN2_M_REG(gen, SSIU, SSI_BUSIF_ADINR,0x4, 0x80), + RSND_GEN2_M_REG(gen, SSIU, SSI_CTRL, 0x10, 0x80), RSND_GEN2_M_REG(gen, SSIU, INT_ENABLE, 0x18, 0x80), + RSND_GEN2_M_REG(gen, SCU, SRC_BUSIF_MODE, 0x0, 0x20), + RSND_GEN2_M_REG(gen, SCU, SRC_ROUTE_MODE0,0xc, 0x20), + RSND_GEN2_M_REG(gen, SCU, SRC_CTRL, 0x10, 0x20), + RSND_GEN2_M_REG(gen, SCU, SRC_SWRSR, 0x200, 0x40), + RSND_GEN2_M_REG(gen, SCU, SRC_SRCIR, 0x204, 0x40), + RSND_GEN2_M_REG(gen, SCU, SRC_ADINR, 0x214, 0x40), + RSND_GEN2_M_REG(gen, SCU, SRC_IFSCR, 0x21c, 0x40), + RSND_GEN2_M_REG(gen, SCU, SRC_IFSVR, 0x220, 0x40), + RSND_GEN2_M_REG(gen, SCU, SRC_SRCCR, 0x224, 0x40), + RSND_GEN2_M_REG(gen, SCU, SRC_BSDSR, 0x22c, 0x40), + RSND_GEN2_M_REG(gen, SCU, SRC_BSISR, 0x238, 0x40), + RSND_GEN2_S_REG(gen, ADG, BRRA, 0x00), RSND_GEN2_S_REG(gen, ADG, BRRB, 0x04), RSND_GEN2_S_REG(gen, ADG, SSICKR, 0x08), RSND_GEN2_S_REG(gen, ADG, AUDIO_CLK_SEL0, 0x0c), RSND_GEN2_S_REG(gen, ADG, AUDIO_CLK_SEL1, 0x10), RSND_GEN2_S_REG(gen, ADG, AUDIO_CLK_SEL2, 0x14), + RSND_GEN2_S_REG(gen, ADG, SRCIN_TIMSEL0, 0x34), + RSND_GEN2_S_REG(gen, ADG, SRCIN_TIMSEL1, 0x38), + RSND_GEN2_S_REG(gen, ADG, SRCIN_TIMSEL2, 0x3c), + RSND_GEN2_S_REG(gen, ADG, SRCIN_TIMSEL3, 0x40), + RSND_GEN2_S_REG(gen, ADG, SRCIN_TIMSEL4, 0x44), + RSND_GEN2_S_REG(gen, ADG, SRCOUT_TIMSEL0, 0x48), + RSND_GEN2_S_REG(gen, ADG, SRCOUT_TIMSEL1, 0x4c), + RSND_GEN2_S_REG(gen, ADG, SRCOUT_TIMSEL2, 0x50), + RSND_GEN2_S_REG(gen, ADG, SRCOUT_TIMSEL3, 0x54), + RSND_GEN2_S_REG(gen, ADG, SRCOUT_TIMSEL4, 0x58), RSND_GEN2_M_REG(gen, SSI, SSICR, 0x00, 0x40), RSND_GEN2_M_REG(gen, SSI, SSISR, 0x04, 0x40), diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index b2c717d2ba7e..8b66dc15fa73 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -37,6 +37,11 @@ enum rsnd_reg { RSND_REG_SRC_TMG_SEL1, /* for Gen1 */ RSND_REG_SRC_TMG_SEL2, /* for Gen1 */ RSND_REG_SRC_ROUTE_CTRL, /* for Gen1 */ + RSND_REG_SRC_CTRL, /* for Gen2 */ + RSND_REG_SSI_CTRL, /* for Gen2 */ + RSND_REG_SSI_CONTROL, + RSND_REG_SSI_BUSIF_MODE, /* for Gen2 */ + RSND_REG_SSI_BUSIF_ADINR, /* for Gen2 */ RSND_REG_SSI_MODE0, RSND_REG_SSI_MODE1, RSND_REG_INT_ENABLE, /* for Gen2 */ @@ -49,6 +54,8 @@ enum rsnd_reg { RSND_REG_SRC_IFSVR, RSND_REG_SRC_SRCCR, RSND_REG_SRC_MNFSR, /* for Gen1 */ + RSND_REG_SRC_BSDSR, /* for Gen2 */ + RSND_REG_SRC_BSISR, /* for Gen2 */ /* ADG */ RSND_REG_BRRA, @@ -60,6 +67,16 @@ enum rsnd_reg { RSND_REG_AUDIO_CLK_SEL3, /* for Gen1 */ RSND_REG_AUDIO_CLK_SEL4, /* for Gen1 */ RSND_REG_AUDIO_CLK_SEL5, /* for Gen1 */ + RSND_REG_SRCIN_TIMSEL0, /* for Gen2 */ + RSND_REG_SRCIN_TIMSEL1, /* for Gen2 */ + RSND_REG_SRCIN_TIMSEL2, /* for Gen2 */ + RSND_REG_SRCIN_TIMSEL3, /* for Gen2 */ + RSND_REG_SRCIN_TIMSEL4, /* for Gen2 */ + RSND_REG_SRCOUT_TIMSEL0, /* for Gen2 */ + RSND_REG_SRCOUT_TIMSEL1, /* for Gen2 */ + RSND_REG_SRCOUT_TIMSEL2, /* for Gen2 */ + RSND_REG_SRCOUT_TIMSEL3, /* for Gen2 */ + RSND_REG_SRCOUT_TIMSEL4, /* for Gen2 */ /* SSI */ RSND_REG_SSICR, @@ -250,6 +267,14 @@ int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv, struct rsnd_mod *mod, unsigned int src_rate, unsigned int dst_rate); +int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io, + unsigned int src_rate, + unsigned int dst_rate); +int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); /* * R-Car sound priv diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c index 29d8990e3f0f..6e5c763e1040 100644 --- a/sound/soc/sh/rcar/scu.c +++ b/sound/soc/sh/rcar/scu.c @@ -33,6 +33,8 @@ struct rsnd_scu { container_of((_mod), struct rsnd_scu, mod) #define rsnd_scu_hpbif_is_enable(scu) \ (rsnd_scu_mode_flags(scu) & RSND_SCU_USE_HPBIF) +#define rsnd_scu_dma_available(scu) \ + rsnd_dma_available(rsnd_mod_to_dma(&(scu)->mod)) #define for_each_rsnd_scu(pos, priv, i) \ for ((i) = 0; \ @@ -472,6 +474,103 @@ static struct rsnd_mod_ops rsnd_scu_non_gen1_ops = { /* * Gen2 functions */ +static int rsnd_scu_set_convert_rate_gen2(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + int ret; + + ret = rsnd_scu_set_convert_rate(mod, rdai, io); + if (ret < 0) + return ret; + + rsnd_mod_write(mod, SSI_BUSIF_ADINR, rsnd_mod_read(mod, SRC_ADINR)); + rsnd_mod_write(mod, SSI_BUSIF_MODE, rsnd_mod_read(mod, SRC_BUSIF_MODE)); + + rsnd_mod_write(mod, SRC_SRCCR, 0x00011110); + + rsnd_mod_write(mod, SRC_BSDSR, 0x01800000); + rsnd_mod_write(mod, SRC_BSISR, 0x00100060); + + return 0; +} + +static int rsnd_scu_set_convert_timing_gen2(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_scu *scu = rsnd_mod_to_scu(mod); + u32 convert_rate = rsnd_scu_convert_rate(scu); + int ret; + + if (convert_rate) + ret = rsnd_adg_set_convert_clk_gen2(mod, rdai, io, + runtime->rate, + convert_rate); + else + ret = rsnd_adg_set_convert_timing_gen2(mod, rdai, io); + + return ret; +} + +static int rsnd_scu_init_gen2(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + int ret; + + ret = rsnd_scu_init(mod, rdai, io); + if (ret < 0) + return ret; + + ret = rsnd_scu_set_convert_rate_gen2(mod, rdai, io); + if (ret < 0) + return ret; + + ret = rsnd_scu_set_convert_timing_gen2(mod, rdai, io); + if (ret < 0) + return ret; + + return 0; +} + +static int rsnd_scu_start_gen2(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_scu *scu = rsnd_mod_to_scu(mod); + + rsnd_dma_start(rsnd_mod_to_dma(&scu->mod)); + + rsnd_mod_write(mod, SSI_CTRL, 0x1); + rsnd_mod_write(mod, SRC_CTRL, 0x11); + + return rsnd_scu_start(mod, rdai, io); +} + +static int rsnd_scu_stop_gen2(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_scu *scu = rsnd_mod_to_scu(mod); + + rsnd_mod_write(mod, SSI_CTRL, 0); + rsnd_mod_write(mod, SRC_CTRL, 0); + + rsnd_dma_stop(rsnd_mod_to_dma(&scu->mod)); + + return rsnd_scu_stop(mod, rdai, io); +} + +static struct rsnd_mod_ops rsnd_scu_gen2_ops = { + .name = "scu (gen2)", + .init = rsnd_scu_init_gen2, + .quit = rsnd_scu_quit, + .start = rsnd_scu_start_gen2, + .stop = rsnd_scu_stop_gen2, +}; + static int rsnd_scu_start_non_gen2(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) @@ -534,6 +633,17 @@ int rsnd_scu_probe(struct platform_device *pdev, if (rsnd_scu_hpbif_is_enable(scu)) { if (rsnd_is_gen1(priv)) ops = &rsnd_scu_gen1_ops; + if (rsnd_is_gen2(priv)) { + struct rsnd_mod *ssi = rsnd_ssi_mod_get(priv, i); + int ret = rsnd_dma_init(priv, + rsnd_mod_to_dma(&scu->mod), + rsnd_ssi_is_play(ssi), + scu->info->dma_id); + if (ret < 0) + return ret; + + ops = &rsnd_scu_gen2_ops; + } } else { if (rsnd_is_gen1(priv)) ops = &rsnd_scu_non_gen1_ops; @@ -553,4 +663,11 @@ int rsnd_scu_probe(struct platform_device *pdev, void rsnd_scu_remove(struct platform_device *pdev, struct rsnd_priv *priv) { + struct rsnd_scu *scu; + int i; + + for_each_rsnd_scu(scu, priv, i) { + if (rsnd_scu_dma_available(scu)) + rsnd_dma_quit(priv, rsnd_mod_to_dma(&scu->mod)); + } } -- cgit v1.2.3 From f227b88f0fce5f9b82aa934f8829a741c2e06d82 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Thu, 16 Jan 2014 16:02:10 +0100 Subject: ASoC: core: Add signed register volume control logic Some codecs use signed volume control representation with non standard register sizes, e.g. 6 or 7 bit signed integers. This patch adds generic signed register volume control logic to soc-core. Instead of a fixed width signed register control, this implementation uses a 'min' value and the signed bit location to translate it to an absolute volume. Using the 'sign_bit' we can calculate a correct mask for the register values and translate it back into signed integers of standard size. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- include/sound/soc.h | 1 + sound/soc/soc-core.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 62 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 9a001472b96a..a25f3ccb3fde 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1067,6 +1067,7 @@ struct soc_mixer_control { int min, max, platform_max; int reg, rreg; unsigned int shift, rshift; + unsigned int sign_bit; unsigned int invert:1; unsigned int autodisable:1; }; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index fe1df50805a3..c1d9d8539ee9 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2715,6 +2715,48 @@ int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_put_value_enum_double); +/** + * snd_soc_read_signed - Read a codec register and interprete as signed value + * @codec: codec + * @reg: Register to read + * @mask: Mask to use after shifting the register value + * @shift: Right shift of register value + * @sign_bit: Bit that describes if a number is negative or not. + * + * This functions reads a codec register. The register value is shifted right + * by 'shift' bits and masked with the given 'mask'. Afterwards it translates + * the given registervalue into a signed integer if sign_bit is non-zero. + * + * Returns the register value as signed int. + */ +static int snd_soc_read_signed(struct snd_soc_codec *codec, unsigned int reg, + unsigned int mask, unsigned int shift, unsigned int sign_bit) +{ + int ret; + unsigned int val; + + val = (snd_soc_read(codec, reg) >> shift) & mask; + + if (!sign_bit) + return val; + + /* non-negative number */ + if (!(val & BIT(sign_bit))) + return val; + + ret = val; + + /* + * The register most probably does not contain a full-sized int. + * Instead we have an arbitrary number of bits in a signed + * representation which has to be translated into a full-sized int. + * This is done by filling up all bits above the sign-bit. + */ + ret |= ~((int)(BIT(sign_bit) - 1)); + + return ret; +} + /** * snd_soc_info_volsw - single mixer info callback * @kcontrol: mixer control @@ -2743,7 +2785,7 @@ int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1; uinfo->value.integer.min = 0; - uinfo->value.integer.max = platform_max; + uinfo->value.integer.max = platform_max - mc->min; return 0; } EXPORT_SYMBOL_GPL(snd_soc_info_volsw); @@ -2769,11 +2811,16 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, unsigned int shift = mc->shift; unsigned int rshift = mc->rshift; int max = mc->max; + int min = mc->min; + int sign_bit = mc->sign_bit; unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; - ucontrol->value.integer.value[0] = - (snd_soc_read(codec, reg) >> shift) & mask; + if (sign_bit) + mask = BIT(sign_bit + 1) - 1; + + ucontrol->value.integer.value[0] = snd_soc_read_signed(codec, reg, mask, + shift, sign_bit) - min; if (invert) ucontrol->value.integer.value[0] = max - ucontrol->value.integer.value[0]; @@ -2781,10 +2828,12 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, if (snd_soc_volsw_is_stereo(mc)) { if (reg == reg2) ucontrol->value.integer.value[1] = - (snd_soc_read(codec, reg) >> rshift) & mask; + snd_soc_read_signed(codec, reg, mask, rshift, + sign_bit) - min; else ucontrol->value.integer.value[1] = - (snd_soc_read(codec, reg2) >> shift) & mask; + snd_soc_read_signed(codec, reg2, mask, shift, + sign_bit) - min; if (invert) ucontrol->value.integer.value[1] = max - ucontrol->value.integer.value[1]; @@ -2815,6 +2864,8 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, unsigned int shift = mc->shift; unsigned int rshift = mc->rshift; int max = mc->max; + int min = mc->min; + unsigned int sign_bit = mc->sign_bit; unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; int err; @@ -2822,13 +2873,16 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, unsigned int val2 = 0; unsigned int val, val_mask; - val = (ucontrol->value.integer.value[0] & mask); + if (sign_bit) + mask = BIT(sign_bit + 1) - 1; + + val = ((ucontrol->value.integer.value[0] + min) & mask); if (invert) val = max - val; val_mask = mask << shift; val = val << shift; if (snd_soc_volsw_is_stereo(mc)) { - val2 = (ucontrol->value.integer.value[1] & mask); + val2 = ((ucontrol->value.integer.value[1] + min) & mask); if (invert) val2 = max - val2; if (reg == reg2) { -- cgit v1.2.3 From cd21b123346c6a2f033d8c3bd2bf240198b5712a Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Thu, 16 Jan 2014 16:02:11 +0100 Subject: ASoC: core: Add SOC_DOUBLE_R_S_TLV Add a macro for signed mixer with two registers and tlv array. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- include/sound/soc.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index a25f3ccb3fde..c4be7ab89399 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -45,6 +45,11 @@ ((unsigned long)&(struct soc_mixer_control) \ {.reg = xlreg, .rreg = xrreg, .shift = xshift, .rshift = xshift, \ .max = xmax, .platform_max = xmax, .invert = xinvert}) +#define SOC_DOUBLE_R_S_VALUE(xlreg, xrreg, xshift, xmin, xmax, xsign_bit, xinvert) \ + ((unsigned long)&(struct soc_mixer_control) \ + {.reg = xlreg, .rreg = xrreg, .shift = xshift, .rshift = xshift, \ + .max = xmax, .min = xmin, .platform_max = xmax, .sign_bit = xsign_bit, \ + .invert = xinvert}) #define SOC_DOUBLE_R_RANGE_VALUE(xlreg, xrreg, xshift, xmin, xmax, xinvert) \ ((unsigned long)&(struct soc_mixer_control) \ {.reg = xlreg, .rreg = xrreg, .shift = xshift, .rshift = xshift, \ @@ -152,6 +157,15 @@ {.reg = xreg, .rreg = xrreg, \ .shift = xshift, .rshift = xshift, \ .max = xmax, .min = xmin} } +#define SOC_DOUBLE_R_S_TLV(xname, reg_left, reg_right, xshift, xmin, xmax, xsign_bit, xinvert, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ + SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, \ + .get = snd_soc_get_volsw, .put = snd_soc_put_volsw, \ + .private_value = SOC_DOUBLE_R_S_VALUE(reg_left, reg_right, xshift, \ + xmin, xmax, xsign_bit, xinvert) } #define SOC_DOUBLE_S8_TLV(xname, xreg, xmin, xmax, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ -- cgit v1.2.3 From 45fce59496cbabd46761e6980c05c82d94d08eaa Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 15 Jan 2014 16:51:56 +0100 Subject: ASoC: simple-card: simplify code The platform data structure contains information which is used only by the driver, and the driver allocates platform information fields which are of no use. Move the driver specific data to a new private structure and cleanup the platform data structure. Signed-off-by: Jean-Francois Moine Signed-off-by: Mark Brown --- include/sound/simple_card.h | 4 ---- sound/soc/generic/simple-card.c | 14 +++++++++++--- 2 files changed, 11 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/sound/simple_card.h b/include/sound/simple_card.h index 6c74527d4926..e1ac996c8feb 100644 --- a/include/sound/simple_card.h +++ b/include/sound/simple_card.h @@ -29,10 +29,6 @@ struct asoc_simple_card_info { unsigned int daifmt; struct asoc_simple_dai cpu_dai; struct asoc_simple_dai codec_dai; - - /* used in simple-card.c */ - struct snd_soc_dai_link snd_link; - struct snd_soc_card snd_card; }; #endif /* __SIMPLE_CARD_H */ diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 90c6fd5c2c7e..0f11c2808437 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -15,6 +15,14 @@ #include #include +struct simple_card_data { + struct snd_soc_card snd_card; + unsigned int daifmt; + struct asoc_simple_dai cpu_dai; + struct asoc_simple_dai codec_dai; + struct snd_soc_dai_link snd_link; +}; + static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai, struct asoc_simple_dai *set, unsigned int daifmt) @@ -39,7 +47,7 @@ static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai, static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) { - struct asoc_simple_card_info *info = + struct simple_card_data *info = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_dai *codec = rtd->codec_dai; struct snd_soc_dai *cpu = rtd->cpu_dai; @@ -121,7 +129,7 @@ parse_error: } static int asoc_simple_card_parse_of(struct device_node *node, - struct asoc_simple_card_info *info, + struct simple_card_data *info, struct device *dev) { struct snd_soc_dai_link *dai_link = info->snd_card.dai_link; @@ -195,7 +203,7 @@ static int asoc_simple_card_parse_of(struct device_node *node, static int asoc_simple_card_probe(struct platform_device *pdev) { - struct asoc_simple_card_info *priv; + struct simple_card_data *priv; struct snd_soc_dai_link *dai_link; struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; -- cgit v1.2.3 From d1debafc381cb1fa340b5d0dc79637ad1d523770 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 3 Feb 2014 14:51:51 +0200 Subject: ASoC: davinci-mcasp: Rename platform data struct Rename the struct for the platform data: snd_platform_data -> davinci_mcasp_pdata Since we have users under arch/arm/mach-davinci/ for this struct add temporary define to avoid breakage. The arch code can be updated later to use the new struct name. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- include/linux/platform_data/davinci_asp.h | 4 +++- sound/soc/davinci/davinci-mcasp.c | 16 ++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/linux/platform_data/davinci_asp.h b/include/linux/platform_data/davinci_asp.h index 5245992b0367..85ad68f9206a 100644 --- a/include/linux/platform_data/davinci_asp.h +++ b/include/linux/platform_data/davinci_asp.h @@ -18,7 +18,7 @@ #include -struct snd_platform_data { +struct davinci_mcasp_pdata { u32 tx_dma_offset; u32 rx_dma_offset; int asp_chan_q; /* event queue number for ASP channel */ @@ -87,6 +87,8 @@ struct snd_platform_data { int tx_dma_channel; int rx_dma_channel; }; +/* TODO: Fix arch/arm/mach-davinci/ users and remove this define */ +#define snd_platform_data davinci_mcasp_pdata enum { MCASP_VERSION_1 = 0, /* DM646x */ diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 63b1ecc97cb1..e5fce2ed4dc6 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -823,28 +823,28 @@ static const struct snd_soc_component_driver davinci_mcasp_component = { }; /* Some HW specific values and defaults. The rest is filled in from DT. */ -static struct snd_platform_data dm646x_mcasp_pdata = { +static struct davinci_mcasp_pdata dm646x_mcasp_pdata = { .tx_dma_offset = 0x400, .rx_dma_offset = 0x400, .asp_chan_q = EVENTQ_0, .version = MCASP_VERSION_1, }; -static struct snd_platform_data da830_mcasp_pdata = { +static struct davinci_mcasp_pdata da830_mcasp_pdata = { .tx_dma_offset = 0x2000, .rx_dma_offset = 0x2000, .asp_chan_q = EVENTQ_0, .version = MCASP_VERSION_2, }; -static struct snd_platform_data am33xx_mcasp_pdata = { +static struct davinci_mcasp_pdata am33xx_mcasp_pdata = { .tx_dma_offset = 0, .rx_dma_offset = 0, .asp_chan_q = EVENTQ_0, .version = MCASP_VERSION_3, }; -static struct snd_platform_data dra7_mcasp_pdata = { +static struct davinci_mcasp_pdata dra7_mcasp_pdata = { .tx_dma_offset = 0x200, .rx_dma_offset = 0x284, .asp_chan_q = EVENTQ_0, @@ -912,11 +912,11 @@ err1: return ret; } -static struct snd_platform_data *davinci_mcasp_set_pdata_from_of( +static struct davinci_mcasp_pdata *davinci_mcasp_set_pdata_from_of( struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - struct snd_platform_data *pdata = NULL; + struct davinci_mcasp_pdata *pdata = NULL; const struct of_device_id *match = of_match_device(mcasp_dt_ids, &pdev->dev); struct of_phandle_args dma_spec; @@ -929,7 +929,7 @@ static struct snd_platform_data *davinci_mcasp_set_pdata_from_of( pdata = pdev->dev.platform_data; return pdata; } else if (match) { - pdata = (struct snd_platform_data *) match->data; + pdata = (struct davinci_mcasp_pdata*) match->data; } else { /* control shouldn't reach here. something is wrong */ ret = -EINVAL; @@ -1023,7 +1023,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) { struct davinci_pcm_dma_params *dma_data; struct resource *mem, *ioarea, *res, *dat; - struct snd_platform_data *pdata; + struct davinci_mcasp_pdata *pdata; struct davinci_mcasp *mcasp; int ret; -- cgit v1.2.3 From 9a6d48605e632e84db2895cf752c65b3c908cd09 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Sat, 8 Feb 2014 15:59:52 +0800 Subject: ASoC: add snd_soc_of_parse_audio_simple_widgets for DT This patch adds snd_soc_of_parse_audio_simple_widgets() and supports below style of widgets name on DT: "template-wname", "user supplied wname" For instance: simple-audio-widgets = "Microphone", "Microphone Jack", "Line", "Line In Jack", "Line", "Line Out Jack", "Headphone", "Headphone Jack", "Speaker", "Speaker External"; The "template-wname" currently includes: "Microphone", "Line", "Headphone" and "Speaker". Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- include/sound/soc.h | 2 ++ sound/soc/soc-core.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 9a001472b96a..465dc6e0674d 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1173,6 +1173,8 @@ void snd_soc_util_exit(void); int snd_soc_of_parse_card_name(struct snd_soc_card *card, const char *propname); +int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, + const char *propname); int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, const char *propname); unsigned int snd_soc_of_parse_daifmt(struct device_node *np, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index fe1df50805a3..0540cb08e0ea 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4417,6 +4417,93 @@ int snd_soc_of_parse_card_name(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(snd_soc_of_parse_card_name); +static const struct snd_soc_dapm_widget simple_widgets[] = { + SND_SOC_DAPM_MIC("Microphone", NULL), + SND_SOC_DAPM_LINE("Line", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), +}; + +int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, + const char *propname) +{ + struct device_node *np = card->dev->of_node; + struct snd_soc_dapm_widget *widgets; + const char *template, *wname; + int i, j, num_widgets, ret; + + num_widgets = of_property_count_strings(np, propname); + if (num_widgets < 0) { + dev_err(card->dev, + "ASoC: Property '%s' does not exist\n", propname); + return -EINVAL; + } + if (num_widgets & 1) { + dev_err(card->dev, + "ASoC: Property '%s' length is not even\n", propname); + return -EINVAL; + } + + num_widgets /= 2; + if (!num_widgets) { + dev_err(card->dev, "ASoC: Property '%s's length is zero\n", + propname); + return -EINVAL; + } + + widgets = devm_kcalloc(card->dev, num_widgets, sizeof(*widgets), + GFP_KERNEL); + if (!widgets) { + dev_err(card->dev, + "ASoC: Could not allocate memory for widgets\n"); + return -ENOMEM; + } + + for (i = 0; i < num_widgets; i++) { + ret = of_property_read_string_index(np, propname, + 2 * i, &template); + if (ret) { + dev_err(card->dev, + "ASoC: Property '%s' index %d read error:%d\n", + propname, 2 * i, ret); + return -EINVAL; + } + + for (j = 0; j < ARRAY_SIZE(simple_widgets); j++) { + if (!strncmp(template, simple_widgets[j].name, + strlen(simple_widgets[j].name))) { + widgets[i] = simple_widgets[j]; + break; + } + } + + if (j >= ARRAY_SIZE(simple_widgets)) { + dev_err(card->dev, + "ASoC: DAPM widget '%s' is not supported\n", + template); + return -EINVAL; + } + + ret = of_property_read_string_index(np, propname, + (2 * i) + 1, + &wname); + if (ret) { + dev_err(card->dev, + "ASoC: Property '%s' index %d read error:%d\n", + propname, (2 * i) + 1, ret); + return -EINVAL; + } + + widgets[i].name = wname; + } + + card->dapm_widgets = widgets; + card->num_dapm_widgets = num_widgets; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets); + int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, const char *propname) { -- cgit v1.2.3 From 048d4ff81f1cf26b3f3627a9a69d35aff7898bb3 Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Mon, 10 Feb 2014 14:09:45 +0800 Subject: ASoC: atmel_ssc_dai: make option to choose clock When SSC works in slave mode, according to the hardware design, the clock can get from TK pin, also can get from RK pin. So, add one parameter to choose where the clock from. Signed-off-by: Bo Shen Acked-by: Nicolas Ferre Signed-off-by: Mark Brown --- drivers/misc/atmel-ssc.c | 6 ++++++ include/linux/atmel-ssc.h | 1 + sound/soc/atmel/atmel_ssc_dai.c | 13 +++++++++---- 3 files changed, 16 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c index 5be808406edc..22de13727641 100644 --- a/drivers/misc/atmel-ssc.c +++ b/drivers/misc/atmel-ssc.c @@ -150,6 +150,12 @@ static int ssc_probe(struct platform_device *pdev) return -ENODEV; ssc->pdata = (struct atmel_ssc_platform_data *)plat_dat; + if (pdev->dev.of_node) { + struct device_node *np = pdev->dev.of_node; + ssc->clk_from_rk_pin = + of_property_read_bool(np, "atmel,clk-from-rk-pin"); + } + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); ssc->regs = devm_ioremap_resource(&pdev->dev, regs); if (IS_ERR(ssc->regs)) diff --git a/include/linux/atmel-ssc.h b/include/linux/atmel-ssc.h index 66a0e5384edd..571a12ebb018 100644 --- a/include/linux/atmel-ssc.h +++ b/include/linux/atmel-ssc.h @@ -18,6 +18,7 @@ struct ssc_device { struct clk *clk; int user; int irq; + bool clk_from_rk_pin; }; struct ssc_device * __must_check ssc_request(unsigned int ssc_num); diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 1ead3c977a51..de433cfd044c 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -341,6 +341,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, { int id = dai->id; struct atmel_ssc_info *ssc_p = &ssc_info[id]; + struct ssc_device *ssc = ssc_p->ssc; struct atmel_pcm_dma_params *dma_params; int dir, channels, bits; u32 tfmr, rfmr, tcmr, rcmr; @@ -466,7 +467,8 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, | SSC_BF(RCMR_START, start_event) | SSC_BF(RCMR_CKI, SSC_CKI_RISING) | SSC_BF(RCMR_CKO, SSC_CKO_NONE) - | SSC_BF(RCMR_CKS, SSC_CKS_CLOCK); + | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ? + SSC_CKS_PIN : SSC_CKS_CLOCK); rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(RFMR_FSOS, SSC_FSOS_NONE) @@ -481,7 +483,8 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, | SSC_BF(TCMR_START, start_event) | SSC_BF(TCMR_CKI, SSC_CKI_FALLING) | SSC_BF(TCMR_CKO, SSC_CKO_NONE) - | SSC_BF(TCMR_CKS, SSC_CKS_PIN); + | SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ? + SSC_CKS_CLOCK : SSC_CKS_PIN); tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(TFMR_FSDEN, 0) @@ -550,7 +553,8 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, | SSC_BF(RCMR_START, SSC_START_RISING_RF) | SSC_BF(RCMR_CKI, SSC_CKI_RISING) | SSC_BF(RCMR_CKO, SSC_CKO_NONE) - | SSC_BF(RCMR_CKS, SSC_CKS_PIN); + | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ? + SSC_CKS_PIN : SSC_CKS_CLOCK); rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(RFMR_FSOS, SSC_FSOS_NONE) @@ -565,7 +569,8 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, | SSC_BF(TCMR_START, SSC_START_RISING_RF) | SSC_BF(TCMR_CKI, SSC_CKI_FALLING) | SSC_BF(TCMR_CKO, SSC_CKO_NONE) - | SSC_BF(TCMR_CKS, SSC_CKS_PIN); + | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ? + SSC_CKS_CLOCK : SSC_CKS_PIN); tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(TFMR_FSDEN, 0) -- cgit v1.2.3 From 603597c9375b8162edae3231dd4cc7f1f500de79 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 18 Feb 2014 15:57:35 +0100 Subject: ASoC: Add ADAU1977 CODEC driver This patch adds support for the ADAU1977, ADAU1978 and ADAU1979 audio CODEC devices. They are a family of 4-channel differential input audio ADC devices. They can be connected to either a SPI or I2C bus. The driver is implemented in three modules, one main module (adau1977.ko) which implements the device logic and one module each for SPI (adau1977-spi.ko) and I2C (adau1977-i2c.ko) bus access. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/linux/platform_data/adau1977.h | 45 ++ sound/soc/codecs/Kconfig | 15 + sound/soc/codecs/Makefile | 6 + sound/soc/codecs/adau1977-i2c.c | 59 ++ sound/soc/codecs/adau1977-spi.c | 76 +++ sound/soc/codecs/adau1977.c | 1018 ++++++++++++++++++++++++++++++++ sound/soc/codecs/adau1977.h | 37 ++ 7 files changed, 1256 insertions(+) create mode 100644 include/linux/platform_data/adau1977.h create mode 100644 sound/soc/codecs/adau1977-i2c.c create mode 100644 sound/soc/codecs/adau1977-spi.c create mode 100644 sound/soc/codecs/adau1977.c create mode 100644 sound/soc/codecs/adau1977.h (limited to 'include') diff --git a/include/linux/platform_data/adau1977.h b/include/linux/platform_data/adau1977.h new file mode 100644 index 000000000000..bed11d908f92 --- /dev/null +++ b/include/linux/platform_data/adau1977.h @@ -0,0 +1,45 @@ +/* + * ADAU1977/ADAU1978/ADAU1979 driver + * + * Copyright 2014 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2. + */ + +#ifndef __LINUX_PLATFORM_DATA_ADAU1977_H__ +#define __LINUX_PLATFORM_DATA_ADAU1977_H__ + +/** + * enum adau1977_micbias - ADAU1977 MICBIAS pin voltage setting + * @ADAU1977_MICBIAS_5V0: MICBIAS is set to 5.0 V + * @ADAU1977_MICBIAS_5V5: MICBIAS is set to 5.5 V + * @ADAU1977_MICBIAS_6V0: MICBIAS is set to 6.0 V + * @ADAU1977_MICBIAS_6V5: MICBIAS is set to 6.5 V + * @ADAU1977_MICBIAS_7V0: MICBIAS is set to 7.0 V + * @ADAU1977_MICBIAS_7V5: MICBIAS is set to 7.5 V + * @ADAU1977_MICBIAS_8V0: MICBIAS is set to 8.0 V + * @ADAU1977_MICBIAS_8V5: MICBIAS is set to 8.5 V + * @ADAU1977_MICBIAS_9V0: MICBIAS is set to 9.0 V + */ +enum adau1977_micbias { + ADAU1977_MICBIAS_5V0 = 0x0, + ADAU1977_MICBIAS_5V5 = 0x1, + ADAU1977_MICBIAS_6V0 = 0x2, + ADAU1977_MICBIAS_6V5 = 0x3, + ADAU1977_MICBIAS_7V0 = 0x4, + ADAU1977_MICBIAS_7V5 = 0x5, + ADAU1977_MICBIAS_8V0 = 0x6, + ADAU1977_MICBIAS_8V5 = 0x7, + ADAU1977_MICBIAS_9V0 = 0x8, +}; + +/** + * struct adau1977_platform_data - Platform configuration data for the ADAU1977 + * @micbias: Specifies the voltage for the MICBIAS pin + */ +struct adau1977_platform_data { + enum adau1977_micbias micbias; +}; + +#endif diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 9ea428bdd872..4535674f6eba 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -24,6 +24,8 @@ config SND_SOC_ALL_CODECS select SND_SOC_ADAU1373 if I2C select SND_SOC_ADAV801 if SPI_MASTER select SND_SOC_ADAV803 if I2C + select SND_SOC_ADAU1977_SPI if SPI_MASTER + select SND_SOC_ADAU1977_I2C if I2C select SND_SOC_ADAU1701 if I2C select SND_SOC_ADS117X select SND_SOC_AK4104 if SPI_MASTER @@ -200,6 +202,19 @@ config SND_SOC_ADAU1701 config SND_SOC_ADAU1373 tristate +config SND_SOC_ADAU1977 + tristate + +config SND_SOC_ADAU1977_SPI + tristate + select SND_SOC_ADAU1977 + select REGMAP_SPI + +config SND_SOC_ADAU1977_I2C + tristate + select SND_SOC_ADAU1977 + select REGMAP_I2C + config SND_SOC_ADAV80X tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 1b2c6eca863f..bfd85ec2dfa8 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -7,6 +7,9 @@ snd-soc-ad1980-objs := ad1980.o snd-soc-ad73311-objs := ad73311.o snd-soc-adau1701-objs := adau1701.o snd-soc-adau1373-objs := adau1373.o +snd-soc-adau1977-objs := adau1977.o +snd-soc-adau1977-spi-objs := adau1977-spi.o +snd-soc-adau1977-i2c-objs := adau1977-i2c.o snd-soc-adav80x-objs := adav80x.o snd-soc-adav801-objs := adav801.o snd-soc-adav803-objs := adav803.o @@ -139,6 +142,9 @@ obj-$(CONFIG_SND_SOC_AD193X) += snd-soc-ad193x.o obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o obj-$(CONFIG_SND_SOC_ADAU1373) += snd-soc-adau1373.o +obj-$(CONFIG_SND_SOC_ADAU1977) += snd-soc-adau1977.o +obj-$(CONFIG_SND_SOC_ADAU1977_SPI) += snd-soc-adau1977-spi.o +obj-$(CONFIG_SND_SOC_ADAU1977_I2C) += snd-soc-adau1977-i2c.o obj-$(CONFIG_SND_SOC_ADAU1701) += snd-soc-adau1701.o obj-$(CONFIG_SND_SOC_ADAV80X) += snd-soc-adav80x.o obj-$(CONFIG_SND_SOC_ADAV801) += snd-soc-adav801.o diff --git a/sound/soc/codecs/adau1977-i2c.c b/sound/soc/codecs/adau1977-i2c.c new file mode 100644 index 000000000000..9700e8c838c9 --- /dev/null +++ b/sound/soc/codecs/adau1977-i2c.c @@ -0,0 +1,59 @@ +/* + * ADAU1977/ADAU1978/ADAU1979 driver + * + * Copyright 2014 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include + +#include "adau1977.h" + +static int adau1977_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct regmap_config config; + + config = adau1977_regmap_config; + config.val_bits = 8; + config.reg_bits = 8; + + return adau1977_probe(&client->dev, + devm_regmap_init_i2c(client, &config), + id->driver_data, NULL); +} + +static int adau1977_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id adau1977_i2c_ids[] = { + { "adau1977", ADAU1977 }, + { "adau1978", ADAU1978 }, + { "adau1979", ADAU1978 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adau1977_i2c_ids); + +static struct i2c_driver adau1977_i2c_driver = { + .driver = { + .name = "adau1977", + .owner = THIS_MODULE, + }, + .probe = adau1977_i2c_probe, + .remove = adau1977_i2c_remove, + .id_table = adau1977_i2c_ids, +}; +module_i2c_driver(adau1977_i2c_driver); + +MODULE_DESCRIPTION("ASoC ADAU1977/ADAU1978/ADAU1979 driver"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adau1977-spi.c b/sound/soc/codecs/adau1977-spi.c new file mode 100644 index 000000000000..b05cf5da3a94 --- /dev/null +++ b/sound/soc/codecs/adau1977-spi.c @@ -0,0 +1,76 @@ +/* + * ADAU1977/ADAU1978/ADAU1979 driver + * + * Copyright 2014 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include + +#include "adau1977.h" + +static void adau1977_spi_switch_mode(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + + /* + * To get the device into SPI mode CLATCH has to be pulled low three + * times. Do this by issuing three dummy reads. + */ + spi_w8r8(spi, 0x00); + spi_w8r8(spi, 0x00); + spi_w8r8(spi, 0x00); +} + +static int adau1977_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct regmap_config config; + + if (!id) + return -EINVAL; + + config = adau1977_regmap_config; + config.val_bits = 8; + config.reg_bits = 16; + config.read_flag_mask = 0x1; + + return adau1977_probe(&spi->dev, + devm_regmap_init_spi(spi, &config), + id->driver_data, adau1977_spi_switch_mode); +} + +static int adau1977_spi_remove(struct spi_device *spi) +{ + snd_soc_unregister_codec(&spi->dev); + return 0; +} + +static const struct spi_device_id adau1977_spi_ids[] = { + { "adau1977", ADAU1977 }, + { "adau1978", ADAU1978 }, + { "adau1979", ADAU1978 }, + { } +}; +MODULE_DEVICE_TABLE(spi, adau1977_spi_ids); + +static struct spi_driver adau1977_spi_driver = { + .driver = { + .name = "adau1977", + .owner = THIS_MODULE, + }, + .probe = adau1977_spi_probe, + .remove = adau1977_spi_remove, + .id_table = adau1977_spi_ids, +}; +module_spi_driver(adau1977_spi_driver); + +MODULE_DESCRIPTION("ASoC ADAU1977/ADAU1978/ADAU1979 driver"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c new file mode 100644 index 000000000000..fd55da7cb9d4 --- /dev/null +++ b/sound/soc/codecs/adau1977.c @@ -0,0 +1,1018 @@ +/* + * ADAU1977/ADAU1978/ADAU1979 driver + * + * Copyright 2014 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "adau1977.h" + +#define ADAU1977_REG_POWER 0x00 +#define ADAU1977_REG_PLL 0x01 +#define ADAU1977_REG_BOOST 0x02 +#define ADAU1977_REG_MICBIAS 0x03 +#define ADAU1977_REG_BLOCK_POWER_SAI 0x04 +#define ADAU1977_REG_SAI_CTRL0 0x05 +#define ADAU1977_REG_SAI_CTRL1 0x06 +#define ADAU1977_REG_CMAP12 0x07 +#define ADAU1977_REG_CMAP34 0x08 +#define ADAU1977_REG_SAI_OVERTEMP 0x09 +#define ADAU1977_REG_POST_ADC_GAIN(x) (0x0a + (x)) +#define ADAU1977_REG_MISC_CONTROL 0x0e +#define ADAU1977_REG_DIAG_CONTROL 0x10 +#define ADAU1977_REG_STATUS(x) (0x11 + (x)) +#define ADAU1977_REG_DIAG_IRQ1 0x15 +#define ADAU1977_REG_DIAG_IRQ2 0x16 +#define ADAU1977_REG_ADJUST1 0x17 +#define ADAU1977_REG_ADJUST2 0x18 +#define ADAU1977_REG_ADC_CLIP 0x19 +#define ADAU1977_REG_DC_HPF_CAL 0x1a + +#define ADAU1977_POWER_RESET BIT(7) +#define ADAU1977_POWER_PWUP BIT(0) + +#define ADAU1977_PLL_CLK_S BIT(4) +#define ADAU1977_PLL_MCS_MASK 0x7 + +#define ADAU1977_MICBIAS_MB_VOLTS_MASK 0xf0 +#define ADAU1977_MICBIAS_MB_VOLTS_OFFSET 4 + +#define ADAU1977_BLOCK_POWER_SAI_LR_POL BIT(7) +#define ADAU1977_BLOCK_POWER_SAI_BCLK_EDGE BIT(6) +#define ADAU1977_BLOCK_POWER_SAI_LDO_EN BIT(5) + +#define ADAU1977_SAI_CTRL0_FMT_MASK (0x3 << 6) +#define ADAU1977_SAI_CTRL0_FMT_I2S (0x0 << 6) +#define ADAU1977_SAI_CTRL0_FMT_LJ (0x1 << 6) +#define ADAU1977_SAI_CTRL0_FMT_RJ_24BIT (0x2 << 6) +#define ADAU1977_SAI_CTRL0_FMT_RJ_16BIT (0x3 << 6) + +#define ADAU1977_SAI_CTRL0_SAI_MASK (0x7 << 3) +#define ADAU1977_SAI_CTRL0_SAI_I2S (0x0 << 3) +#define ADAU1977_SAI_CTRL0_SAI_TDM_2 (0x1 << 3) +#define ADAU1977_SAI_CTRL0_SAI_TDM_4 (0x2 << 3) +#define ADAU1977_SAI_CTRL0_SAI_TDM_8 (0x3 << 3) +#define ADAU1977_SAI_CTRL0_SAI_TDM_16 (0x4 << 3) + +#define ADAU1977_SAI_CTRL0_FS_MASK (0x7) +#define ADAU1977_SAI_CTRL0_FS_8000_12000 (0x0) +#define ADAU1977_SAI_CTRL0_FS_16000_24000 (0x1) +#define ADAU1977_SAI_CTRL0_FS_32000_48000 (0x2) +#define ADAU1977_SAI_CTRL0_FS_64000_96000 (0x3) +#define ADAU1977_SAI_CTRL0_FS_128000_192000 (0x4) + +#define ADAU1977_SAI_CTRL1_SLOT_WIDTH_MASK (0x3 << 5) +#define ADAU1977_SAI_CTRL1_SLOT_WIDTH_32 (0x0 << 5) +#define ADAU1977_SAI_CTRL1_SLOT_WIDTH_24 (0x1 << 5) +#define ADAU1977_SAI_CTRL1_SLOT_WIDTH_16 (0x2 << 5) +#define ADAU1977_SAI_CTRL1_DATA_WIDTH_MASK (0x1 << 4) +#define ADAU1977_SAI_CTRL1_DATA_WIDTH_16BIT (0x1 << 4) +#define ADAU1977_SAI_CTRL1_DATA_WIDTH_24BIT (0x0 << 4) +#define ADAU1977_SAI_CTRL1_LRCLK_PULSE BIT(3) +#define ADAU1977_SAI_CTRL1_MSB BIT(2) +#define ADAU1977_SAI_CTRL1_BCLKRATE_16 (0x1 << 1) +#define ADAU1977_SAI_CTRL1_BCLKRATE_32 (0x0 << 1) +#define ADAU1977_SAI_CTRL1_BCLKRATE_MASK (0x1 << 1) +#define ADAU1977_SAI_CTRL1_MASTER BIT(0) + +#define ADAU1977_SAI_OVERTEMP_DRV_C(x) BIT(4 + (x)) +#define ADAU1977_SAI_OVERTEMP_DRV_HIZ BIT(3) + +#define ADAU1977_MISC_CONTROL_SUM_MODE_MASK (0x3 << 6) +#define ADAU1977_MISC_CONTROL_SUM_MODE_1CH (0x2 << 6) +#define ADAU1977_MISC_CONTROL_SUM_MODE_2CH (0x1 << 6) +#define ADAU1977_MISC_CONTROL_SUM_MODE_4CH (0x0 << 6) +#define ADAU1977_MISC_CONTROL_MMUTE BIT(4) +#define ADAU1977_MISC_CONTROL_DC_CAL BIT(0) + +#define ADAU1977_CHAN_MAP_SECOND_SLOT_OFFSET 4 +#define ADAU1977_CHAN_MAP_FIRST_SLOT_OFFSET 0 + +struct adau1977 { + struct regmap *regmap; + bool right_j; + unsigned int sysclk; + enum adau1977_sysclk_src sysclk_src; + struct gpio_desc *reset_gpio; + enum adau1977_type type; + + struct regulator *avdd_reg; + struct regulator *dvdd_reg; + + struct snd_pcm_hw_constraint_list constraints; + + struct device *dev; + void (*switch_mode)(struct device *dev); + + unsigned int max_master_fs; + unsigned int slot_width; + bool enabled; + bool master; +}; + +static const struct reg_default adau1977_reg_defaults[] = { + { 0x00, 0x00 }, + { 0x01, 0x41 }, + { 0x02, 0x4a }, + { 0x03, 0x7d }, + { 0x04, 0x3d }, + { 0x05, 0x02 }, + { 0x06, 0x00 }, + { 0x07, 0x10 }, + { 0x08, 0x32 }, + { 0x09, 0xf0 }, + { 0x0a, 0xa0 }, + { 0x0b, 0xa0 }, + { 0x0c, 0xa0 }, + { 0x0d, 0xa0 }, + { 0x0e, 0x02 }, + { 0x10, 0x0f }, + { 0x15, 0x20 }, + { 0x16, 0x00 }, + { 0x17, 0x00 }, + { 0x18, 0x00 }, + { 0x1a, 0x00 }, +}; + +static const DECLARE_TLV_DB_MINMAX_MUTE(adau1977_adc_gain, -3562, 6000); + +static const struct snd_soc_dapm_widget adau1977_micbias_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("MICBIAS", ADAU1977_REG_MICBIAS, + 3, 0, NULL, 0) +}; + +static const struct snd_soc_dapm_widget adau1977_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("Vref", ADAU1977_REG_BLOCK_POWER_SAI, + 4, 0, NULL, 0), + + SND_SOC_DAPM_ADC("ADC1", "Capture", ADAU1977_REG_BLOCK_POWER_SAI, 0, 0), + SND_SOC_DAPM_ADC("ADC2", "Capture", ADAU1977_REG_BLOCK_POWER_SAI, 1, 0), + SND_SOC_DAPM_ADC("ADC3", "Capture", ADAU1977_REG_BLOCK_POWER_SAI, 2, 0), + SND_SOC_DAPM_ADC("ADC4", "Capture", ADAU1977_REG_BLOCK_POWER_SAI, 3, 0), + + SND_SOC_DAPM_INPUT("AIN1"), + SND_SOC_DAPM_INPUT("AIN2"), + SND_SOC_DAPM_INPUT("AIN3"), + SND_SOC_DAPM_INPUT("AIN4"), + + SND_SOC_DAPM_OUTPUT("VREF"), +}; + +static const struct snd_soc_dapm_route adau1977_dapm_routes[] = { + { "ADC1", NULL, "AIN1" }, + { "ADC2", NULL, "AIN2" }, + { "ADC3", NULL, "AIN3" }, + { "ADC4", NULL, "AIN4" }, + + { "ADC1", NULL, "Vref" }, + { "ADC2", NULL, "Vref" }, + { "ADC3", NULL, "Vref" }, + { "ADC4", NULL, "Vref" }, + + { "VREF", NULL, "Vref" }, +}; + +#define ADAU1977_VOLUME(x) \ + SOC_SINGLE_TLV("ADC" #x " Capture Volume", \ + ADAU1977_REG_POST_ADC_GAIN((x) - 1), \ + 0, 255, 1, adau1977_adc_gain) + +#define ADAU1977_HPF_SWITCH(x) \ + SOC_SINGLE("ADC" #x " Highpass-Filter Capture Switch", \ + ADAU1977_REG_DC_HPF_CAL, (x) - 1, 1, 0) + +#define ADAU1977_DC_SUB_SWITCH(x) \ + SOC_SINGLE("ADC" #x " DC Substraction Capture Switch", \ + ADAU1977_REG_DC_HPF_CAL, (x) + 3, 1, 0) + +static const struct snd_kcontrol_new adau1977_snd_controls[] = { + ADAU1977_VOLUME(1), + ADAU1977_VOLUME(2), + ADAU1977_VOLUME(3), + ADAU1977_VOLUME(4), + + ADAU1977_HPF_SWITCH(1), + ADAU1977_HPF_SWITCH(2), + ADAU1977_HPF_SWITCH(3), + ADAU1977_HPF_SWITCH(4), + + ADAU1977_DC_SUB_SWITCH(1), + ADAU1977_DC_SUB_SWITCH(2), + ADAU1977_DC_SUB_SWITCH(3), + ADAU1977_DC_SUB_SWITCH(4), +}; + +static int adau1977_reset(struct adau1977 *adau1977) +{ + int ret; + + /* + * The reset bit is obviously volatile, but we need to be able to cache + * the other bits in the register, so we can't just mark the whole + * register as volatile. Since this is the only place where we'll ever + * touch the reset bit just bypass the cache for this operation. + */ + regcache_cache_bypass(adau1977->regmap, true); + ret = regmap_write(adau1977->regmap, ADAU1977_REG_POWER, + ADAU1977_POWER_RESET); + regcache_cache_bypass(adau1977->regmap, false); + if (ret) + return ret; + + return ret; +} + +/* + * Returns the appropriate setting for ths FS field in the CTRL0 register + * depending on the rate. + */ +static int adau1977_lookup_fs(unsigned int rate) +{ + if (rate >= 8000 && rate <= 12000) + return ADAU1977_SAI_CTRL0_FS_8000_12000; + else if (rate >= 16000 && rate <= 24000) + return ADAU1977_SAI_CTRL0_FS_16000_24000; + else if (rate >= 32000 && rate <= 48000) + return ADAU1977_SAI_CTRL0_FS_32000_48000; + else if (rate >= 64000 && rate <= 96000) + return ADAU1977_SAI_CTRL0_FS_64000_96000; + else if (rate >= 128000 && rate <= 192000) + return ADAU1977_SAI_CTRL0_FS_128000_192000; + else + return -EINVAL; +} + +static int adau1977_lookup_mcs(struct adau1977 *adau1977, unsigned int rate, + unsigned int fs) +{ + unsigned int mcs; + + /* + * rate = sysclk / (512 * mcs_lut[mcs]) * 2**fs + * => mcs_lut[mcs] = sysclk / (512 * rate) * 2**fs + * => mcs_lut[mcs] = sysclk / ((512 / 2**fs) * rate) + */ + + rate *= 512 >> fs; + + if (adau1977->sysclk % rate != 0) + return -EINVAL; + + mcs = adau1977->sysclk / rate; + + /* The factors configured by MCS are 1, 2, 3, 4, 6 */ + if (mcs < 1 || mcs > 6 || mcs == 5) + return -EINVAL; + + mcs = mcs - 1; + if (mcs == 5) + mcs = 4; + + return mcs; +} + +static int adau1977_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec); + unsigned int rate = params_rate(params); + unsigned int slot_width; + unsigned int ctrl0, ctrl0_mask; + unsigned int ctrl1; + int mcs, fs; + int ret; + + fs = adau1977_lookup_fs(rate); + if (fs < 0) + return fs; + + if (adau1977->sysclk_src == ADAU1977_SYSCLK_SRC_MCLK) { + mcs = adau1977_lookup_mcs(adau1977, rate, fs); + if (mcs < 0) + return mcs; + } else { + mcs = 0; + } + + ctrl0_mask = ADAU1977_SAI_CTRL0_FS_MASK; + ctrl0 = fs; + + if (adau1977->right_j) { + switch (params_width(params)) { + case 16: + ctrl0 |= ADAU1977_SAI_CTRL0_FMT_RJ_16BIT; + break; + case 24: + ctrl0 |= ADAU1977_SAI_CTRL0_FMT_RJ_24BIT; + break; + default: + return -EINVAL; + } + ctrl0_mask |= ADAU1977_SAI_CTRL0_FMT_MASK; + } + + if (adau1977->master) { + switch (params_width(params)) { + case 16: + ctrl1 = ADAU1977_SAI_CTRL1_DATA_WIDTH_16BIT; + slot_width = 16; + break; + case 24: + case 32: + ctrl1 = ADAU1977_SAI_CTRL1_DATA_WIDTH_24BIT; + slot_width = 32; + break; + default: + return -EINVAL; + } + + /* In TDM mode there is a fixed slot width */ + if (adau1977->slot_width) + slot_width = adau1977->slot_width; + + if (slot_width == 16) + ctrl1 |= ADAU1977_SAI_CTRL1_BCLKRATE_16; + else + ctrl1 |= ADAU1977_SAI_CTRL1_BCLKRATE_32; + + ret = regmap_update_bits(adau1977->regmap, + ADAU1977_REG_SAI_CTRL1, + ADAU1977_SAI_CTRL1_DATA_WIDTH_MASK | + ADAU1977_SAI_CTRL1_BCLKRATE_MASK, + ctrl1); + if (ret < 0) + return ret; + } + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL0, + ctrl0_mask, ctrl0); + if (ret < 0) + return ret; + + return regmap_update_bits(adau1977->regmap, ADAU1977_REG_PLL, + ADAU1977_PLL_MCS_MASK, mcs); +} + +static int adau1977_power_disable(struct adau1977 *adau1977) +{ + int ret = 0; + + if (!adau1977->enabled) + return 0; + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_POWER, + ADAU1977_POWER_PWUP, 0); + if (ret) + return ret; + + regcache_mark_dirty(adau1977->regmap); + + if (adau1977->reset_gpio) + gpiod_set_value_cansleep(adau1977->reset_gpio, 0); + + regcache_cache_only(adau1977->regmap, true); + + regulator_disable(adau1977->avdd_reg); + if (adau1977->dvdd_reg) + regulator_disable(adau1977->dvdd_reg); + + adau1977->enabled = false; + + return 0; +} + +static int adau1977_power_enable(struct adau1977 *adau1977) +{ + unsigned int val; + int ret = 0; + + if (adau1977->enabled) + return 0; + + ret = regulator_enable(adau1977->avdd_reg); + if (ret) + return ret; + + if (adau1977->dvdd_reg) { + ret = regulator_enable(adau1977->dvdd_reg); + if (ret) + goto err_disable_avdd; + } + + if (adau1977->reset_gpio) + gpiod_set_value_cansleep(adau1977->reset_gpio, 1); + + regcache_cache_only(adau1977->regmap, false); + + if (adau1977->switch_mode) + adau1977->switch_mode(adau1977->dev); + + ret = adau1977_reset(adau1977); + if (ret) + goto err_disable_dvdd; + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_POWER, + ADAU1977_POWER_PWUP, ADAU1977_POWER_PWUP); + if (ret) + goto err_disable_dvdd; + + ret = regcache_sync(adau1977->regmap); + if (ret) + goto err_disable_dvdd; + + /* + * The PLL register is not affected by the software reset. It is + * possible that the value of the register was changed to the + * default value while we were in cache only mode. In this case + * regcache_sync will skip over it and we have to manually sync + * it. + */ + ret = regmap_read(adau1977->regmap, ADAU1977_REG_PLL, &val); + if (ret) + goto err_disable_dvdd; + + if (val == 0x41) { + regcache_cache_bypass(adau1977->regmap, true); + ret = regmap_write(adau1977->regmap, ADAU1977_REG_PLL, + 0x41); + if (ret) + goto err_disable_dvdd; + regcache_cache_bypass(adau1977->regmap, false); + } + + adau1977->enabled = true; + + return ret; + +err_disable_dvdd: + if (adau1977->dvdd_reg) + regulator_disable(adau1977->dvdd_reg); +err_disable_avdd: + regulator_disable(adau1977->avdd_reg); + return ret; +} + +static int adau1977_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + ret = adau1977_power_enable(adau1977); + break; + case SND_SOC_BIAS_OFF: + ret = adau1977_power_disable(adau1977); + break; + } + + if (ret) + return ret; + + codec->dapm.bias_level = level; + + return 0; +} + +static int adau1977_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int width) +{ + struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec); + unsigned int ctrl0, ctrl1, drv; + unsigned int slot[4]; + unsigned int i; + int ret; + + if (slots == 0) { + /* 0 = No fixed slot width */ + adau1977->slot_width = 0; + adau1977->max_master_fs = 192000; + return regmap_update_bits(adau1977->regmap, + ADAU1977_REG_SAI_CTRL0, ADAU1977_SAI_CTRL0_SAI_MASK, + ADAU1977_SAI_CTRL0_SAI_I2S); + } + + if (rx_mask == 0 || tx_mask != 0) + return -EINVAL; + + drv = 0; + for (i = 0; i < 4; i++) { + slot[i] = __ffs(rx_mask); + drv |= ADAU1977_SAI_OVERTEMP_DRV_C(i); + rx_mask &= ~(1 << slot[i]); + if (slot[i] >= slots) + return -EINVAL; + if (rx_mask == 0) + break; + } + + if (rx_mask != 0) + return -EINVAL; + + switch (width) { + case 16: + ctrl1 = ADAU1977_SAI_CTRL1_SLOT_WIDTH_16; + break; + case 24: + /* We can only generate 16 bit or 32 bit wide slots */ + if (adau1977->master) + return -EINVAL; + ctrl1 = ADAU1977_SAI_CTRL1_SLOT_WIDTH_24; + break; + case 32: + ctrl1 = ADAU1977_SAI_CTRL1_SLOT_WIDTH_32; + break; + default: + return -EINVAL; + } + + switch (slots) { + case 2: + ctrl0 = ADAU1977_SAI_CTRL0_SAI_TDM_2; + break; + case 4: + ctrl0 = ADAU1977_SAI_CTRL0_SAI_TDM_4; + break; + case 8: + ctrl0 = ADAU1977_SAI_CTRL0_SAI_TDM_8; + break; + case 16: + ctrl0 = ADAU1977_SAI_CTRL0_SAI_TDM_16; + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_OVERTEMP, + ADAU1977_SAI_OVERTEMP_DRV_C(0) | + ADAU1977_SAI_OVERTEMP_DRV_C(1) | + ADAU1977_SAI_OVERTEMP_DRV_C(2) | + ADAU1977_SAI_OVERTEMP_DRV_C(3), drv); + if (ret) + return ret; + + ret = regmap_write(adau1977->regmap, ADAU1977_REG_CMAP12, + (slot[1] << ADAU1977_CHAN_MAP_SECOND_SLOT_OFFSET) | + (slot[0] << ADAU1977_CHAN_MAP_FIRST_SLOT_OFFSET)); + if (ret) + return ret; + + ret = regmap_write(adau1977->regmap, ADAU1977_REG_CMAP34, + (slot[3] << ADAU1977_CHAN_MAP_SECOND_SLOT_OFFSET) | + (slot[2] << ADAU1977_CHAN_MAP_FIRST_SLOT_OFFSET)); + if (ret) + return ret; + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL0, + ADAU1977_SAI_CTRL0_SAI_MASK, ctrl0); + if (ret) + return ret; + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL1, + ADAU1977_SAI_CTRL1_SLOT_WIDTH_MASK, ctrl1); + if (ret) + return ret; + + adau1977->slot_width = width; + + /* In master mode the maximum bitclock is 24.576 MHz */ + adau1977->max_master_fs = min(192000, 24576000 / width / slots); + + return 0; +} + +static int adau1977_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec); + unsigned int val; + + if (mute) + val = ADAU1977_MISC_CONTROL_MMUTE; + else + val = 0; + + return regmap_update_bits(adau1977->regmap, ADAU1977_REG_MISC_CONTROL, + ADAU1977_MISC_CONTROL_MMUTE, val); +} + +static int adau1977_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec); + unsigned int ctrl0 = 0, ctrl1 = 0, block_power = 0; + bool invert_lrclk; + int ret; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + adau1977->master = false; + break; + case SND_SOC_DAIFMT_CBM_CFM: + ctrl1 |= ADAU1977_SAI_CTRL1_MASTER; + adau1977->master = true; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + invert_lrclk = false; + break; + case SND_SOC_DAIFMT_IB_NF: + block_power |= ADAU1977_BLOCK_POWER_SAI_BCLK_EDGE; + invert_lrclk = false; + break; + case SND_SOC_DAIFMT_NB_IF: + invert_lrclk = true; + break; + case SND_SOC_DAIFMT_IB_IF: + block_power |= ADAU1977_BLOCK_POWER_SAI_BCLK_EDGE; + invert_lrclk = true; + break; + default: + return -EINVAL; + } + + adau1977->right_j = false; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ctrl0 |= ADAU1977_SAI_CTRL0_FMT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + ctrl0 |= ADAU1977_SAI_CTRL0_FMT_LJ; + invert_lrclk = !invert_lrclk; + break; + case SND_SOC_DAIFMT_RIGHT_J: + ctrl0 |= ADAU1977_SAI_CTRL0_FMT_RJ_24BIT; + adau1977->right_j = true; + invert_lrclk = !invert_lrclk; + break; + case SND_SOC_DAIFMT_DSP_A: + ctrl1 |= ADAU1977_SAI_CTRL1_LRCLK_PULSE; + ctrl0 |= ADAU1977_SAI_CTRL0_FMT_I2S; + invert_lrclk = false; + break; + case SND_SOC_DAIFMT_DSP_B: + ctrl1 |= ADAU1977_SAI_CTRL1_LRCLK_PULSE; + ctrl0 |= ADAU1977_SAI_CTRL0_FMT_LJ; + invert_lrclk = false; + break; + default: + return -EINVAL; + } + + if (invert_lrclk) + block_power |= ADAU1977_BLOCK_POWER_SAI_LR_POL; + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_BLOCK_POWER_SAI, + ADAU1977_BLOCK_POWER_SAI_LR_POL | + ADAU1977_BLOCK_POWER_SAI_BCLK_EDGE, block_power); + if (ret) + return ret; + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL0, + ADAU1977_SAI_CTRL0_FMT_MASK, + ctrl0); + if (ret) + return ret; + + return regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL1, + ADAU1977_SAI_CTRL1_MASTER | ADAU1977_SAI_CTRL1_LRCLK_PULSE, + ctrl1); +} + +static int adau1977_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec); + u64 formats = 0; + + if (adau1977->slot_width == 16) + formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE; + else if (adau1977->right_j || adau1977->slot_width == 24) + formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE; + + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &adau1977->constraints); + + if (adau1977->master) + snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, 8000, adau1977->max_master_fs); + + if (formats != 0) + snd_pcm_hw_constraint_mask64(substream->runtime, + SNDRV_PCM_HW_PARAM_FORMAT, formats); + + return 0; +} + +static int adau1977_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec); + unsigned int val; + + if (tristate) + val = ADAU1977_SAI_OVERTEMP_DRV_HIZ; + else + val = 0; + + return regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_OVERTEMP, + ADAU1977_SAI_OVERTEMP_DRV_HIZ, val); +} + +static const struct snd_soc_dai_ops adau1977_dai_ops = { + .startup = adau1977_startup, + .hw_params = adau1977_hw_params, + .mute_stream = adau1977_mute, + .set_fmt = adau1977_set_dai_fmt, + .set_tdm_slot = adau1977_set_tdm_slot, + .set_tristate = adau1977_set_tristate, +}; + +static struct snd_soc_dai_driver adau1977_dai = { + .name = "adau1977-hifi", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .sig_bits = 24, + }, + .ops = &adau1977_dai_ops, +}; + +static const unsigned int adau1977_rates[] = { + 8000, 16000, 32000, 64000, 128000, + 11025, 22050, 44100, 88200, 172400, + 12000, 24000, 48000, 96000, 192000, +}; + +#define ADAU1977_RATE_CONSTRAINT_MASK_32000 0x001f +#define ADAU1977_RATE_CONSTRAINT_MASK_44100 0x03e0 +#define ADAU1977_RATE_CONSTRAINT_MASK_48000 0x7c00 +/* All rates >= 32000 */ +#define ADAU1977_RATE_CONSTRAINT_MASK_LRCLK 0x739c + +static bool adau1977_check_sysclk(unsigned int mclk, unsigned int base_freq) +{ + unsigned int mcs; + + if (mclk % (base_freq * 128) != 0) + return false; + + mcs = mclk / (128 * base_freq); + if (mcs < 1 || mcs > 6 || mcs == 5) + return false; + + return true; +} + +static int adau1977_set_sysclk(struct snd_soc_codec *codec, + int clk_id, int source, unsigned int freq, int dir) +{ + struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec); + unsigned int mask = 0; + unsigned int clk_src; + unsigned int ret; + + if (dir != SND_SOC_CLOCK_IN) + return -EINVAL; + + if (clk_id != ADAU1977_SYSCLK) + return -EINVAL; + + switch (source) { + case ADAU1977_SYSCLK_SRC_MCLK: + clk_src = 0; + break; + case ADAU1977_SYSCLK_SRC_LRCLK: + clk_src = ADAU1977_PLL_CLK_S; + break; + default: + return -EINVAL; + } + + if (freq != 0 && source == ADAU1977_SYSCLK_SRC_MCLK) { + if (freq < 4000000 || freq > 36864000) + return -EINVAL; + + if (adau1977_check_sysclk(freq, 32000)) + mask |= ADAU1977_RATE_CONSTRAINT_MASK_32000; + if (adau1977_check_sysclk(freq, 44100)) + mask |= ADAU1977_RATE_CONSTRAINT_MASK_44100; + if (adau1977_check_sysclk(freq, 48000)) + mask |= ADAU1977_RATE_CONSTRAINT_MASK_48000; + + if (mask == 0) + return -EINVAL; + } else if (source == ADAU1977_SYSCLK_SRC_LRCLK) { + mask = ADAU1977_RATE_CONSTRAINT_MASK_LRCLK; + } + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_PLL, + ADAU1977_PLL_CLK_S, clk_src); + if (ret) + return ret; + + adau1977->constraints.mask = mask; + adau1977->sysclk_src = source; + adau1977->sysclk = freq; + + return 0; +} + +static int adau1977_codec_probe(struct snd_soc_codec *codec) +{ + struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec); + int ret; + + switch (adau1977->type) { + case ADAU1977: + ret = snd_soc_dapm_new_controls(&codec->dapm, + adau1977_micbias_dapm_widgets, + ARRAY_SIZE(adau1977_micbias_dapm_widgets)); + if (ret < 0) + return ret; + break; + default: + break; + } + + return 0; +} + +static struct snd_soc_codec_driver adau1977_codec_driver = { + .probe = adau1977_codec_probe, + .set_bias_level = adau1977_set_bias_level, + .set_sysclk = adau1977_set_sysclk, + .idle_bias_off = true, + + .controls = adau1977_snd_controls, + .num_controls = ARRAY_SIZE(adau1977_snd_controls), + .dapm_widgets = adau1977_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adau1977_dapm_widgets), + .dapm_routes = adau1977_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(adau1977_dapm_routes), +}; + +static int adau1977_setup_micbias(struct adau1977 *adau1977) +{ + struct adau1977_platform_data *pdata = adau1977->dev->platform_data; + unsigned int micbias; + + if (pdata) { + micbias = pdata->micbias; + if (micbias > ADAU1977_MICBIAS_9V0) + return -EINVAL; + + } else { + micbias = ADAU1977_MICBIAS_8V5; + } + + return regmap_update_bits(adau1977->regmap, ADAU1977_REG_MICBIAS, + ADAU1977_MICBIAS_MB_VOLTS_MASK, + micbias << ADAU1977_MICBIAS_MB_VOLTS_OFFSET); +} + +int adau1977_probe(struct device *dev, struct regmap *regmap, + enum adau1977_type type, void (*switch_mode)(struct device *dev)) +{ + unsigned int power_off_mask; + struct adau1977 *adau1977; + int ret; + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + adau1977 = devm_kzalloc(dev, sizeof(*adau1977), GFP_KERNEL); + if (adau1977 == NULL) + return -ENOMEM; + + adau1977->dev = dev; + adau1977->type = type; + adau1977->regmap = regmap; + adau1977->switch_mode = switch_mode; + adau1977->max_master_fs = 192000; + + adau1977->constraints.list = adau1977_rates; + adau1977->constraints.count = ARRAY_SIZE(adau1977_rates); + + adau1977->avdd_reg = devm_regulator_get(dev, "AVDD"); + if (IS_ERR(adau1977->avdd_reg)) + return PTR_ERR(adau1977->avdd_reg); + + adau1977->dvdd_reg = devm_regulator_get_optional(dev, "DVDD"); + if (IS_ERR(adau1977->dvdd_reg)) { + if (PTR_ERR(adau1977->dvdd_reg) != -ENODEV) + return PTR_ERR(adau1977->dvdd_reg); + adau1977->dvdd_reg = NULL; + } + + adau1977->reset_gpio = devm_gpiod_get(dev, "reset"); + if (IS_ERR(adau1977->reset_gpio)) { + ret = PTR_ERR(adau1977->reset_gpio); + if (ret != -ENOENT && ret != -ENOSYS) + return PTR_ERR(adau1977->reset_gpio); + adau1977->reset_gpio = NULL; + } + + dev_set_drvdata(dev, adau1977); + + if (adau1977->reset_gpio) { + ret = gpiod_direction_output(adau1977->reset_gpio, 0); + if (ret) + return ret; + ndelay(100); + } + + ret = adau1977_power_enable(adau1977); + if (ret) + return ret; + + if (type == ADAU1977) { + ret = adau1977_setup_micbias(adau1977); + if (ret) + goto err_poweroff; + } + + if (adau1977->dvdd_reg) + power_off_mask = ~0; + else + power_off_mask = ~ADAU1977_BLOCK_POWER_SAI_LDO_EN; + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_BLOCK_POWER_SAI, + power_off_mask, 0x00); + if (ret) + goto err_poweroff; + + ret = adau1977_power_disable(adau1977); + if (ret) + return ret; + + return snd_soc_register_codec(dev, &adau1977_codec_driver, + &adau1977_dai, 1); + +err_poweroff: + adau1977_power_disable(adau1977); + return ret; + +} +EXPORT_SYMBOL_GPL(adau1977_probe); + +static bool adau1977_register_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADAU1977_REG_STATUS(0): + case ADAU1977_REG_STATUS(1): + case ADAU1977_REG_STATUS(2): + case ADAU1977_REG_STATUS(3): + case ADAU1977_REG_ADC_CLIP: + return true; + } + + return false; +} + +const struct regmap_config adau1977_regmap_config = { + .max_register = ADAU1977_REG_DC_HPF_CAL, + .volatile_reg = adau1977_register_volatile, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = adau1977_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(adau1977_reg_defaults), +}; +EXPORT_SYMBOL_GPL(adau1977_regmap_config); + +MODULE_DESCRIPTION("ASoC ADAU1977/ADAU1978/ADAU1979 driver"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adau1977.h b/sound/soc/codecs/adau1977.h new file mode 100644 index 000000000000..95e714345a86 --- /dev/null +++ b/sound/soc/codecs/adau1977.h @@ -0,0 +1,37 @@ +/* + * ADAU1977/ADAU1978/ADAU1979 driver + * + * Copyright 2014 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2. + */ + +#ifndef __SOUND_SOC_CODECS_ADAU1977_H__ +#define __SOUND_SOC_CODECS_ADAU1977_H__ + +#include + +struct device; + +enum adau1977_type { + ADAU1977, + ADAU1978, + ADAU1979, +}; + +int adau1977_probe(struct device *dev, struct regmap *regmap, + enum adau1977_type type, void (*switch_mode)(struct device *dev)); + +extern const struct regmap_config adau1977_regmap_config; + +enum adau1977_clk_id { + ADAU1977_SYSCLK, +}; + +enum adau1977_sysclk_src { + ADAU1977_SYSCLK_SRC_MCLK, + ADAU1977_SYSCLK_SRC_LRCLK, +}; + +#endif -- cgit v1.2.3 From 9a8d38db030f016bee45b927af02d9b46398ed46 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Feb 2014 08:11:42 +0100 Subject: ASoC: Rename soc_enum.max field with items The name "max" in struct soc_enum is rather confusing since it actually takes the number of items. With "max", one might try to assign (nitems - 1) value. Rename the field to a more appropriate one, "items", which is also used in struct snd_ctl_elem_info, too. This patch also rewrites some code like "if (x > e->nitems - 1)" with "if (x >= e->nitems)". Not only the latter improves the readability, it also fixes a potential bug when e->items is zero. Signed-off-by: Takashi Iwai Acked-by: Liam Girdwood Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 24 ++++++++++++------------ sound/soc/codecs/max98088.c | 2 +- sound/soc/codecs/max98095.c | 4 ++-- sound/soc/codecs/twl4030.c | 4 ++-- sound/soc/codecs/wm8904.c | 4 ++-- sound/soc/codecs/wm8958-dsp2.c | 8 ++++---- sound/soc/codecs/wm8994.c | 4 ++-- sound/soc/codecs/wm8996.c | 2 +- sound/soc/omap/ams-delta.c | 2 +- sound/soc/soc-core.c | 18 +++++++++--------- sound/soc/soc-dapm.c | 22 +++++++++++----------- 11 files changed, 47 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 9a001472b96a..66de6a70581e 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -162,19 +162,19 @@ .private_value = (unsigned long)&(struct soc_mixer_control) \ {.reg = xreg, .min = xmin, .max = xmax, \ .platform_max = xmax} } -#define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmax, xtexts) \ +#define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xitems, xtexts) \ { .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \ - .max = xmax, .texts = xtexts, \ - .mask = xmax ? roundup_pow_of_two(xmax) - 1 : 0} -#define SOC_ENUM_SINGLE(xreg, xshift, xmax, xtexts) \ - SOC_ENUM_DOUBLE(xreg, xshift, xshift, xmax, xtexts) -#define SOC_ENUM_SINGLE_EXT(xmax, xtexts) \ -{ .max = xmax, .texts = xtexts } -#define SOC_VALUE_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xmax, xtexts, xvalues) \ + .items = xitems, .texts = xtexts, \ + .mask = xitems ? roundup_pow_of_two(xitems) - 1 : 0} +#define SOC_ENUM_SINGLE(xreg, xshift, xitems, xtexts) \ + SOC_ENUM_DOUBLE(xreg, xshift, xshift, xitems, xtexts) +#define SOC_ENUM_SINGLE_EXT(xitems, xtexts) \ +{ .items = xitems, .texts = xtexts } +#define SOC_VALUE_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xitems, xtexts, xvalues) \ { .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \ - .mask = xmask, .max = xmax, .texts = xtexts, .values = xvalues} -#define SOC_VALUE_ENUM_SINGLE(xreg, xshift, xmask, xmax, xtexts, xvalues) \ - SOC_VALUE_ENUM_DOUBLE(xreg, xshift, xshift, xmask, xmax, xtexts, xvalues) + .mask = xmask, .items = xitems, .texts = xtexts, .values = xvalues} +#define SOC_VALUE_ENUM_SINGLE(xreg, xshift, xmask, xnitmes, xtexts, xvalues) \ + SOC_VALUE_ENUM_DOUBLE(xreg, xshift, xshift, xmask, xnitmes, xtexts, xvalues) #define SOC_ENUM(xname, xenum) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\ .info = snd_soc_info_enum_double, \ @@ -1089,7 +1089,7 @@ struct soc_enum { unsigned short reg2; unsigned char shift_l; unsigned char shift_r; - unsigned int max; + unsigned int items; unsigned int mask; const char * const *texts; const unsigned int *values; diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index ee660e2d3df3..bb1ecfc4459b 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -1849,7 +1849,7 @@ static void max98088_handle_eq_pdata(struct snd_soc_codec *codec) /* Now point the soc_enum to .texts array items */ max98088->eq_enum.texts = max98088->eq_texts; - max98088->eq_enum.max = max98088->eq_textcnt; + max98088->eq_enum.items = max98088->eq_textcnt; ret = snd_soc_add_codec_controls(codec, controls, ARRAY_SIZE(controls)); if (ret != 0) diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index 3ba1170ebb53..5bce9cde4a6d 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -1861,7 +1861,7 @@ static void max98095_handle_eq_pdata(struct snd_soc_codec *codec) /* Now point the soc_enum to .texts array items */ max98095->eq_enum.texts = max98095->eq_texts; - max98095->eq_enum.max = max98095->eq_textcnt; + max98095->eq_enum.items = max98095->eq_textcnt; ret = snd_soc_add_codec_controls(codec, controls, ARRAY_SIZE(controls)); if (ret != 0) @@ -2016,7 +2016,7 @@ static void max98095_handle_bq_pdata(struct snd_soc_codec *codec) /* Now point the soc_enum to .texts array items */ max98095->bq_enum.texts = max98095->bq_texts; - max98095->bq_enum.max = max98095->bq_textcnt; + max98095->bq_enum.items = max98095->bq_textcnt; ret = snd_soc_add_codec_controls(codec, controls, ARRAY_SIZE(controls)); if (ret != 0) diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 00665ada23e2..1eb13d586309 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -975,13 +975,13 @@ static int snd_soc_put_twl4030_opmode_enum_double(struct snd_kcontrol *kcontrol, return -EBUSY; } - if (ucontrol->value.enumerated.item[0] > e->max - 1) + if (ucontrol->value.enumerated.item[0] >= e->items) return -EINVAL; val = ucontrol->value.enumerated.item[0] << e->shift_l; mask = e->mask << e->shift_l; if (e->shift_l != e->shift_r) { - if (ucontrol->value.enumerated.item[1] > e->max - 1) + if (ucontrol->value.enumerated.item[1] >= e->items) return -EINVAL; val |= ucontrol->value.enumerated.item[1] << e->shift_r; mask |= e->mask << e->shift_r; diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 53bbfac6a83a..b2664ec901b9 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -1981,7 +1981,7 @@ static void wm8904_handle_retune_mobile_pdata(struct snd_soc_codec *codec) dev_dbg(codec->dev, "Allocated %d unique ReTune Mobile names\n", wm8904->num_retune_mobile_texts); - wm8904->retune_mobile_enum.max = wm8904->num_retune_mobile_texts; + wm8904->retune_mobile_enum.items = wm8904->num_retune_mobile_texts; wm8904->retune_mobile_enum.texts = wm8904->retune_mobile_texts; ret = snd_soc_add_codec_controls(codec, &control, 1); @@ -2022,7 +2022,7 @@ static void wm8904_handle_pdata(struct snd_soc_codec *codec) for (i = 0; i < pdata->num_drc_cfgs; i++) wm8904->drc_texts[i] = pdata->drc_cfgs[i].name; - wm8904->drc_enum.max = pdata->num_drc_cfgs; + wm8904->drc_enum.items = pdata->num_drc_cfgs; wm8904->drc_enum.texts = wm8904->drc_texts; ret = snd_soc_add_codec_controls(codec, &control, 1); diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c index b7488f190d2b..19743779bf4d 100644 --- a/sound/soc/codecs/wm8958-dsp2.c +++ b/sound/soc/codecs/wm8958-dsp2.c @@ -944,7 +944,7 @@ void wm8958_dsp2_init(struct snd_soc_codec *codec) for (i = 0; i < pdata->num_mbc_cfgs; i++) wm8994->mbc_texts[i] = pdata->mbc_cfgs[i].name; - wm8994->mbc_enum.max = pdata->num_mbc_cfgs; + wm8994->mbc_enum.items = pdata->num_mbc_cfgs; wm8994->mbc_enum.texts = wm8994->mbc_texts; ret = snd_soc_add_codec_controls(wm8994->hubs.codec, @@ -973,7 +973,7 @@ void wm8958_dsp2_init(struct snd_soc_codec *codec) for (i = 0; i < pdata->num_vss_cfgs; i++) wm8994->vss_texts[i] = pdata->vss_cfgs[i].name; - wm8994->vss_enum.max = pdata->num_vss_cfgs; + wm8994->vss_enum.items = pdata->num_vss_cfgs; wm8994->vss_enum.texts = wm8994->vss_texts; ret = snd_soc_add_codec_controls(wm8994->hubs.codec, @@ -1003,7 +1003,7 @@ void wm8958_dsp2_init(struct snd_soc_codec *codec) for (i = 0; i < pdata->num_vss_hpf_cfgs; i++) wm8994->vss_hpf_texts[i] = pdata->vss_hpf_cfgs[i].name; - wm8994->vss_hpf_enum.max = pdata->num_vss_hpf_cfgs; + wm8994->vss_hpf_enum.items = pdata->num_vss_hpf_cfgs; wm8994->vss_hpf_enum.texts = wm8994->vss_hpf_texts; ret = snd_soc_add_codec_controls(wm8994->hubs.codec, @@ -1034,7 +1034,7 @@ void wm8958_dsp2_init(struct snd_soc_codec *codec) for (i = 0; i < pdata->num_enh_eq_cfgs; i++) wm8994->enh_eq_texts[i] = pdata->enh_eq_cfgs[i].name; - wm8994->enh_eq_enum.max = pdata->num_enh_eq_cfgs; + wm8994->enh_eq_enum.items = pdata->num_enh_eq_cfgs; wm8994->enh_eq_enum.texts = wm8994->enh_eq_texts; ret = snd_soc_add_codec_controls(wm8994->hubs.codec, diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index b9be9cbc4603..8253c3c6db0e 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -3237,7 +3237,7 @@ static void wm8994_handle_retune_mobile_pdata(struct wm8994_priv *wm8994) dev_dbg(codec->dev, "Allocated %d unique ReTune Mobile names\n", wm8994->num_retune_mobile_texts); - wm8994->retune_mobile_enum.max = wm8994->num_retune_mobile_texts; + wm8994->retune_mobile_enum.items = wm8994->num_retune_mobile_texts; wm8994->retune_mobile_enum.texts = wm8994->retune_mobile_texts; ret = snd_soc_add_codec_controls(wm8994->hubs.codec, controls, @@ -3293,7 +3293,7 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994) for (i = 0; i < pdata->num_drc_cfgs; i++) wm8994->drc_texts[i] = pdata->drc_cfgs[i].name; - wm8994->drc_enum.max = pdata->num_drc_cfgs; + wm8994->drc_enum.items = pdata->num_drc_cfgs; wm8994->drc_enum.texts = wm8994->drc_texts; ret = snd_soc_add_codec_controls(wm8994->hubs.codec, controls, diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index 1a7655b0aa22..ea6b587f65c1 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -2595,7 +2595,7 @@ static void wm8996_retune_mobile_pdata(struct snd_soc_codec *codec) dev_dbg(codec->dev, "Allocated %d unique ReTune Mobile names\n", wm8996->num_retune_mobile_texts); - wm8996->retune_mobile_enum.max = wm8996->num_retune_mobile_texts; + wm8996->retune_mobile_enum.items = wm8996->num_retune_mobile_texts; wm8996->retune_mobile_enum.texts = wm8996->retune_mobile_texts; ret = snd_soc_add_codec_controls(codec, controls, ARRAY_SIZE(controls)); diff --git a/sound/soc/omap/ams-delta.c b/sound/soc/omap/ams-delta.c index 629446482a91..2eca91a51f51 100644 --- a/sound/soc/omap/ams-delta.c +++ b/sound/soc/omap/ams-delta.c @@ -103,7 +103,7 @@ static int ams_delta_set_audio_mode(struct snd_kcontrol *kcontrol, if (!codec->hw_write) return -EUNATCH; - if (ucontrol->value.enumerated.item[0] >= control->max) + if (ucontrol->value.enumerated.item[0] >= control->items) return -EINVAL; mutex_lock(&codec->mutex); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index fe1df50805a3..4372efb4e033 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2571,10 +2571,10 @@ int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol, uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = e->shift_l == e->shift_r ? 1 : 2; - uinfo->value.enumerated.items = e->max; + uinfo->value.enumerated.items = e->items; - if (uinfo->value.enumerated.item > e->max - 1) - uinfo->value.enumerated.item = e->max - 1; + if (uinfo->value.enumerated.item >= e->items) + uinfo->value.enumerated.item = e->items - 1; strlcpy(uinfo->value.enumerated.name, e->texts[uinfo->value.enumerated.item], sizeof(uinfo->value.enumerated.name)); @@ -2626,12 +2626,12 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, unsigned int val; unsigned int mask; - if (ucontrol->value.enumerated.item[0] > e->max - 1) + if (ucontrol->value.enumerated.item[0] >= e->items) return -EINVAL; val = ucontrol->value.enumerated.item[0] << e->shift_l; mask = e->mask << e->shift_l; if (e->shift_l != e->shift_r) { - if (ucontrol->value.enumerated.item[1] > e->max - 1) + if (ucontrol->value.enumerated.item[1] >= e->items) return -EINVAL; val |= ucontrol->value.enumerated.item[1] << e->shift_r; mask |= e->mask << e->shift_r; @@ -2662,14 +2662,14 @@ int snd_soc_get_value_enum_double(struct snd_kcontrol *kcontrol, reg_val = snd_soc_read(codec, e->reg); val = (reg_val >> e->shift_l) & e->mask; - for (mux = 0; mux < e->max; mux++) { + for (mux = 0; mux < e->items; mux++) { if (val == e->values[mux]) break; } ucontrol->value.enumerated.item[0] = mux; if (e->shift_l != e->shift_r) { val = (reg_val >> e->shift_r) & e->mask; - for (mux = 0; mux < e->max; mux++) { + for (mux = 0; mux < e->items; mux++) { if (val == e->values[mux]) break; } @@ -2700,12 +2700,12 @@ int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol, unsigned int val; unsigned int mask; - if (ucontrol->value.enumerated.item[0] > e->max - 1) + if (ucontrol->value.enumerated.item[0] >= e->items) return -EINVAL; val = e->values[ucontrol->value.enumerated.item[0]] << e->shift_l; mask = e->mask << e->shift_l; if (e->shift_l != e->shift_r) { - if (ucontrol->value.enumerated.item[1] > e->max - 1) + if (ucontrol->value.enumerated.item[1] >= e->items) return -EINVAL; val |= e->values[ucontrol->value.enumerated.item[1]] << e->shift_r; mask |= e->mask << e->shift_r; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index dc8ff13187f7..2026a64a0afb 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -535,7 +535,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, soc_widget_read(w, e->reg, &val); item = (val >> e->shift_l) & e->mask; - if (item < e->max && !strcmp(p->name, e->texts[item])) + if (item < e->items && !strcmp(p->name, e->texts[item])) p->connect = 1; else p->connect = 0; @@ -563,12 +563,12 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, soc_widget_read(w, e->reg, &val); val = (val >> e->shift_l) & e->mask; - for (item = 0; item < e->max; item++) { + for (item = 0; item < e->items; item++) { if (val == e->values[item]) break; } - if (item < e->max && !strcmp(p->name, e->texts[item])) + if (item < e->items && !strcmp(p->name, e->texts[item])) p->connect = 1; else p->connect = 0; @@ -616,7 +616,7 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; int i; - for (i = 0; i < e->max; i++) { + for (i = 0; i < e->items; i++) { if (!(strcmp(control_name, e->texts[i]))) { list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &dest->sources); @@ -2967,13 +2967,13 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_update update; int ret = 0; - if (ucontrol->value.enumerated.item[0] > e->max - 1) + if (ucontrol->value.enumerated.item[0] >= e->items) return -EINVAL; mux = ucontrol->value.enumerated.item[0]; val = mux << e->shift_l; mask = e->mask << e->shift_l; if (e->shift_l != e->shift_r) { - if (ucontrol->value.enumerated.item[1] > e->max - 1) + if (ucontrol->value.enumerated.item[1] >= e->items) return -EINVAL; val |= ucontrol->value.enumerated.item[1] << e->shift_r; mask |= e->mask << e->shift_r; @@ -3036,7 +3036,7 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, int change; int ret = 0; - if (ucontrol->value.enumerated.item[0] >= e->max) + if (ucontrol->value.enumerated.item[0] >= e->items) return -EINVAL; mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); @@ -3077,14 +3077,14 @@ int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol, reg_val = snd_soc_read(codec, e->reg); val = (reg_val >> e->shift_l) & e->mask; - for (mux = 0; mux < e->max; mux++) { + for (mux = 0; mux < e->items; mux++) { if (val == e->values[mux]) break; } ucontrol->value.enumerated.item[0] = mux; if (e->shift_l != e->shift_r) { val = (reg_val >> e->shift_r) & e->mask; - for (mux = 0; mux < e->max; mux++) { + for (mux = 0; mux < e->items; mux++) { if (val == e->values[mux]) break; } @@ -3119,13 +3119,13 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_update update; int ret = 0; - if (ucontrol->value.enumerated.item[0] > e->max - 1) + if (ucontrol->value.enumerated.item[0] >= e->items) return -EINVAL; mux = ucontrol->value.enumerated.item[0]; val = e->values[ucontrol->value.enumerated.item[0]] << e->shift_l; mask = e->mask << e->shift_l; if (e->shift_l != e->shift_r) { - if (ucontrol->value.enumerated.item[1] > e->max - 1) + if (ucontrol->value.enumerated.item[1] >= e->items) return -EINVAL; val |= e->values[ucontrol->value.enumerated.item[1]] << e->shift_r; mask |= e->mask << e->shift_r; -- cgit v1.2.3 From 2e7e1993f992b16342b3799de2f9572b2e53d53b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Feb 2014 09:08:51 +0100 Subject: ASoC: Add const to SOC_ENUM_*_DECL() macros Since these macros are supposed to be used for decalring const objects, let's add the const modifier there. The doubled const appearing in usages will be cleaned by later patches. Signed-off-by: Takashi Iwai Acked-by: Liam Girdwood Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 66de6a70581e..ab5f1fed698a 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -272,14 +272,14 @@ * ARRAY_SIZE internally */ #define SOC_ENUM_DOUBLE_DECL(name, xreg, xshift_l, xshift_r, xtexts) \ - struct soc_enum name = SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, \ + const struct soc_enum name = SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, \ ARRAY_SIZE(xtexts), xtexts) #define SOC_ENUM_SINGLE_DECL(name, xreg, xshift, xtexts) \ SOC_ENUM_DOUBLE_DECL(name, xreg, xshift, xshift, xtexts) #define SOC_ENUM_SINGLE_EXT_DECL(name, xtexts) \ - struct soc_enum name = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(xtexts), xtexts) + const struct soc_enum name = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(xtexts), xtexts) #define SOC_VALUE_ENUM_DOUBLE_DECL(name, xreg, xshift_l, xshift_r, xmask, xtexts, xvalues) \ - struct soc_enum name = SOC_VALUE_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, \ + const struct soc_enum name = SOC_VALUE_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, \ ARRAY_SIZE(xtexts), xtexts, xvalues) #define SOC_VALUE_ENUM_SINGLE_DECL(name, xreg, xshift, xmask, xtexts, xvalues) \ SOC_VALUE_ENUM_DOUBLE_DECL(name, xreg, xshift, xshift, xmask, xtexts, xvalues) -- cgit v1.2.3 From f6d5e586b416746664c01ab5d4b19ed5e2d1cbaa Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 18 Feb 2014 15:22:13 +0000 Subject: ASoC: dapm: Add helpers to lock/unlock DAPM mutex Acquiring the DAPM mutex is necessary before using several DAPM functions and dereference is quite ugly. This patch provides a helper function to simplify this. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- include/sound/soc.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 9a001472b96a..1e12b66da2cc 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1188,4 +1188,15 @@ extern struct dentry *snd_soc_debugfs_root; extern const struct dev_pm_ops snd_soc_pm_ops; +/* Helper functions */ +static inline void snd_soc_dapm_mutex_lock(struct snd_soc_dapm_context *dapm) +{ + mutex_lock(&dapm->card->dapm_mutex); +} + +static inline void snd_soc_dapm_mutex_unlock(struct snd_soc_dapm_context *dapm) +{ + mutex_unlock(&dapm->card->dapm_mutex); +} + #endif -- cgit v1.2.3 From 3eb29dfb3d3bd4b600370007b96c3c675fb97aa7 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 18 Feb 2014 15:22:15 +0000 Subject: ASoC: dapm: Add unlocked version of snd_soc_dapm_sync We will often call sync after several functions that require the DAPM mutex to be held. Rather than release and immediately relock the mutex provide an unlocked function for this situation. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 1 + sound/soc/soc-dapm.c | 27 ++++++++++++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 6e89ef6c11c1..3b991766a8f2 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -461,6 +461,7 @@ int snd_soc_dapm_nc_pin_unlocked(struct snd_soc_dapm_context *dapm, int snd_soc_dapm_get_pin_status(struct snd_soc_dapm_context *dapm, const char *pin); int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm); +int snd_soc_dapm_sync_unlocked(struct snd_soc_dapm_context *dapm); int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin); int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 48a4eeb05192..4200f96a1483 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2342,18 +2342,18 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, } /** - * snd_soc_dapm_sync - scan and power dapm paths + * snd_soc_dapm_sync_unlocked - scan and power dapm paths * @dapm: DAPM context * * Walks all dapm audio paths and powers widgets according to their * stream or path usage. * + * Requires external locking. + * * Returns 0 for success. */ -int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) +int snd_soc_dapm_sync_unlocked(struct snd_soc_dapm_context *dapm) { - int ret; - /* * Suppress early reports (eg, jacks syncing their state) to avoid * silly DAPM runs during card startup. @@ -2361,8 +2361,25 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) if (!dapm->card || !dapm->card->instantiated) return 0; + return dapm_power_widgets(dapm->card, SND_SOC_DAPM_STREAM_NOP); +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_sync_unlocked); + +/** + * snd_soc_dapm_sync - scan and power dapm paths + * @dapm: DAPM context + * + * Walks all dapm audio paths and powers widgets according to their + * stream or path usage. + * + * Returns 0 for success. + */ +int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) +{ + int ret; + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - ret = dapm_power_widgets(dapm->card, SND_SOC_DAPM_STREAM_NOP); + ret = snd_soc_dapm_sync_unlocked(dapm); mutex_unlock(&dapm->card->dapm_mutex); return ret; } -- cgit v1.2.3 From 89c6785715592a6b082b3f9f28c27bb14b041c7d Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Fri, 14 Feb 2014 09:34:35 +0800 Subject: ASoC: core: add TDM slot parsing from DT supports For some CPU/CODEC DAI devices the TDM slot infomation maybe needed. This patch adds the slot parsing from DT supports. TDM slot properties: dai-tdm-slot-num : Number of slots in use. dai-tdm-slot-width : Width in bits for each slot. For instance: dai-tdm-slot-num = <2>; dai-tdm-slot-width = <8>; And for each spcified driver, there could be one .of_xlate_tdm_slot_mask() to specify a explicit mapping of the channels and the slots. If it's absent the default snd_soc_of_xlate_tdm_slot_mask() will be used to generating the tx and rx masks. For snd_soc_of_xlate_tdm_slot_mask(), the tx and rx masks will use a 1 bit for an active slot as default, and the default active bits are at the LSB of the masks. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- include/sound/soc-dai.h | 2 ++ include/sound/soc.h | 3 +++ sound/soc/soc-core.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+) (limited to 'include') diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 71f27c403194..d86e0fc41e80 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -142,6 +142,8 @@ struct snd_soc_dai_ops { * Called by soc_card drivers, normally in their hw_params. */ int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt); + int (*of_xlate_tdm_slot_mask)(unsigned int slots, + unsigned int *tx_mask, unsigned int *rx_mask); int (*set_tdm_slot)(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width); diff --git a/include/sound/soc.h b/include/sound/soc.h index 465dc6e0674d..2a878d03c147 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1175,6 +1175,9 @@ int snd_soc_of_parse_card_name(struct snd_soc_card *card, const char *propname); int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, const char *propname); +int snd_soc_of_parse_tdm_slot(struct device_node *np, + unsigned int *slots, + unsigned int *slot_width); int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, const char *propname); unsigned int snd_soc_of_parse_daifmt(struct device_node *np, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 0540cb08e0ea..5b7d3ba87c7a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3608,6 +3608,30 @@ int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) } EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt); +/** + * snd_soc_of_xlate_tdm_slot - generate tx/rx slot mask. + * @slots: Number of slots in use. + * @tx_mask: bitmask representing active TX slots. + * @rx_mask: bitmask representing active RX slots. + * + * Generates the TDM tx and rx slot default masks for DAI. + */ +static int snd_soc_of_xlate_tdm_slot_mask(unsigned int slots, + unsigned int *tx_mask, + unsigned int *rx_mask) +{ + if (*tx_mask || *rx_mask) + return 0; + + if (!slots) + return -EINVAL; + + *tx_mask = (1 << slots) - 1; + *rx_mask = (1 << slots) - 1; + + return 0; +} + /** * snd_soc_dai_set_tdm_slot - configure DAI TDM. * @dai: DAI @@ -3622,6 +3646,12 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt); int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) { + if (dai->driver && dai->driver->ops->of_xlate_tdm_slot_mask) + dai->driver->ops->of_xlate_tdm_slot_mask(slots, + &tx_mask, &rx_mask); + else + snd_soc_of_xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask); + if (dai->driver && dai->driver->ops->set_tdm_slot) return dai->driver->ops->set_tdm_slot(dai, tx_mask, rx_mask, slots, slot_width); @@ -4504,6 +4534,35 @@ int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets); +int snd_soc_of_parse_tdm_slot(struct device_node *np, + unsigned int *slots, + unsigned int *slot_width) +{ + u32 val; + int ret; + + if (of_property_read_bool(np, "dai-tdm-slot-num")) { + ret = of_property_read_u32(np, "dai-tdm-slot-num", &val); + if (ret) + return ret; + + if (slots) + *slots = val; + } + + if (of_property_read_bool(np, "dai-tdm-slot-width")) { + ret = of_property_read_u32(np, "dai-tdm-slot-width", &val); + if (ret) + return ret; + + if (slot_width) + *slot_width = val; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_of_parse_tdm_slot); + int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, const char *propname) { -- cgit v1.2.3 From 6ff62eedce4f7756b092d276165d8e11edab9f28 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Fri, 14 Feb 2014 09:34:36 +0800 Subject: ASoC: simple-card: add slot information parsing supports For some CPU/CODEC DAI devices the slot information maybe needed. This patch adds the slot information parsing for simple-card driver. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/simple-card.txt | 5 +++++ include/sound/simple_card.h | 2 ++ sound/soc/generic/simple-card.c | 18 ++++++++++++++++++ 3 files changed, 25 insertions(+) (limited to 'include') diff --git a/Documentation/devicetree/bindings/sound/simple-card.txt b/Documentation/devicetree/bindings/sound/simple-card.txt index 05273583490c..b30c222f9cd3 100644 --- a/Documentation/devicetree/bindings/sound/simple-card.txt +++ b/Documentation/devicetree/bindings/sound/simple-card.txt @@ -18,6 +18,8 @@ Optional properties: Each entry is a pair of strings, the first being the connection's sink, the second being the connection's source. +- dai-tdm-slot-num : Please refer to tdm-slot.txt. +- dai-tdm-slot-width : Please refer to tdm-slot.txt. Required subnodes: @@ -56,6 +58,9 @@ sound { "Headphone Jack", "HP_OUT", "External Speaker", "LINE_OUT"; + dai-tdm-slot-num = <2>; + dai-tdm-slot-width = <8>; + simple-audio-card,cpu { sound-dai = <&sh_fsi2 0>; }; diff --git a/include/sound/simple_card.h b/include/sound/simple_card.h index e1ac996c8feb..9b0ac77177b6 100644 --- a/include/sound/simple_card.h +++ b/include/sound/simple_card.h @@ -18,6 +18,8 @@ struct asoc_simple_dai { const char *name; unsigned int fmt; unsigned int sysclk; + int slots; + int slot_width; }; struct asoc_simple_card_info { diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 4fe8abc6e216..bdd176ddff07 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -9,11 +9,14 @@ * published by the Free Software Foundation. */ #include +#include #include #include #include #include #include +#include +#include struct simple_card_data { struct snd_soc_card snd_card; @@ -44,6 +47,16 @@ static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai, } } + if (set->slots) { + ret = snd_soc_dai_set_tdm_slot(dai, 0, 0, + set->slots, + set->slot_width); + if (ret && ret != -ENOTSUPP) { + dev_err(dai->dev, "simple-card: set_tdm_slot error\n"); + goto err; + } + } + ret = 0; err: @@ -94,6 +107,11 @@ asoc_simple_card_sub_parse_of(struct device_node *np, if (ret < 0) goto parse_error; + /* parse TDM slot */ + ret = snd_soc_of_parse_tdm_slot(np, &dai->slots, &dai->slot_width); + if (ret) + goto parse_error; + /* * bitclock-inversion, frame-inversion * bitclock-master, frame-master -- cgit v1.2.3 From 8d971691834fd5506cb2d7aef71a3717b7ed597d Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 25 Feb 2014 15:10:39 +0530 Subject: ASoC: samsung: Remove invalid file reference Remove file references rendered invalid due to relocation. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown --- include/linux/platform_data/asoc-s3c.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/platform_data/asoc-s3c.h b/include/linux/platform_data/asoc-s3c.h index 9efc04dd255a..709c6f7e2f8c 100644 --- a/include/linux/platform_data/asoc-s3c.h +++ b/include/linux/platform_data/asoc-s3c.h @@ -1,5 +1,4 @@ -/* arch/arm/plat-samsung/include/plat/audio.h - * +/* * Copyright (c) 2009 Samsung Electronics Co. Ltd * Author: Jaswinder Singh * -- cgit v1.2.3 From c3c125e294fd4276add6f80d2b333ca7c55e935d Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 25 Feb 2014 15:10:40 +0530 Subject: ASoC: s3c24xx: Remove invalid file reference Remove file references rendered invalid due to relocation. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown --- include/linux/platform_data/asoc-s3c24xx_simtec.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/platform_data/asoc-s3c24xx_simtec.h b/include/linux/platform_data/asoc-s3c24xx_simtec.h index 376af5286a3e..d220e54123aa 100644 --- a/include/linux/platform_data/asoc-s3c24xx_simtec.h +++ b/include/linux/platform_data/asoc-s3c24xx_simtec.h @@ -1,5 +1,4 @@ -/* arch/arm/plat-samsung/include/plat/audio-simtec.h - * +/* * Copyright 2008 Simtec Electronics * http://armlinux.simtec.co.uk/ * Ben Dooks -- cgit v1.2.3 From 8303d769ea9e9626c4f0c3bd13e35e904a1253ab Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 28 Feb 2014 08:31:02 +0100 Subject: ASoC: Remove unused 'reg2' field from soc_enum struct Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 01bd80a2c600..49d6c10f4612 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1101,7 +1101,6 @@ struct soc_mreg_control { /* enumerated kcontrol */ struct soc_enum { unsigned short reg; - unsigned short reg2; unsigned char shift_l; unsigned char shift_r; unsigned int items; -- cgit v1.2.3 From 29ae2fa5533e607a7d97b7564dc015252f1e73f4 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 28 Feb 2014 08:31:03 +0100 Subject: ASoC: Consolidate enum and value enum controls The implementations for enum and value enum controls are almost identical. The only difference is that the value enum uses an additional look-up table to map the control value to the register value, while the enum control uses a direct mapping. Enums and value enums can easily be distinguished at runtime, for value enums the values field of the snd_soc_enum struct contains the look-up table, while for enums it is NULL. This patch adds two new small helper functions called snd_soc_enum_item_to_val() and snd_soc_enum_val_to_item() which map between register value and control item. If the items field of the snd_soc_enum struct is NULL the function will do a direct mapping otherwise they'll use the look-up table to do the mapping. Using these small helper functions it is possible to use the same kcontrol handlers for both enums and value enums. The functions are added a inline functions in soc.h so they can also be used by the DAPM code to accomplish similar consolidation. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 34 ++++++++++++----- sound/soc/soc-core.c | 101 ++++++++------------------------------------------- 2 files changed, 41 insertions(+), 94 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 49d6c10f4612..60c700ccc518 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -195,11 +195,7 @@ .get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double, \ .private_value = (unsigned long)&xenum } #define SOC_VALUE_ENUM(xname, xenum) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\ - .info = snd_soc_info_enum_double, \ - .get = snd_soc_get_value_enum_double, \ - .put = snd_soc_put_value_enum_double, \ - .private_value = (unsigned long)&xenum } + SOC_ENUM(xname, xenum) #define SOC_SINGLE_EXT(xname, xreg, xshift, xmax, xinvert,\ xhandler_get, xhandler_put) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ @@ -510,10 +506,6 @@ int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); -int snd_soc_get_value_enum_double(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); #define snd_soc_info_bool_ext snd_ctl_boolean_mono_info @@ -1182,6 +1174,30 @@ static inline bool snd_soc_volsw_is_stereo(struct soc_mixer_control *mc) return 1; } +static inline unsigned int snd_soc_enum_val_to_item(struct soc_enum *e, + unsigned int val) +{ + unsigned int i; + + if (!e->values) + return val; + + for (i = 0; i < e->items; i++) + if (val == e->values[i]) + return i; + + return 0; +} + +static inline unsigned int snd_soc_enum_item_to_val(struct soc_enum *e, + unsigned int item) +{ + if (!e->values) + return item; + + return e->values[item]; +} + int snd_soc_util_init(void); void snd_soc_util_exit(void); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 851733897206..2ddc7a4939d2 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2596,14 +2596,18 @@ int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int val; + unsigned int val, item; + unsigned int reg_val; - val = snd_soc_read(codec, e->reg); - ucontrol->value.enumerated.item[0] - = (val >> e->shift_l) & e->mask; - if (e->shift_l != e->shift_r) - ucontrol->value.enumerated.item[1] = - (val >> e->shift_r) & e->mask; + reg_val = snd_soc_read(codec, e->reg); + val = (reg_val >> e->shift_l) & e->mask; + item = snd_soc_enum_val_to_item(e, val); + ucontrol->value.enumerated.item[0] = item; + if (e->shift_l != e->shift_r) { + val = (reg_val >> e->shift_l) & e->mask; + item = snd_soc_enum_val_to_item(e, val); + ucontrol->value.enumerated.item[1] = item; + } return 0; } @@ -2623,17 +2627,18 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; unsigned int val; unsigned int mask; - if (ucontrol->value.enumerated.item[0] >= e->items) + if (item[0] >= e->items) return -EINVAL; - val = ucontrol->value.enumerated.item[0] << e->shift_l; + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; mask = e->mask << e->shift_l; if (e->shift_l != e->shift_r) { - if (ucontrol->value.enumerated.item[1] >= e->items) + if (item[1] >= e->items) return -EINVAL; - val |= ucontrol->value.enumerated.item[1] << e->shift_r; + val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r; mask |= e->mask << e->shift_r; } @@ -2641,80 +2646,6 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_put_enum_double); -/** - * snd_soc_get_value_enum_double - semi enumerated double mixer get callback - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to get the value of a double semi enumerated mixer. - * - * Semi enumerated mixer: the enumerated items are referred as values. Can be - * used for handling bitfield coded enumeration for example. - * - * Returns 0 for success. - */ -int snd_soc_get_value_enum_double(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int reg_val, val, mux; - - reg_val = snd_soc_read(codec, e->reg); - val = (reg_val >> e->shift_l) & e->mask; - for (mux = 0; mux < e->items; mux++) { - if (val == e->values[mux]) - break; - } - ucontrol->value.enumerated.item[0] = mux; - if (e->shift_l != e->shift_r) { - val = (reg_val >> e->shift_r) & e->mask; - for (mux = 0; mux < e->items; mux++) { - if (val == e->values[mux]) - break; - } - ucontrol->value.enumerated.item[1] = mux; - } - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_get_value_enum_double); - -/** - * snd_soc_put_value_enum_double - semi enumerated double mixer put callback - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to set the value of a double semi enumerated mixer. - * - * Semi enumerated mixer: the enumerated items are referred as values. Can be - * used for handling bitfield coded enumeration for example. - * - * Returns 0 for success. - */ -int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int val; - unsigned int mask; - - if (ucontrol->value.enumerated.item[0] >= e->items) - return -EINVAL; - val = e->values[ucontrol->value.enumerated.item[0]] << e->shift_l; - mask = e->mask << e->shift_l; - if (e->shift_l != e->shift_r) { - if (ucontrol->value.enumerated.item[1] >= e->items) - return -EINVAL; - val |= e->values[ucontrol->value.enumerated.item[1]] << e->shift_r; - mask |= e->mask << e->shift_r; - } - - return snd_soc_update_bits_locked(codec, e->reg, mask, val); -} -EXPORT_SYMBOL_GPL(snd_soc_put_value_enum_double); - /** * snd_soc_read_signed - Read a codec register and interprete as signed value * @codec: codec -- cgit v1.2.3 From 3727b4968453dbab8fe18f979d67285eb6b66801 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 28 Feb 2014 08:31:04 +0100 Subject: ASoC: dapm: Consolidate MUXs and value MUXs MUXs and value MUXs are almost identical, the only difference is that a value MUX uses a look-up table to map from the selected control item to a register value, while MUXs use a direct mapping. This patch uses snd_soc_enum_item_to_val() and snd_soc_enum_val_to_item(), which where earlier introduced during the consolidation of enum and value enum controls, to hide this difference. This allows us to use the same code path for both MUXs and value MUXs and a lot of nearly duplicated code can be removed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 15 +---- sound/soc/soc-dapm.c | 157 ++++++----------------------------------------- 2 files changed, 21 insertions(+), 151 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 3b991766a8f2..2ec14cb3ff1e 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -112,9 +112,7 @@ struct device; SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ .kcontrol_news = wcontrols, .num_kcontrols = 1} #define SND_SOC_DAPM_VALUE_MUX(wname, wreg, wshift, winvert, wcontrols) \ -{ .id = snd_soc_dapm_value_mux, .name = wname, \ - SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ - .kcontrol_news = wcontrols, .num_kcontrols = 1} + SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) /* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */ #define SOC_PGA_ARRAY(wname, wreg, wshift, winvert,\ @@ -324,11 +322,7 @@ struct device; .put = xput, \ .private_value = (unsigned long)&xenum } #define SOC_DAPM_VALUE_ENUM(xname, xenum) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .info = snd_soc_info_enum_double, \ - .get = snd_soc_dapm_get_value_enum_double, \ - .put = snd_soc_dapm_put_value_enum_double, \ - .private_value = (unsigned long)&xenum } + SOC_DAPM_ENUM(xname, xenum) #define SOC_DAPM_PIN_SWITCH(xname) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname " Switch", \ .info = snd_soc_dapm_info_pin_switch, \ @@ -396,10 +390,6 @@ int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); -int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); int snd_soc_dapm_info_pin_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol, @@ -486,7 +476,6 @@ enum snd_soc_dapm_type { snd_soc_dapm_output, /* output pin */ snd_soc_dapm_mux, /* selects 1 analog signal from many inputs */ snd_soc_dapm_virt_mux, /* virtual version of snd_soc_dapm_mux */ - snd_soc_dapm_value_mux, /* selects 1 analog signal from many inputs */ snd_soc_dapm_mixer, /* mixes several analog signals together */ snd_soc_dapm_mixer_named_ctl, /* mixer with named controls */ snd_soc_dapm_pga, /* programmable gain/attenuation (volume) */ diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 96a97a3dfb68..f23eeeeaafc5 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -71,7 +71,6 @@ static int dapm_up_seq[] = { [snd_soc_dapm_mic] = 5, [snd_soc_dapm_mux] = 6, [snd_soc_dapm_virt_mux] = 6, - [snd_soc_dapm_value_mux] = 6, [snd_soc_dapm_dac] = 7, [snd_soc_dapm_switch] = 8, [snd_soc_dapm_mixer] = 8, @@ -103,7 +102,6 @@ static int dapm_down_seq[] = { [snd_soc_dapm_micbias] = 8, [snd_soc_dapm_mux] = 9, [snd_soc_dapm_virt_mux] = 9, - [snd_soc_dapm_value_mux] = 9, [snd_soc_dapm_aif_in] = 10, [snd_soc_dapm_aif_out] = 10, [snd_soc_dapm_dai_in] = 10, @@ -534,7 +532,8 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, unsigned int val, item; soc_widget_read(w, e->reg, &val); - item = (val >> e->shift_l) & e->mask; + val = (val >> e->shift_l) & e->mask; + item = snd_soc_enum_val_to_item(e, val); if (item < e->items && !strcmp(p->name, e->texts[item])) p->connect = 1; @@ -557,24 +556,6 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, p->connect = 1; } break; - case snd_soc_dapm_value_mux: { - struct soc_enum *e = (struct soc_enum *) - w->kcontrol_news[i].private_value; - unsigned int val, item; - - soc_widget_read(w, e->reg, &val); - val = (val >> e->shift_l) & e->mask; - for (item = 0; item < e->items; item++) { - if (val == e->values[item]) - break; - } - - if (item < e->items && !strcmp(p->name, e->texts[item])) - p->connect = 1; - else - p->connect = 0; - } - break; /* does not affect routing - always connected */ case snd_soc_dapm_pga: case snd_soc_dapm_out_drv: @@ -725,7 +706,6 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, break; case snd_soc_dapm_mux: case snd_soc_dapm_virt_mux: - case snd_soc_dapm_value_mux: wname_in_long_name = true; kcname_in_long_name = false; break; @@ -2463,7 +2443,6 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, return 0; case snd_soc_dapm_mux: case snd_soc_dapm_virt_mux: - case snd_soc_dapm_value_mux: ret = dapm_connect_mux(dapm, wsource, wsink, path, control, &wsink->kcontrol_news[0]); if (ret != 0) @@ -2791,7 +2770,6 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) break; case snd_soc_dapm_mux: case snd_soc_dapm_virt_mux: - case snd_soc_dapm_value_mux: dapm_new_mux(w); break; case snd_soc_dapm_pga: @@ -2953,13 +2931,16 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int val; + unsigned int reg_val, val; - val = snd_soc_read(codec, e->reg); - ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & e->mask; - if (e->shift_l != e->shift_r) - ucontrol->value.enumerated.item[1] = - (val >> e->shift_r) & e->mask; + reg_val = snd_soc_read(codec, e->reg); + val = (reg_val >> e->shift_l) & e->mask; + ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(e, val); + if (e->shift_l != e->shift_r) { + val = (reg_val >> e->shift_r) & e->mask; + val = snd_soc_enum_val_to_item(e, val); + ucontrol->value.enumerated.item[1] = val; + } return 0; } @@ -2980,20 +2961,21 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct snd_soc_card *card = codec->card; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int val, mux, change; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int val, change; unsigned int mask; struct snd_soc_dapm_update update; int ret = 0; - if (ucontrol->value.enumerated.item[0] >= e->items) + if (item[0] >= e->items) return -EINVAL; - mux = ucontrol->value.enumerated.item[0]; - val = mux << e->shift_l; + + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; mask = e->mask << e->shift_l; if (e->shift_l != e->shift_r) { - if (ucontrol->value.enumerated.item[1] >= e->items) + if (item[1] > e->items) return -EINVAL; - val |= ucontrol->value.enumerated.item[1] << e->shift_r; + val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_l; mask |= e->mask << e->shift_r; } @@ -3007,7 +2989,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, update.val = val; card->update = &update; - ret = soc_dapm_mux_update_power(card, kcontrol, mux, e); + ret = soc_dapm_mux_update_power(card, kcontrol, item[0], e); card->update = NULL; } @@ -3073,106 +3055,6 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt); -/** - * snd_soc_dapm_get_value_enum_double - dapm semi enumerated double mixer get - * callback - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to get the value of a dapm semi enumerated double mixer control. - * - * Semi enumerated mixer: the enumerated items are referred as values. Can be - * used for handling bitfield coded enumeration for example. - * - * Returns 0 for success. - */ -int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int reg_val, val, mux; - - reg_val = snd_soc_read(codec, e->reg); - val = (reg_val >> e->shift_l) & e->mask; - for (mux = 0; mux < e->items; mux++) { - if (val == e->values[mux]) - break; - } - ucontrol->value.enumerated.item[0] = mux; - if (e->shift_l != e->shift_r) { - val = (reg_val >> e->shift_r) & e->mask; - for (mux = 0; mux < e->items; mux++) { - if (val == e->values[mux]) - break; - } - ucontrol->value.enumerated.item[1] = mux; - } - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_dapm_get_value_enum_double); - -/** - * snd_soc_dapm_put_value_enum_double - dapm semi enumerated double mixer set - * callback - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to set the value of a dapm semi enumerated double mixer control. - * - * Semi enumerated mixer: the enumerated items are referred as values. Can be - * used for handling bitfield coded enumeration for example. - * - * Returns 0 for success. - */ -int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); - struct snd_soc_card *card = codec->card; - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int val, mux, change; - unsigned int mask; - struct snd_soc_dapm_update update; - int ret = 0; - - if (ucontrol->value.enumerated.item[0] >= e->items) - return -EINVAL; - mux = ucontrol->value.enumerated.item[0]; - val = e->values[ucontrol->value.enumerated.item[0]] << e->shift_l; - mask = e->mask << e->shift_l; - if (e->shift_l != e->shift_r) { - if (ucontrol->value.enumerated.item[1] >= e->items) - return -EINVAL; - val |= e->values[ucontrol->value.enumerated.item[1]] << e->shift_r; - mask |= e->mask << e->shift_r; - } - - mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - - change = snd_soc_test_bits(codec, e->reg, mask, val); - if (change) { - update.kcontrol = kcontrol; - update.reg = e->reg; - update.mask = mask; - update.val = val; - card->update = &update; - - ret = soc_dapm_mux_update_power(card, kcontrol, mux, e); - - card->update = NULL; - } - - mutex_unlock(&card->dapm_mutex); - - if (ret > 0) - soc_dpcm_runtime_update(card); - - return change; -} -EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double); - /** * snd_soc_dapm_info_pin_switch - Info for a pin switch * @@ -3302,7 +3184,6 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, break; case snd_soc_dapm_mux: case snd_soc_dapm_virt_mux: - case snd_soc_dapm_value_mux: w->power_check = dapm_generic_check_power; break; case snd_soc_dapm_dai_out: -- cgit v1.2.3 From b948837a32cc2a510c2baf90111c8c5dde801a66 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 28 Feb 2014 08:31:05 +0100 Subject: ASoC: Add macros for defining virtual enums With the upcoming consolidation of normal MUXs and virtual MUXs we need to be able to distinguish between enums with and without a backing register at the enum level. Use the same approach as used for virtual mixer controls by setting the reg field of the enum to SND_SOC_NOPM for enums without a backing register. This patch adds a set of helper macros that can be used to define such enums. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 60c700ccc518..08cb677b464b 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -189,6 +189,8 @@ .mask = xmask, .items = xitems, .texts = xtexts, .values = xvalues} #define SOC_VALUE_ENUM_SINGLE(xreg, xshift, xmask, xnitmes, xtexts, xvalues) \ SOC_VALUE_ENUM_DOUBLE(xreg, xshift, xshift, xmask, xnitmes, xtexts, xvalues) +#define SOC_ENUM_SINGLE_VIRT(xitems, xtexts) \ + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, xitems, xtexts) #define SOC_ENUM(xname, xenum) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\ .info = snd_soc_info_enum_double, \ @@ -293,6 +295,8 @@ ARRAY_SIZE(xtexts), xtexts, xvalues) #define SOC_VALUE_ENUM_SINGLE_DECL(name, xreg, xshift, xmask, xtexts, xvalues) \ SOC_VALUE_ENUM_DOUBLE_DECL(name, xreg, xshift, xshift, xmask, xtexts, xvalues) +#define SOC_ENUM_SINGLE_VIRT_DECL(name, xtexts) \ + const struct soc_enum name = SOC_ENUM_SINGLE_VIRT(ARRAY_SIZE(xtexts), xtexts) /* * Component probe and remove ordering levels for components with runtime @@ -1092,7 +1096,7 @@ struct soc_mreg_control { /* enumerated kcontrol */ struct soc_enum { - unsigned short reg; + int reg; unsigned char shift_l; unsigned char shift_r; unsigned int items; -- cgit v1.2.3 From 236aaa6863581634bd6d599ccf7f7b38deeafdc0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 28 Feb 2014 08:31:11 +0100 Subject: ASoC: dapm: Consolidate MUXs and virtual MUXs MUXs and virtual MUXs are almost identical, the only difference is that for virtual MUX there is no hardware backing register in which setting is stored. This patch adds code, which is similar to what we already do for DAPM mixer controls to support virtual mixer controls, to DAPM enum controls. The new code will check if the enum does a hardware backing register and skip over reading and writing to the register if it has not. This allows us to use the same code path for both MUXs and virtual MUXs and a lot of nearly identical code can be removed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 23 +++------- sound/soc/soc-dapm.c | 113 +++++++++++++---------------------------------- 2 files changed, 35 insertions(+), 101 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 2ec14cb3ff1e..04d32d89bc7d 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -108,9 +108,7 @@ struct device; SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ .kcontrol_news = wcontrols, .num_kcontrols = 1} #define SND_SOC_DAPM_VIRT_MUX(wname, wreg, wshift, winvert, wcontrols) \ -{ .id = snd_soc_dapm_virt_mux, .name = wname, \ - SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ - .kcontrol_news = wcontrols, .num_kcontrols = 1} + SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) #define SND_SOC_DAPM_VALUE_MUX(wname, wreg, wshift, winvert, wcontrols) \ SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) @@ -170,10 +168,8 @@ struct device; .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_VIRT_MUX_E(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ -{ .id = snd_soc_dapm_virt_mux, .name = wname, \ - SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ - .kcontrol_news = wcontrols, .num_kcontrols = 1, \ - .event = wevent, .event_flags = wflags} + SND_SOC_DAPM_MUX_E(wname, wreg, wshift, winvert, wcontrols, wevent, \ + wflags) /* additional sequencing control within an event type */ #define SND_SOC_DAPM_PGA_S(wname, wsubseq, wreg, wshift, winvert, \ @@ -309,12 +305,8 @@ struct device; .get = snd_soc_dapm_get_enum_double, \ .put = snd_soc_dapm_put_enum_double, \ .private_value = (unsigned long)&xenum } -#define SOC_DAPM_ENUM_VIRT(xname, xenum) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .info = snd_soc_info_enum_double, \ - .get = snd_soc_dapm_get_enum_virt, \ - .put = snd_soc_dapm_put_enum_virt, \ - .private_value = (unsigned long)&xenum } +#define SOC_DAPM_ENUM_VIRT(xname, xenum) \ + SOC_DAPM_ENUM(xname, xenum) #define SOC_DAPM_ENUM_EXT(xname, xenum, xget, xput) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_enum_double, \ @@ -386,10 +378,6 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); -int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); int snd_soc_dapm_info_pin_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol, @@ -475,7 +463,6 @@ enum snd_soc_dapm_type { snd_soc_dapm_input = 0, /* input pin */ snd_soc_dapm_output, /* output pin */ snd_soc_dapm_mux, /* selects 1 analog signal from many inputs */ - snd_soc_dapm_virt_mux, /* virtual version of snd_soc_dapm_mux */ snd_soc_dapm_mixer, /* mixes several analog signals together */ snd_soc_dapm_mixer_named_ctl, /* mixer with named controls */ snd_soc_dapm_pga, /* programmable gain/attenuation (volume) */ diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index f23eeeeaafc5..c07c7fb7593a 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -70,7 +70,6 @@ static int dapm_up_seq[] = { [snd_soc_dapm_aif_out] = 4, [snd_soc_dapm_mic] = 5, [snd_soc_dapm_mux] = 6, - [snd_soc_dapm_virt_mux] = 6, [snd_soc_dapm_dac] = 7, [snd_soc_dapm_switch] = 8, [snd_soc_dapm_mixer] = 8, @@ -101,7 +100,6 @@ static int dapm_down_seq[] = { [snd_soc_dapm_mic] = 7, [snd_soc_dapm_micbias] = 8, [snd_soc_dapm_mux] = 9, - [snd_soc_dapm_virt_mux] = 9, [snd_soc_dapm_aif_in] = 10, [snd_soc_dapm_aif_out] = 10, [snd_soc_dapm_dai_in] = 10, @@ -531,9 +529,19 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, w->kcontrol_news[i].private_value; unsigned int val, item; - soc_widget_read(w, e->reg, &val); - val = (val >> e->shift_l) & e->mask; - item = snd_soc_enum_val_to_item(e, val); + if (e->reg != SND_SOC_NOPM) { + soc_widget_read(w, e->reg, &val); + val = (val >> e->shift_l) & e->mask; + item = snd_soc_enum_val_to_item(e, val); + } else { + /* since a virtual mux has no backing registers to + * decide which path to connect, it will try to match + * with the first enumeration. This is to ensure + * that the default mux choice (the first) will be + * correctly powered up during initialization. + */ + item = 0; + } if (item < e->items && !strcmp(p->name, e->texts[item])) p->connect = 1; @@ -541,21 +549,6 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, p->connect = 0; } break; - case snd_soc_dapm_virt_mux: { - struct soc_enum *e = (struct soc_enum *) - w->kcontrol_news[i].private_value; - - p->connect = 0; - /* since a virtual mux has no backing registers to - * decide which path to connect, it will try to match - * with the first enumeration. This is to ensure - * that the default mux choice (the first) will be - * correctly powered up during initialization. - */ - if (!strcmp(p->name, e->texts[0])) - p->connect = 1; - } - break; /* does not affect routing - always connected */ case snd_soc_dapm_pga: case snd_soc_dapm_out_drv: @@ -705,7 +698,6 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, kcname_in_long_name = true; break; case snd_soc_dapm_mux: - case snd_soc_dapm_virt_mux: wname_in_long_name = true; kcname_in_long_name = false; break; @@ -2442,7 +2434,6 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, path->connect = 1; return 0; case snd_soc_dapm_mux: - case snd_soc_dapm_virt_mux: ret = dapm_connect_mux(dapm, wsource, wsink, path, control, &wsink->kcontrol_news[0]); if (ret != 0) @@ -2769,7 +2760,6 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) dapm_new_mixer(w); break; case snd_soc_dapm_mux: - case snd_soc_dapm_virt_mux: dapm_new_mux(w); break; case snd_soc_dapm_pga: @@ -2933,7 +2923,11 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int reg_val, val; - reg_val = snd_soc_read(codec, e->reg); + if (e->reg != SND_SOC_NOPM) + reg_val = snd_soc_read(codec, e->reg); + else + reg_val = dapm_kcontrol_get_value(kcontrol); + val = (reg_val >> e->shift_l) & e->mask; ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(e, val); if (e->shift_l != e->shift_r) { @@ -2981,13 +2975,19 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - change = snd_soc_test_bits(codec, e->reg, mask, val); + if (e->reg != SND_SOC_NOPM) + change = snd_soc_test_bits(codec, e->reg, mask, val); + else + change = dapm_kcontrol_set_value(kcontrol, val); + if (change) { - update.kcontrol = kcontrol; - update.reg = e->reg; - update.mask = mask; - update.val = val; - card->update = &update; + if (e->reg != SND_SOC_NOPM) { + update.kcontrol = kcontrol; + update.reg = e->reg; + update.mask = mask; + update.val = val; + card->update = &update; + } ret = soc_dapm_mux_update_power(card, kcontrol, item[0], e); @@ -3003,58 +3003,6 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); -/** - * snd_soc_dapm_get_enum_virt - Get virtual DAPM mux - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Returns 0 for success. - */ -int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.enumerated.item[0] = dapm_kcontrol_get_value(kcontrol); - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt); - -/** - * snd_soc_dapm_put_enum_virt - Set virtual DAPM mux - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Returns 0 for success. - */ -int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); - struct snd_soc_card *card = codec->card; - unsigned int value; - struct soc_enum *e = - (struct soc_enum *)kcontrol->private_value; - int change; - int ret = 0; - - if (ucontrol->value.enumerated.item[0] >= e->items) - return -EINVAL; - - mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - - value = ucontrol->value.enumerated.item[0]; - change = dapm_kcontrol_set_value(kcontrol, value); - if (change) - ret = soc_dapm_mux_update_power(card, kcontrol, value, e); - - mutex_unlock(&card->dapm_mutex); - - if (ret > 0) - soc_dpcm_runtime_update(card); - - return change; -} -EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt); - /** * snd_soc_dapm_info_pin_switch - Info for a pin switch * @@ -3183,7 +3131,6 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, w->power_check = dapm_generic_check_power; break; case snd_soc_dapm_mux: - case snd_soc_dapm_virt_mux: w->power_check = dapm_generic_check_power; break; case snd_soc_dapm_dai_out: -- cgit v1.2.3 From 697dce94ed37e0653e5bba593f11e2b14877cd63 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 24 Feb 2014 22:15:33 -0800 Subject: ASoC: rsnd: tidyup RSND_SSI_xxx flags 6f3ab6c1c022e7a4877d38940cd45ae7804a15e2 (ASoC: rsnd: remove pin sync option) added unused RSND_SSI_CLK_FROM_ADG flag. It should remove RSND_SSI_SYNC. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/rcar_snd.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include') diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 2be05aea54f9..e3d585c67685 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -34,9 +34,6 @@ * B : SSI direction */ #define RSND_SSI_CLK_PIN_SHARE (1 << 31) -#define RSND_SSI_SYNC (1 << 29) /* SSI34_sync etc */ -#define RSND_SSI_CLK_FROM_ADG (1 << 30) /* clock parent is master */ - #define RSND_SSI_PLAY (1 << 24) #define RSND_SSI_SET(_dai_id, _dma_id, _pio_irq, _flags) \ -- cgit v1.2.3 From 78f13d0c5a2888564b2bed7f8433c8ec889997ff Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 3 Mar 2014 20:49:50 -0800 Subject: ASoC: rsnd: add struct rsnd_dai_platform_info R-Car sound DAI consists from SSI/SCU/SSIU/SRU... Current R-Car sound DAI is decided from these settings, but it is intuitively unclear, and is not good design for DT support. This patch adds new rsnd_dai_platform_info to solve this issue. But now, many platform is using this driver without rsnd_dai_platform_info. So, this patch still supports DAI settings via SSI to keep compatible. It will be removed in next Linux version. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/rcar_snd.h | 17 ++++++++++++++++- sound/soc/sh/rcar/core.c | 23 ++++++++++++++++------- sound/soc/sh/rcar/ssi.c | 14 ++++++++++++++ 3 files changed, 46 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index e3d585c67685..698f7b5fc76d 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -36,13 +36,15 @@ #define RSND_SSI_CLK_PIN_SHARE (1 << 31) #define RSND_SSI_PLAY (1 << 24) +#define RSND_SSI(_dma_id, _pio_irq, _flags) \ +{ .dma_id = _dma_id, .pio_irq = _pio_irq, .flags = _flags } #define RSND_SSI_SET(_dai_id, _dma_id, _pio_irq, _flags) \ { .dai_id = _dai_id, .dma_id = _dma_id, .pio_irq = _pio_irq, .flags = _flags } #define RSND_SSI_UNUSED \ { .dai_id = -1, .dma_id = -1, .pio_irq = -1, .flags = 0 } struct rsnd_ssi_platform_info { - int dai_id; + int dai_id; /* will be removed */ int dma_id; int pio_irq; u32 flags; @@ -53,6 +55,8 @@ struct rsnd_ssi_platform_info { */ #define RSND_SCU_USE_HPBIF (1 << 31) /* it needs RSND_SSI_DEPENDENT */ +#define RSND_SCU(rate, _dma_id) \ +{ .flags = RSND_SCU_USE_HPBIF, .convert_rate = rate, .dma_id = _dma_id, } #define RSND_SCU_SET(rate, _dma_id) \ { .flags = RSND_SCU_USE_HPBIF, .convert_rate = rate, .dma_id = _dma_id, } #define RSND_SCU_UNUSED \ @@ -64,6 +68,15 @@ struct rsnd_scu_platform_info { int dma_id; /* for Gen2 SCU */ }; +struct rsnd_dai_path_info { + struct rsnd_ssi_platform_info *ssi; +}; + +struct rsnd_dai_platform_info { + struct rsnd_dai_path_info playback; + struct rsnd_dai_path_info capture; +}; + /* * flags * @@ -81,6 +94,8 @@ struct rcar_snd_info { int ssi_info_nr; struct rsnd_scu_platform_info *scu_info; int scu_info_nr; + struct rsnd_dai_platform_info *dai_info; + int dai_info_nr; int (*start)(int id); int (*stop)(int id); }; diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index ea747614fbf8..450472633eb1 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -639,19 +639,26 @@ static int rsnd_dai_probe(struct platform_device *pdev, struct rsnd_priv *priv) { struct snd_soc_dai_driver *drv; + struct rcar_snd_info *info = rsnd_priv_to_info(priv); struct rsnd_dai *rdai; struct rsnd_mod *pmod, *cmod; struct device *dev = rsnd_priv_to_dev(priv); - int dai_nr; + int dai_nr = info->dai_info_nr; int i; - /* get max dai nr */ - for (dai_nr = 0; dai_nr < 32; dai_nr++) { - pmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 1); - cmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 0); + /* + * dai_nr should be set via dai_info_nr, + * but allow it to keeping compatible + */ + if (!dai_nr) { + /* get max dai nr */ + for (dai_nr = 0; dai_nr < 32; dai_nr++) { + pmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 1); + cmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 0); - if (!pmod && !cmod) - break; + if (!pmod && !cmod) + break; + } } if (!dai_nr) { @@ -671,6 +678,8 @@ static int rsnd_dai_probe(struct platform_device *pdev, priv->rdai = rdai; for (i = 0; i < dai_nr; i++) { + if (info->dai_info) + rdai[i].info = &info->dai_info[i]; pmod = rsnd_ssi_mod_get_frm_dai(priv, i, 1); cmod = rsnd_ssi_mod_get_frm_dai(priv, i, 0); diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 25a7d441f8fc..34234813f742 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -451,12 +451,26 @@ static struct rsnd_mod_ops rsnd_ssi_non_ops = { struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv, int dai_id, int is_play) { + struct rsnd_dai_platform_info *dai_info = NULL; + struct rsnd_dai_path_info *path_info = NULL; + struct rsnd_ssi_platform_info *target_info = NULL; struct rsnd_ssi *ssi; int i, has_play; + if (priv->rdai) + dai_info = priv->rdai[dai_id].info; + if (dai_info) + path_info = (is_play) ? &dai_info->playback : &dai_info->capture; + if (path_info) + target_info = path_info->ssi; + is_play = !!is_play; for_each_rsnd_ssi(ssi, priv, i) { + if (target_info == ssi->info) + return &ssi->mod; + + /* for compatible */ if (rsnd_ssi_dai_id(ssi) != dai_id) continue; -- cgit v1.2.3 From 389933d9f6e55a1ef3a71549c36f6283b9f8c145 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 3 Mar 2014 20:50:00 -0800 Subject: ASoC: rsnd: Get correct SCU ID Current rsnd driver is assuming that SCU/SRU ID is same as SSIU/SSI ID, because Gen1 can't select it. But, Gen2 can select it. The SCU/SRU/SSIU/SSI pair depends on the platform. This patch get correct SCU ID from platform info. To keep compatible, it still assuming SCU ID = SSI ID if platform doesn't have info Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/rcar_snd.h | 1 + sound/soc/sh/rcar/core.c | 47 +++++++++++++++++++++++++++++++++-------------- sound/soc/sh/rcar/rsnd.h | 14 ++++++++++++++ sound/soc/sh/rcar/scu.c | 21 ++++++++++++++++----- sound/soc/sh/rcar/ssi.c | 9 ++++++++- 5 files changed, 72 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 698f7b5fc76d..1d8c68323f49 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -70,6 +70,7 @@ struct rsnd_scu_platform_info { struct rsnd_dai_path_info { struct rsnd_ssi_platform_info *ssi; + struct rsnd_scu_platform_info *scu; }; struct rsnd_dai_platform_info { diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 450472633eb1..7316d10e4649 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -107,6 +107,11 @@ (!(priv->info->func) ? 0 : \ priv->info->func(param)) +#define rsnd_is_enable_path(io, name) \ + ((io)->info ? (io)->info->name : NULL) +#define rsnd_info_id(priv, io, name) \ + ((io)->info->name - priv->info->name##_info) + /* * rsnd_mod functions */ @@ -572,8 +577,10 @@ static int rsnd_path_init(struct rsnd_priv *priv, struct rsnd_dai_stream *io) { struct rsnd_mod *mod; + struct rsnd_dai_platform_info *dai_info = rdai->info; int ret; - int id; + int ssi_id = -1; + int scu_id = -1; /* * Gen1 is created by SRU/SSI, and this SRU is base module of @@ -584,29 +591,35 @@ static int rsnd_path_init(struct rsnd_priv *priv, * * Gen2 SCU path is very flexible, but, Gen1 SRU (SCU parts) is * using fixed path. - * - * Then, SSI id = SCU id here */ - /* get SSI's ID */ - mod = rsnd_ssi_mod_get_frm_dai(priv, - rsnd_dai_id(priv, rdai), - rsnd_dai_is_play(rdai, io)); - if (!mod) - return 0; - id = rsnd_mod_id(mod); + if (dai_info) { + if (rsnd_is_enable_path(io, ssi)) + ssi_id = rsnd_info_id(priv, io, ssi); + if (rsnd_is_enable_path(io, scu)) + scu_id = rsnd_info_id(priv, io, scu); + } else { + /* get SSI's ID */ + mod = rsnd_ssi_mod_get_frm_dai(priv, + rsnd_dai_id(priv, rdai), + rsnd_dai_is_play(rdai, io)); + if (!mod) + return 0; + ssi_id = scu_id = rsnd_mod_id(mod); + } + ret = 0; /* SCU */ - mod = rsnd_scu_mod_get(priv, id); - if (mod) { + if (scu_id >= 0) { + mod = rsnd_scu_mod_get(priv, scu_id); ret = rsnd_dai_connect(mod, io); if (ret < 0) return ret; } /* SSI */ - mod = rsnd_ssi_mod_get(priv, id); - if (mod) { + if (ssi_id >= 0) { + mod = rsnd_ssi_mod_get(priv, ssi_id); ret = rsnd_dai_connect(mod, io); if (ret < 0) return ret; @@ -699,6 +712,9 @@ static int rsnd_dai_probe(struct platform_device *pdev, drv[i].playback.formats = RSND_FMTS; drv[i].playback.channels_min = 2; drv[i].playback.channels_max = 2; + + if (info->dai_info) + rdai[i].playback.info = &info->dai_info[i].playback; rsnd_path_init(priv, &rdai[i], &rdai[i].playback); } if (cmod) { @@ -706,6 +722,9 @@ static int rsnd_dai_probe(struct platform_device *pdev, drv[i].capture.formats = RSND_FMTS; drv[i].capture.channels_min = 2; drv[i].capture.channels_max = 2; + + if (info->dai_info) + rdai[i].capture.info = &info->dai_info[i].capture; rsnd_path_init(priv, &rdai[i], &rdai[i].capture); } diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index d5afdee6b6f2..3472631c7b35 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -211,6 +211,7 @@ char *rsnd_mod_name(struct rsnd_mod *mod); struct rsnd_dai_stream { struct snd_pcm_substream *substream; struct rsnd_mod *mod[RSND_MOD_MAX]; + struct rsnd_dai_path_info *info; /* rcar_snd.h */ int byte_pos; int period_pos; int byte_per_period; @@ -328,6 +329,19 @@ struct rsnd_priv { #define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags) #define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags) +#define rsnd_info_is_playback(priv, type) \ +({ \ + struct rcar_snd_info *info = rsnd_priv_to_info(priv); \ + int i, is_play = 0; \ + for (i = 0; i < info->dai_info_nr; i++) { \ + if (info->dai_info[i].playback.type == (type)->info) { \ + is_play = 1; \ + break; \ + } \ + } \ + is_play; \ +}) + /* * R-Car SCU */ diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c index 1073d35486e3..b517300f32ce 100644 --- a/sound/soc/sh/rcar/scu.c +++ b/sound/soc/sh/rcar/scu.c @@ -620,6 +620,9 @@ int rsnd_scu_probe(struct platform_device *pdev, * init SCU */ nr = info->scu_info_nr; + if (!nr) + return 0; + scu = devm_kzalloc(dev, sizeof(*scu) * nr, GFP_KERNEL); if (!scu) { dev_err(dev, "SCU allocate failed\n"); @@ -644,11 +647,19 @@ int rsnd_scu_probe(struct platform_device *pdev, if (rsnd_is_gen1(priv)) ops = &rsnd_scu_gen1_ops; if (rsnd_is_gen2(priv)) { - struct rsnd_mod *ssi = rsnd_ssi_mod_get(priv, i); - int ret = rsnd_dma_init(priv, - rsnd_mod_to_dma(&scu->mod), - rsnd_ssi_is_play(ssi), - scu->info->dma_id); + int ret; + int is_play; + + if (info->dai_info) { + is_play = rsnd_info_is_playback(priv, scu); + } else { + struct rsnd_mod *ssi = rsnd_ssi_mod_get(priv, i); + is_play = rsnd_ssi_is_play(ssi); + } + ret = rsnd_dma_init(priv, + rsnd_mod_to_dma(&scu->mod), + is_play, + scu->info->dma_id); if (ret < 0) return ret; diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 34234813f742..9162c2bb6cc5 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -567,9 +567,16 @@ int rsnd_ssi_probe(struct platform_device *pdev, * SSI DMA case */ if (pinfo->dma_id > 0) { + int is_play; + + if (info->dai_info) + is_play = rsnd_info_is_playback(priv, ssi); + else + is_play = rsnd_ssi_is_play(&ssi->mod); + ret = rsnd_dma_init( priv, rsnd_mod_to_dma(&ssi->mod), - rsnd_ssi_is_play(&ssi->mod), + is_play, pinfo->dma_id); if (ret < 0) dev_info(dev, "SSI DMA failed. try PIO transter\n"); -- cgit v1.2.3 From ba9c949f797aa3af56303445812a452144c61c35 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 3 Mar 2014 20:51:21 -0800 Subject: ASoC: rsnd: rename scu to src R-Car sound has SCU unit which has SRC/CTU/MIX/DVC, and current rsnd driver has scu.c and scu module. Current scu.c has SRC support only. My first concept was control these feature on scu.c but, it become difficult and un-understandable now. This patch rename scu to src Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/rcar_snd.h | 18 +- sound/soc/sh/rcar/Makefile | 2 +- sound/soc/sh/rcar/core.c | 26 +- sound/soc/sh/rcar/rsnd.h | 24 +- sound/soc/sh/rcar/scu.c | 682 -------------------------------------------- sound/soc/sh/rcar/src.c | 687 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/sh/rcar/ssi.c | 6 +- 7 files changed, 727 insertions(+), 718 deletions(-) delete mode 100644 sound/soc/sh/rcar/scu.c create mode 100644 sound/soc/sh/rcar/src.c (limited to 'include') diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 1d8c68323f49..34a3c02a4576 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -55,14 +55,18 @@ struct rsnd_ssi_platform_info { */ #define RSND_SCU_USE_HPBIF (1 << 31) /* it needs RSND_SSI_DEPENDENT */ -#define RSND_SCU(rate, _dma_id) \ +#define RSND_SRC(rate, _dma_id) \ { .flags = RSND_SCU_USE_HPBIF, .convert_rate = rate, .dma_id = _dma_id, } -#define RSND_SCU_SET(rate, _dma_id) \ +#define RSND_SRC_SET(rate, _dma_id) \ { .flags = RSND_SCU_USE_HPBIF, .convert_rate = rate, .dma_id = _dma_id, } -#define RSND_SCU_UNUSED \ +#define RSND_SRC_UNUSED \ { .flags = 0, .convert_rate = 0, .dma_id = 0, } -struct rsnd_scu_platform_info { +#define rsnd_scu_platform_info rsnd_src_platform_info +#define src_info scu_info +#define src_info_nr scu_info_nr + +struct rsnd_src_platform_info { u32 flags; u32 convert_rate; /* sampling rate convert */ int dma_id; /* for Gen2 SCU */ @@ -70,7 +74,7 @@ struct rsnd_scu_platform_info { struct rsnd_dai_path_info { struct rsnd_ssi_platform_info *ssi; - struct rsnd_scu_platform_info *scu; + struct rsnd_src_platform_info *src; }; struct rsnd_dai_platform_info { @@ -93,8 +97,8 @@ struct rcar_snd_info { u32 flags; struct rsnd_ssi_platform_info *ssi_info; int ssi_info_nr; - struct rsnd_scu_platform_info *scu_info; - int scu_info_nr; + struct rsnd_src_platform_info *src_info; + int src_info_nr; struct rsnd_dai_platform_info *dai_info; int dai_info_nr; int (*start)(int id); diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index 0ff492df7929..7d0051ced838 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile @@ -1,2 +1,2 @@ -snd-soc-rcar-objs := core.o gen.o scu.o adg.o ssi.o +snd-soc-rcar-objs := core.o gen.o src.o adg.o ssi.o obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o \ No newline at end of file diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index ceb4e8bd4970..6a1b45df8101 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -73,13 +73,13 @@ * | +- ssi[2] * | ... * | - * | ** these control scu + * | ** these control src * | - * +- scu + * +- src * | - * +- scu[0] - * +- scu[1] - * +- scu[2] + * +- src[0] + * +- src[1] + * +- src[2] * ... * * @@ -572,7 +572,7 @@ static int rsnd_path_init(struct rsnd_priv *priv, struct rsnd_dai_platform_info *dai_info = rdai->info; int ret; int ssi_id = -1; - int scu_id = -1; + int src_id = -1; /* * Gen1 is created by SRU/SSI, and this SRU is base module of @@ -587,8 +587,8 @@ static int rsnd_path_init(struct rsnd_priv *priv, if (dai_info) { if (rsnd_is_enable_path(io, ssi)) ssi_id = rsnd_info_id(priv, io, ssi); - if (rsnd_is_enable_path(io, scu)) - scu_id = rsnd_info_id(priv, io, scu); + if (rsnd_is_enable_path(io, src)) + src_id = rsnd_info_id(priv, io, src); } else { /* get SSI's ID */ mod = rsnd_ssi_mod_get_frm_dai(priv, @@ -596,14 +596,14 @@ static int rsnd_path_init(struct rsnd_priv *priv, rsnd_dai_is_play(rdai, io)); if (!mod) return 0; - ssi_id = scu_id = rsnd_mod_id(mod); + ssi_id = src_id = rsnd_mod_id(mod); } ret = 0; - /* SCU */ - if (scu_id >= 0) { - mod = rsnd_scu_mod_get(priv, scu_id); + /* SRC */ + if (src_id >= 0) { + mod = rsnd_src_mod_get(priv, src_id); ret = rsnd_dai_connect(mod, io); if (ret < 0) return ret; @@ -806,7 +806,7 @@ static int rsnd_probe(struct platform_device *pdev) struct rsnd_priv *priv) = { rsnd_gen_probe, rsnd_ssi_probe, - rsnd_scu_probe, + rsnd_src_probe, rsnd_adg_probe, rsnd_dai_probe, }; diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 878dc6e20ff2..c46e0afa54ae 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -160,7 +160,7 @@ void rsnd_dma_quit(struct rsnd_priv *priv, * R-Car sound mod */ enum rsnd_mod_type { - RSND_MOD_SCU = 0, + RSND_MOD_SRC = 0, RSND_MOD_SSI, RSND_MOD_MAX, }; @@ -224,7 +224,7 @@ struct rsnd_dai_stream { int next_period_byte; }; #define rsnd_io_to_mod_ssi(io) ((io)->mod[RSND_MOD_SSI]) -#define rsnd_io_to_mod_scu(io) ((io)->mod[RSND_MOD_SCU]) +#define rsnd_io_to_mod_src(io) ((io)->mod[RSND_MOD_SRC]) struct rsnd_dai { char name[RSND_DAI_NAME_SIZE]; @@ -302,10 +302,10 @@ struct rsnd_priv { void *gen; /* - * below value will be filled on rsnd_scu_probe() + * below value will be filled on rsnd_src_probe() */ - void *scu; - int scu_nr; + void *src; + int src_nr; /* * below value will be filled on rsnd_adg_probe() @@ -345,22 +345,22 @@ struct rsnd_priv { }) /* - * R-Car SCU + * R-Car SRC */ -int rsnd_scu_probe(struct platform_device *pdev, +int rsnd_src_probe(struct platform_device *pdev, struct rsnd_priv *priv); -struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id); -unsigned int rsnd_scu_get_ssi_rate(struct rsnd_priv *priv, +struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id); +unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, struct rsnd_dai_stream *io, struct snd_pcm_runtime *runtime); -int rsnd_scu_ssi_mode_init(struct rsnd_mod *ssi_mod, +int rsnd_src_ssi_mode_init(struct rsnd_mod *ssi_mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io); -int rsnd_scu_enable_ssi_irq(struct rsnd_mod *ssi_mod, +int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io); -#define rsnd_scu_nr(priv) ((priv)->scu_nr) +#define rsnd_src_nr(priv) ((priv)->src_nr) /* * R-Car SSI diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c deleted file mode 100644 index 3526a5c2f608..000000000000 --- a/sound/soc/sh/rcar/scu.c +++ /dev/null @@ -1,682 +0,0 @@ -/* - * Renesas R-Car SCU support - * - * Copyright (C) 2013 Renesas Solutions Corp. - * Kuninori Morimoto - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include "rsnd.h" - -struct rsnd_scu { - struct rsnd_scu_platform_info *info; /* rcar_snd.h */ - struct rsnd_mod mod; - struct clk *clk; -}; - -#define RSND_SCU_NAME_SIZE 16 - -/* - * ADINR - */ -#define OTBL_24 (0 << 16) -#define OTBL_22 (2 << 16) -#define OTBL_20 (4 << 16) -#define OTBL_18 (6 << 16) -#define OTBL_16 (8 << 16) - -#define rsnd_scu_mode_flags(p) ((p)->info->flags) -#define rsnd_scu_convert_rate(p) ((p)->info->convert_rate) -#define rsnd_mod_to_scu(_mod) \ - container_of((_mod), struct rsnd_scu, mod) -#define rsnd_scu_hpbif_is_enable(scu) \ - (rsnd_scu_mode_flags(scu) & RSND_SCU_USE_HPBIF) -#define rsnd_scu_dma_available(scu) \ - rsnd_dma_available(rsnd_mod_to_dma(&(scu)->mod)) - -#define for_each_rsnd_scu(pos, priv, i) \ - for ((i) = 0; \ - ((i) < rsnd_scu_nr(priv)) && \ - ((pos) = (struct rsnd_scu *)(priv)->scu + i); \ - i++) - - -/* - * image of SRC (Sampling Rate Converter) - * - * 96kHz <-> +-----+ 48kHz +-----+ 48kHz +-------+ - * 48kHz <-> | SRC | <------> | SSI | <-----> | codec | - * 44.1kHz <-> +-----+ +-----+ +-------+ - * ... - * - */ - -/* - * scu.c is caring... - * - * Gen1 - * - * [mem] -> [SRU] -> [SSI] - * |--------| - * - * Gen2 - * - * [mem] -> [SCU] -> [SSIU] -> [SSI] - * |-----------------| - */ - -/* - * How to use SRC bypass mode for debugging - * - * SRC has bypass mode, and it is useful for debugging. - * In Gen2 case, - * SRCm_MODE controls whether SRC is used or not - * SSI_MODE0 controls whether SSIU which receives SRC data - * is used or not. - * Both SRCm_MODE/SSI_MODE0 settings are needed if you use SRC, - * but SRC bypass mode needs SSI_MODE0 only. - * - * This driver request - * struct rsnd_scu_platform_info { - * u32 flags; - * u32 convert_rate; - * } - * - * rsnd_scu_hpbif_is_enable() will be true - * if flags had RSND_SCU_USE_HPBIF, - * and it controls whether SSIU is used or not. - * - * rsnd_scu_convert_rate() indicates - * above convert_rate, and it controls - * whether SRC is used or not. - * - * ex) doesn't use SRC - * struct rsnd_scu_platform_info info = { - * .flags = 0, - * .convert_rate = 0, - * }; - * - * ex) uses SRC - * struct rsnd_scu_platform_info info = { - * .flags = RSND_SCU_USE_HPBIF, - * .convert_rate = 48000, - * }; - * - * ex) uses SRC bypass mode - * struct rsnd_scu_platform_info info = { - * .flags = RSND_SCU_USE_HPBIF, - * .convert_rate = 0, - * }; - * - */ - -/* - * Gen1/Gen2 common functions - */ -int rsnd_scu_ssi_mode_init(struct rsnd_mod *ssi_mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); - struct rsnd_mod *scu_mod = rsnd_io_to_mod_scu(io); - struct rcar_snd_info *info = rsnd_priv_to_info(priv); - int ssi_id = rsnd_mod_id(ssi_mod); - int has_scu = 0; - - /* - * SSI_MODE0 - */ - if (info->dai_info) { - has_scu = !!scu_mod; - } else { - struct rsnd_scu *scu = rsnd_mod_to_scu(scu_mod); - has_scu = rsnd_scu_hpbif_is_enable(scu); - } - - rsnd_mod_bset(ssi_mod, SSI_MODE0, (1 << ssi_id), - has_scu ? 0 : (1 << ssi_id)); - - /* - * SSI_MODE1 - */ - if (rsnd_ssi_is_pin_sharing(ssi_mod)) { - int shift = -1; - switch (ssi_id) { - case 1: - shift = 0; - break; - case 2: - shift = 2; - break; - case 4: - shift = 16; - break; - } - - if (shift >= 0) - rsnd_mod_bset(ssi_mod, SSI_MODE1, - 0x3 << shift, - rsnd_dai_is_clk_master(rdai) ? - 0x2 << shift : 0x1 << shift); - } - - return 0; -} - -int rsnd_scu_enable_ssi_irq(struct rsnd_mod *ssi_mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); - - /* enable PIO interrupt if Gen2 */ - if (rsnd_is_gen2(priv)) - rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0f000000); - - return 0; -} - -unsigned int rsnd_scu_get_ssi_rate(struct rsnd_priv *priv, - struct rsnd_dai_stream *io, - struct snd_pcm_runtime *runtime) -{ - struct rsnd_scu *scu; - unsigned int rate; - - scu = rsnd_mod_to_scu(rsnd_io_to_mod_scu(io)); - - /* - * return convert rate if SRC is used, - * otherwise, return runtime->rate as usual - */ - rate = rsnd_scu_convert_rate(scu); - if (!rate) - rate = runtime->rate; - - return rate; -} - -static int rsnd_scu_set_convert_rate(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - struct rsnd_scu *scu = rsnd_mod_to_scu(mod); - u32 convert_rate = rsnd_scu_convert_rate(scu); - u32 adinr = runtime->channels; - u32 fsrate = 0; - - if (convert_rate) - fsrate = 0x0400000 / convert_rate * runtime->rate; - - /* set/clear soft reset */ - rsnd_mod_write(mod, SRC_SWRSR, 0); - rsnd_mod_write(mod, SRC_SWRSR, 1); - - /* - * Initialize the operation of the SRC internal circuits - * see rsnd_scu_start() - */ - rsnd_mod_write(mod, SRC_SRCIR, 1); - - /* Set channel number and output bit length */ - switch (runtime->sample_bits) { - case 16: - adinr |= OTBL_16; - break; - case 32: - adinr |= OTBL_24; - break; - default: - return -EIO; - } - rsnd_mod_write(mod, SRC_ADINR, adinr); - - /* Enable the initial value of IFS */ - if (fsrate) { - rsnd_mod_write(mod, SRC_IFSCR, 1); - - /* Set initial value of IFS */ - rsnd_mod_write(mod, SRC_IFSVR, fsrate); - } - - /* use DMA transfer */ - rsnd_mod_write(mod, SRC_BUSIF_MODE, 1); - - return 0; -} - -static int rsnd_scu_init(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - struct rsnd_scu *scu = rsnd_mod_to_scu(mod); - - clk_enable(scu->clk); - - return 0; -} - -static int rsnd_scu_quit(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - struct rsnd_scu *scu = rsnd_mod_to_scu(mod); - - clk_disable(scu->clk); - - return 0; -} - -static int rsnd_scu_start(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - struct rsnd_scu *scu = rsnd_mod_to_scu(mod); - - /* - * Cancel the initialization and operate the SRC function - * see rsnd_scu_set_convert_rate() - */ - rsnd_mod_write(mod, SRC_SRCIR, 0); - - if (rsnd_scu_convert_rate(scu)) - rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1); - - return 0; -} - - -static int rsnd_scu_stop(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - struct rsnd_scu *scu = rsnd_mod_to_scu(mod); - - if (rsnd_scu_convert_rate(scu)) - rsnd_mod_write(mod, SRC_ROUTE_MODE0, 0); - - return 0; -} - -static struct rsnd_mod_ops rsnd_scu_non_ops = { - .name = "scu (non)", -}; - -/* - * Gen1 functions - */ -static int rsnd_src_set_route_gen1(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - struct scu_route_config { - u32 mask; - int shift; - } routes[] = { - { 0xF, 0, }, /* 0 */ - { 0xF, 4, }, /* 1 */ - { 0xF, 8, }, /* 2 */ - { 0x7, 12, }, /* 3 */ - { 0x7, 16, }, /* 4 */ - { 0x7, 20, }, /* 5 */ - { 0x7, 24, }, /* 6 */ - { 0x3, 28, }, /* 7 */ - { 0x3, 30, }, /* 8 */ - }; - u32 mask; - u32 val; - int id; - - id = rsnd_mod_id(mod); - if (id < 0 || id >= ARRAY_SIZE(routes)) - return -EIO; - - /* - * SRC_ROUTE_SELECT - */ - val = rsnd_dai_is_play(rdai, io) ? 0x1 : 0x2; - val = val << routes[id].shift; - mask = routes[id].mask << routes[id].shift; - - rsnd_mod_bset(mod, SRC_ROUTE_SEL, mask, val); - - return 0; -} - -static int rsnd_scu_set_convert_timing_gen1(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_scu *scu = rsnd_mod_to_scu(mod); - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - u32 convert_rate = rsnd_scu_convert_rate(scu); - u32 mask; - u32 val; - int shift; - int id = rsnd_mod_id(mod); - int ret; - - /* - * SRC_TIMING_SELECT - */ - shift = (id % 4) * 8; - mask = 0x1F << shift; - - /* - * ADG is used as source clock if SRC was used, - * then, SSI WS is used as destination clock. - * SSI WS is used as source clock if SRC is not used - * (when playback, source/destination become reverse when capture) - */ - ret = 0; - if (convert_rate) { - /* use ADG */ - val = 0; - ret = rsnd_adg_set_convert_clk_gen1(priv, mod, - runtime->rate, - convert_rate); - } else if (8 == id) { - /* use SSI WS, but SRU8 is special */ - val = id << shift; - } else { - /* use SSI WS */ - val = (id + 1) << shift; - } - - if (ret < 0) - return ret; - - switch (id / 4) { - case 0: - rsnd_mod_bset(mod, SRC_TMG_SEL0, mask, val); - break; - case 1: - rsnd_mod_bset(mod, SRC_TMG_SEL1, mask, val); - break; - case 2: - rsnd_mod_bset(mod, SRC_TMG_SEL2, mask, val); - break; - } - - return 0; -} - -static int rsnd_scu_set_convert_rate_gen1(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - int ret; - - ret = rsnd_scu_set_convert_rate(mod, rdai, io); - if (ret < 0) - return ret; - - /* Select SRC mode (fixed value) */ - rsnd_mod_write(mod, SRC_SRCCR, 0x00010110); - - /* Set the restriction value of the FS ratio (98%) */ - rsnd_mod_write(mod, SRC_MNFSR, - rsnd_mod_read(mod, SRC_IFSVR) / 100 * 98); - - /* no SRC_BFSSR settings, since SRC_SRCCR::BUFMD is 0 */ - - return 0; -} - -static int rsnd_scu_init_gen1(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - int ret; - - ret = rsnd_scu_init(mod, rdai, io); - if (ret < 0) - return ret; - - ret = rsnd_src_set_route_gen1(mod, rdai, io); - if (ret < 0) - return ret; - - ret = rsnd_scu_set_convert_rate_gen1(mod, rdai, io); - if (ret < 0) - return ret; - - ret = rsnd_scu_set_convert_timing_gen1(mod, rdai, io); - if (ret < 0) - return ret; - - return 0; -} - -static int rsnd_scu_start_gen1(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - int id = rsnd_mod_id(mod); - - rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), (1 << id)); - - return rsnd_scu_start(mod, rdai, io); -} - -static int rsnd_scu_stop_gen1(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - int id = rsnd_mod_id(mod); - - rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), 0); - - return rsnd_scu_stop(mod, rdai, io); -} - -static struct rsnd_mod_ops rsnd_scu_gen1_ops = { - .name = "sru (gen1)", - .init = rsnd_scu_init_gen1, - .quit = rsnd_scu_quit, - .start = rsnd_scu_start_gen1, - .stop = rsnd_scu_stop_gen1, -}; - -/* - * Gen2 functions - */ -static int rsnd_scu_set_convert_rate_gen2(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - int ret; - - ret = rsnd_scu_set_convert_rate(mod, rdai, io); - if (ret < 0) - return ret; - - rsnd_mod_write(mod, SSI_BUSIF_ADINR, rsnd_mod_read(mod, SRC_ADINR)); - rsnd_mod_write(mod, SSI_BUSIF_MODE, rsnd_mod_read(mod, SRC_BUSIF_MODE)); - - rsnd_mod_write(mod, SRC_SRCCR, 0x00011110); - - rsnd_mod_write(mod, SRC_BSDSR, 0x01800000); - rsnd_mod_write(mod, SRC_BSISR, 0x00100060); - - return 0; -} - -static int rsnd_scu_set_convert_timing_gen2(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - struct rsnd_scu *scu = rsnd_mod_to_scu(mod); - u32 convert_rate = rsnd_scu_convert_rate(scu); - int ret; - - if (convert_rate) - ret = rsnd_adg_set_convert_clk_gen2(mod, rdai, io, - runtime->rate, - convert_rate); - else - ret = rsnd_adg_set_convert_timing_gen2(mod, rdai, io); - - return ret; -} - -static int rsnd_scu_probe_gen2(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rcar_snd_info *info = rsnd_priv_to_info(priv); - struct rsnd_scu *scu = rsnd_mod_to_scu(mod); - struct rsnd_mod *ssi = rsnd_ssi_mod_get(priv, rsnd_mod_id(mod)); - struct device *dev = rsnd_priv_to_dev(priv); - int ret; - int is_play; - - if (info->dai_info) - is_play = rsnd_info_is_playback(priv, scu); - else - is_play = rsnd_ssi_is_play(ssi); - - ret = rsnd_dma_init(priv, - rsnd_mod_to_dma(mod), - is_play, - scu->info->dma_id); - if (ret < 0) - dev_err(dev, "SCU DMA failed\n"); - - return ret; -} - -static int rsnd_scu_remove_gen2(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - rsnd_dma_quit(rsnd_mod_to_priv(mod), rsnd_mod_to_dma(mod)); - - return 0; -} - -static int rsnd_scu_init_gen2(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - int ret; - - ret = rsnd_scu_init(mod, rdai, io); - if (ret < 0) - return ret; - - ret = rsnd_scu_set_convert_rate_gen2(mod, rdai, io); - if (ret < 0) - return ret; - - ret = rsnd_scu_set_convert_timing_gen2(mod, rdai, io); - if (ret < 0) - return ret; - - return 0; -} - -static int rsnd_scu_start_gen2(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - struct rsnd_scu *scu = rsnd_mod_to_scu(mod); - - rsnd_dma_start(rsnd_mod_to_dma(&scu->mod)); - - rsnd_mod_write(mod, SSI_CTRL, 0x1); - rsnd_mod_write(mod, SRC_CTRL, 0x11); - - return rsnd_scu_start(mod, rdai, io); -} - -static int rsnd_scu_stop_gen2(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - struct rsnd_scu *scu = rsnd_mod_to_scu(mod); - - rsnd_mod_write(mod, SSI_CTRL, 0); - rsnd_mod_write(mod, SRC_CTRL, 0); - - rsnd_dma_stop(rsnd_mod_to_dma(&scu->mod)); - - return rsnd_scu_stop(mod, rdai, io); -} - -static struct rsnd_mod_ops rsnd_scu_gen2_ops = { - .name = "scu (gen2)", - .probe = rsnd_scu_probe_gen2, - .remove = rsnd_scu_remove_gen2, - .init = rsnd_scu_init_gen2, - .quit = rsnd_scu_quit, - .start = rsnd_scu_start_gen2, - .stop = rsnd_scu_stop_gen2, -}; - -struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id) -{ - if (WARN_ON(id < 0 || id >= rsnd_scu_nr(priv))) - id = 0; - - return &((struct rsnd_scu *)(priv->scu) + id)->mod; -} - -int rsnd_scu_probe(struct platform_device *pdev, - struct rsnd_priv *priv) -{ - struct rcar_snd_info *info = rsnd_priv_to_info(priv); - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_scu *scu; - struct rsnd_mod_ops *ops; - struct clk *clk; - char name[RSND_SCU_NAME_SIZE]; - int i, nr; - - /* - * init SCU - */ - nr = info->scu_info_nr; - if (!nr) - return 0; - - scu = devm_kzalloc(dev, sizeof(*scu) * nr, GFP_KERNEL); - if (!scu) { - dev_err(dev, "SCU allocate failed\n"); - return -ENOMEM; - } - - priv->scu_nr = nr; - priv->scu = scu; - - for_each_rsnd_scu(scu, priv, i) { - snprintf(name, RSND_SCU_NAME_SIZE, "scu.%d", i); - - clk = devm_clk_get(dev, name); - if (IS_ERR(clk)) - return PTR_ERR(clk); - - scu->info = &info->scu_info[i]; - scu->clk = clk; - - ops = &rsnd_scu_non_ops; - if (rsnd_scu_hpbif_is_enable(scu)) { - if (rsnd_is_gen1(priv)) - ops = &rsnd_scu_gen1_ops; - if (rsnd_is_gen2(priv)) - ops = &rsnd_scu_gen2_ops; - } - - rsnd_mod_init(priv, &scu->mod, ops, RSND_MOD_SCU, i); - - dev_dbg(dev, "SCU%d probed\n", i); - } - - return 0; -} diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c new file mode 100644 index 000000000000..ea6a214985d0 --- /dev/null +++ b/sound/soc/sh/rcar/src.c @@ -0,0 +1,687 @@ +/* + * Renesas R-Car SRC support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include "rsnd.h" + +struct rsnd_src { + struct rsnd_src_platform_info *info; /* rcar_snd.h */ + struct rsnd_mod mod; + struct clk *clk; +}; + +#define RSND_SRC_NAME_SIZE 16 + +/* + * ADINR + */ +#define OTBL_24 (0 << 16) +#define OTBL_22 (2 << 16) +#define OTBL_20 (4 << 16) +#define OTBL_18 (6 << 16) +#define OTBL_16 (8 << 16) + +#define rsnd_src_mode_flags(p) ((p)->info->flags) +#define rsnd_src_convert_rate(p) ((p)->info->convert_rate) +#define rsnd_mod_to_src(_mod) \ + container_of((_mod), struct rsnd_src, mod) +#define rsnd_src_hpbif_is_enable(src) \ + (rsnd_src_mode_flags(src) & RSND_SCU_USE_HPBIF) +#define rsnd_src_dma_available(src) \ + rsnd_dma_available(rsnd_mod_to_dma(&(src)->mod)) + +#define for_each_rsnd_src(pos, priv, i) \ + for ((i) = 0; \ + ((i) < rsnd_src_nr(priv)) && \ + ((pos) = (struct rsnd_src *)(priv)->src + i); \ + i++) + + +/* + * image of SRC (Sampling Rate Converter) + * + * 96kHz <-> +-----+ 48kHz +-----+ 48kHz +-------+ + * 48kHz <-> | SRC | <------> | SSI | <-----> | codec | + * 44.1kHz <-> +-----+ +-----+ +-------+ + * ... + * + */ + +/* + * src.c is caring... + * + * Gen1 + * + * [mem] -> [SRU] -> [SSI] + * |--------| + * + * Gen2 + * + * [mem] -> [SRC] -> [SSIU] -> [SSI] + * |-----------------| + */ + +/* + * How to use SRC bypass mode for debugging + * + * SRC has bypass mode, and it is useful for debugging. + * In Gen2 case, + * SRCm_MODE controls whether SRC is used or not + * SSI_MODE0 controls whether SSIU which receives SRC data + * is used or not. + * Both SRCm_MODE/SSI_MODE0 settings are needed if you use SRC, + * but SRC bypass mode needs SSI_MODE0 only. + * + * This driver request + * struct rsnd_src_platform_info { + * u32 flags; + * u32 convert_rate; + * } + * + * rsnd_src_hpbif_is_enable() will be true + * if flags had RSND_SRC_USE_HPBIF, + * and it controls whether SSIU is used or not. + * + * rsnd_src_convert_rate() indicates + * above convert_rate, and it controls + * whether SRC is used or not. + * + * ex) doesn't use SRC + * struct rsnd_src_platform_info info = { + * .flags = 0, + * .convert_rate = 0, + * }; + * + * ex) uses SRC + * struct rsnd_src_platform_info info = { + * .flags = RSND_SRC_USE_HPBIF, + * .convert_rate = 48000, + * }; + * + * ex) uses SRC bypass mode + * struct rsnd_src_platform_info info = { + * .flags = RSND_SRC_USE_HPBIF, + * .convert_rate = 0, + * }; + * + */ + +/* + * Gen1/Gen2 common functions + */ +int rsnd_src_ssi_mode_init(struct rsnd_mod *ssi_mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); + struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); + struct rcar_snd_info *info = rsnd_priv_to_info(priv); + int ssi_id = rsnd_mod_id(ssi_mod); + int has_src = 0; + + /* + * SSI_MODE0 + */ + if (info->dai_info) { + has_src = !!src_mod; + } else { + struct rsnd_src *src = rsnd_mod_to_src(src_mod); + has_src = rsnd_src_hpbif_is_enable(src); + } + + rsnd_mod_bset(ssi_mod, SSI_MODE0, (1 << ssi_id), + has_src ? 0 : (1 << ssi_id)); + + /* + * SSI_MODE1 + */ + if (rsnd_ssi_is_pin_sharing(ssi_mod)) { + int shift = -1; + switch (ssi_id) { + case 1: + shift = 0; + break; + case 2: + shift = 2; + break; + case 4: + shift = 16; + break; + } + + if (shift >= 0) + rsnd_mod_bset(ssi_mod, SSI_MODE1, + 0x3 << shift, + rsnd_dai_is_clk_master(rdai) ? + 0x2 << shift : 0x1 << shift); + } + + return 0; +} + +int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); + + /* enable PIO interrupt if Gen2 */ + if (rsnd_is_gen2(priv)) + rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0f000000); + + return 0; +} + +unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + struct snd_pcm_runtime *runtime) +{ + struct rsnd_src *src; + unsigned int rate; + + src = rsnd_mod_to_src(rsnd_io_to_mod_src(io)); + + /* + * return convert rate if SRC is used, + * otherwise, return runtime->rate as usual + */ + rate = rsnd_src_convert_rate(src); + if (!rate) + rate = runtime->rate; + + return rate; +} + +static int rsnd_src_set_convert_rate(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_src *src = rsnd_mod_to_src(mod); + u32 convert_rate = rsnd_src_convert_rate(src); + u32 adinr = runtime->channels; + u32 fsrate = 0; + + if (convert_rate) + fsrate = 0x0400000 / convert_rate * runtime->rate; + + /* set/clear soft reset */ + rsnd_mod_write(mod, SRC_SWRSR, 0); + rsnd_mod_write(mod, SRC_SWRSR, 1); + + /* + * Initialize the operation of the SRC internal circuits + * see rsnd_src_start() + */ + rsnd_mod_write(mod, SRC_SRCIR, 1); + + /* Set channel number and output bit length */ + switch (runtime->sample_bits) { + case 16: + adinr |= OTBL_16; + break; + case 32: + adinr |= OTBL_24; + break; + default: + return -EIO; + } + rsnd_mod_write(mod, SRC_ADINR, adinr); + + /* Enable the initial value of IFS */ + if (fsrate) { + rsnd_mod_write(mod, SRC_IFSCR, 1); + + /* Set initial value of IFS */ + rsnd_mod_write(mod, SRC_IFSVR, fsrate); + } + + /* use DMA transfer */ + rsnd_mod_write(mod, SRC_BUSIF_MODE, 1); + + return 0; +} + +static int rsnd_src_init(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_src *src = rsnd_mod_to_src(mod); + + clk_enable(src->clk); + + return 0; +} + +static int rsnd_src_quit(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_src *src = rsnd_mod_to_src(mod); + + clk_disable(src->clk); + + return 0; +} + +static int rsnd_src_start(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_src *src = rsnd_mod_to_src(mod); + + /* + * Cancel the initialization and operate the SRC function + * see rsnd_src_set_convert_rate() + */ + rsnd_mod_write(mod, SRC_SRCIR, 0); + + if (rsnd_src_convert_rate(src)) + rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1); + + return 0; +} + + +static int rsnd_src_stop(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_src *src = rsnd_mod_to_src(mod); + + if (rsnd_src_convert_rate(src)) + rsnd_mod_write(mod, SRC_ROUTE_MODE0, 0); + + return 0; +} + +static struct rsnd_mod_ops rsnd_src_non_ops = { + .name = "src (non)", +}; + +/* + * Gen1 functions + */ +static int rsnd_src_set_route_gen1(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct src_route_config { + u32 mask; + int shift; + } routes[] = { + { 0xF, 0, }, /* 0 */ + { 0xF, 4, }, /* 1 */ + { 0xF, 8, }, /* 2 */ + { 0x7, 12, }, /* 3 */ + { 0x7, 16, }, /* 4 */ + { 0x7, 20, }, /* 5 */ + { 0x7, 24, }, /* 6 */ + { 0x3, 28, }, /* 7 */ + { 0x3, 30, }, /* 8 */ + }; + u32 mask; + u32 val; + int id; + + id = rsnd_mod_id(mod); + if (id < 0 || id >= ARRAY_SIZE(routes)) + return -EIO; + + /* + * SRC_ROUTE_SELECT + */ + val = rsnd_dai_is_play(rdai, io) ? 0x1 : 0x2; + val = val << routes[id].shift; + mask = routes[id].mask << routes[id].shift; + + rsnd_mod_bset(mod, SRC_ROUTE_SEL, mask, val); + + return 0; +} + +static int rsnd_src_set_convert_timing_gen1(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_src *src = rsnd_mod_to_src(mod); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + u32 convert_rate = rsnd_src_convert_rate(src); + u32 mask; + u32 val; + int shift; + int id = rsnd_mod_id(mod); + int ret; + + /* + * SRC_TIMING_SELECT + */ + shift = (id % 4) * 8; + mask = 0x1F << shift; + + /* + * ADG is used as source clock if SRC was used, + * then, SSI WS is used as destination clock. + * SSI WS is used as source clock if SRC is not used + * (when playback, source/destination become reverse when capture) + */ + ret = 0; + if (convert_rate) { + /* use ADG */ + val = 0; + ret = rsnd_adg_set_convert_clk_gen1(priv, mod, + runtime->rate, + convert_rate); + } else if (8 == id) { + /* use SSI WS, but SRU8 is special */ + val = id << shift; + } else { + /* use SSI WS */ + val = (id + 1) << shift; + } + + if (ret < 0) + return ret; + + switch (id / 4) { + case 0: + rsnd_mod_bset(mod, SRC_TMG_SEL0, mask, val); + break; + case 1: + rsnd_mod_bset(mod, SRC_TMG_SEL1, mask, val); + break; + case 2: + rsnd_mod_bset(mod, SRC_TMG_SEL2, mask, val); + break; + } + + return 0; +} + +static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + int ret; + + ret = rsnd_src_set_convert_rate(mod, rdai, io); + if (ret < 0) + return ret; + + /* Select SRC mode (fixed value) */ + rsnd_mod_write(mod, SRC_SRCCR, 0x00010110); + + /* Set the restriction value of the FS ratio (98%) */ + rsnd_mod_write(mod, SRC_MNFSR, + rsnd_mod_read(mod, SRC_IFSVR) / 100 * 98); + + /* no SRC_BFSSR settings, since SRC_SRCCR::BUFMD is 0 */ + + return 0; +} + +static int rsnd_src_init_gen1(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + int ret; + + ret = rsnd_src_init(mod, rdai, io); + if (ret < 0) + return ret; + + ret = rsnd_src_set_route_gen1(mod, rdai, io); + if (ret < 0) + return ret; + + ret = rsnd_src_set_convert_rate_gen1(mod, rdai, io); + if (ret < 0) + return ret; + + ret = rsnd_src_set_convert_timing_gen1(mod, rdai, io); + if (ret < 0) + return ret; + + return 0; +} + +static int rsnd_src_start_gen1(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + int id = rsnd_mod_id(mod); + + rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), (1 << id)); + + return rsnd_src_start(mod, rdai, io); +} + +static int rsnd_src_stop_gen1(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + int id = rsnd_mod_id(mod); + + rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), 0); + + return rsnd_src_stop(mod, rdai, io); +} + +static struct rsnd_mod_ops rsnd_src_gen1_ops = { + .name = "sru (gen1)", + .init = rsnd_src_init_gen1, + .quit = rsnd_src_quit, + .start = rsnd_src_start_gen1, + .stop = rsnd_src_stop_gen1, +}; + +/* + * Gen2 functions + */ +static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + int ret; + + ret = rsnd_src_set_convert_rate(mod, rdai, io); + if (ret < 0) + return ret; + + rsnd_mod_write(mod, SSI_BUSIF_ADINR, rsnd_mod_read(mod, SRC_ADINR)); + rsnd_mod_write(mod, SSI_BUSIF_MODE, rsnd_mod_read(mod, SRC_BUSIF_MODE)); + + rsnd_mod_write(mod, SRC_SRCCR, 0x00011110); + + rsnd_mod_write(mod, SRC_BSDSR, 0x01800000); + rsnd_mod_write(mod, SRC_BSISR, 0x00100060); + + return 0; +} + +static int rsnd_src_set_convert_timing_gen2(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_src *src = rsnd_mod_to_src(mod); + u32 convert_rate = rsnd_src_convert_rate(src); + int ret; + + if (convert_rate) + ret = rsnd_adg_set_convert_clk_gen2(mod, rdai, io, + runtime->rate, + convert_rate); + else + ret = rsnd_adg_set_convert_timing_gen2(mod, rdai, io); + + return ret; +} + +static int rsnd_src_probe_gen2(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rcar_snd_info *info = rsnd_priv_to_info(priv); + struct rsnd_src *src = rsnd_mod_to_src(mod); + struct rsnd_mod *ssi = rsnd_ssi_mod_get(priv, rsnd_mod_id(mod)); + struct device *dev = rsnd_priv_to_dev(priv); + int ret; + int is_play; + + if (info->dai_info) + is_play = rsnd_info_is_playback(priv, src); + else + is_play = rsnd_ssi_is_play(ssi); + + ret = rsnd_dma_init(priv, + rsnd_mod_to_dma(mod), + is_play, + src->info->dma_id); + if (ret < 0) + dev_err(dev, "SRC DMA failed\n"); + + return ret; +} + +static int rsnd_src_remove_gen2(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + rsnd_dma_quit(rsnd_mod_to_priv(mod), rsnd_mod_to_dma(mod)); + + return 0; +} + +static int rsnd_src_init_gen2(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + int ret; + + ret = rsnd_src_init(mod, rdai, io); + if (ret < 0) + return ret; + + ret = rsnd_src_set_convert_rate_gen2(mod, rdai, io); + if (ret < 0) + return ret; + + ret = rsnd_src_set_convert_timing_gen2(mod, rdai, io); + if (ret < 0) + return ret; + + return 0; +} + +static int rsnd_src_start_gen2(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_src *src = rsnd_mod_to_src(mod); + + rsnd_dma_start(rsnd_mod_to_dma(&src->mod)); + + rsnd_mod_write(mod, SSI_CTRL, 0x1); + rsnd_mod_write(mod, SRC_CTRL, 0x11); + + return rsnd_src_start(mod, rdai, io); +} + +static int rsnd_src_stop_gen2(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_src *src = rsnd_mod_to_src(mod); + + rsnd_mod_write(mod, SSI_CTRL, 0); + rsnd_mod_write(mod, SRC_CTRL, 0); + + rsnd_dma_stop(rsnd_mod_to_dma(&src->mod)); + + return rsnd_src_stop(mod, rdai, io); +} + +static struct rsnd_mod_ops rsnd_src_gen2_ops = { + .name = "src (gen2)", + .probe = rsnd_src_probe_gen2, + .remove = rsnd_src_remove_gen2, + .init = rsnd_src_init_gen2, + .quit = rsnd_src_quit, + .start = rsnd_src_start_gen2, + .stop = rsnd_src_stop_gen2, +}; + +struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) +{ + if (WARN_ON(id < 0 || id >= rsnd_src_nr(priv))) + id = 0; + + return &((struct rsnd_src *)(priv->src) + id)->mod; +} + +int rsnd_src_probe(struct platform_device *pdev, + struct rsnd_priv *priv) +{ + struct rcar_snd_info *info = rsnd_priv_to_info(priv); + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_src *src; + struct rsnd_mod_ops *ops; + struct clk *clk; + char name[RSND_SRC_NAME_SIZE]; + int i, nr; + + /* + * init SRC + */ + nr = info->src_info_nr; + if (!nr) + return 0; + + src = devm_kzalloc(dev, sizeof(*src) * nr, GFP_KERNEL); + if (!src) { + dev_err(dev, "SRC allocate failed\n"); + return -ENOMEM; + } + + priv->src_nr = nr; + priv->src = src; + + for_each_rsnd_src(src, priv, i) { + snprintf(name, RSND_SRC_NAME_SIZE, "src.%d", i); + + clk = devm_clk_get(dev, name); + if (IS_ERR(clk)) { + snprintf(name, RSND_SRC_NAME_SIZE, "scu.%d", i); + clk = devm_clk_get(dev, name); + } + + if (IS_ERR(clk)) + return PTR_ERR(clk); + + src->info = &info->src_info[i]; + src->clk = clk; + + ops = &rsnd_src_non_ops; + if (rsnd_src_hpbif_is_enable(src)) { + if (rsnd_is_gen1(priv)) + ops = &rsnd_src_gen1_ops; + if (rsnd_is_gen2(priv)) + ops = &rsnd_src_gen2_ops; + } + + rsnd_mod_init(priv, &src->mod, ops, RSND_MOD_SRC, i); + + dev_dbg(dev, "SRC%d probed\n", i); + } + + return 0; +} diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 45f828ded935..633b23d209b9 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -121,7 +121,7 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, 1, 2, 4, 8, 16, 6, 12, }; unsigned int main_rate; - unsigned int rate = rsnd_scu_get_ssi_rate(priv, io, runtime); + unsigned int rate = rsnd_src_get_ssi_rate(priv, io, runtime); /* * Find best clock, and try to start ADG @@ -287,7 +287,7 @@ static int rsnd_ssi_init(struct rsnd_mod *mod, ssi->cr_own = cr; ssi->err = -1; /* ignore 1st error */ - rsnd_scu_ssi_mode_init(mod, rdai, io); + rsnd_src_ssi_mode_init(mod, rdai, io); return 0; } @@ -387,7 +387,7 @@ static int rsnd_ssi_pio_start(struct rsnd_mod *mod, /* enable PIO IRQ */ ssi->cr_etc = UIEN | OIEN | DIEN; - rsnd_scu_enable_ssi_irq(mod, rdai, io); + rsnd_src_enable_ssi_irq(mod, rdai, io); rsnd_ssi_hw_start(ssi, rdai, io); -- cgit v1.2.3 From 208a1589db3e30767223d97e39e13237328e8a6e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 5 Mar 2014 13:17:42 +0100 Subject: ASoC: Handle ignore_pmdown_time for CODEC to CODEC links For CODEC to CODEC links we should only immediately power down if both CODECs are configured to ignore the power down delay. Factor the logic for this into a helper function that can be used for both compressed and normal PCMs. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 2 ++ sound/soc/soc-compress.c | 3 +-- sound/soc/soc-pcm.c | 27 +++++++++++++++++++++++++-- 3 files changed, 28 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 9a001472b96a..93c31c70b90a 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -413,6 +413,8 @@ struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card, struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card, const char *dai_link); +bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd); + /* Utility functions to get clock rates from various things */ int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots); int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params); diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 5e9690c85d8f..ef585af4081b 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -235,8 +235,7 @@ static int soc_compr_free(struct snd_compr_stream *cstream) cpu_dai->runtime = NULL; if (cstream->direction == SND_COMPRESS_PLAYBACK) { - if (!rtd->pmdown_time || codec->ignore_pmdown_time || - rtd->dai_link->ignore_pmdown_time) { + if (snd_soc_runtime_ignore_pmdown_time(rtd)) { snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, SND_SOC_DAPM_STREAM_STOP); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 47e1ce771e65..f098c8007cbe 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -34,6 +34,30 @@ #define DPCM_MAX_BE_USERS 8 +/** + * snd_soc_runtime_ignore_pmdown_time() - Check whether to ignore the power down delay + * @rtd: The ASoC PCM runtime that should be checked. + * + * This function checks whether the power down delay should be ignored for a + * specific PCM runtime. Returns true if the delay is 0, if it the DAI link has + * been configured to ignore the delay, or if none of the components benefits + * from having the delay. + */ +bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd) +{ + bool ignore = true; + + if (!rtd->pmdown_time || rtd->dai_link->ignore_pmdown_time) + return true; + + if (rtd->cpu_dai->codec) + ignore &= rtd->cpu_dai->codec->ignore_pmdown_time; + + ignore &= rtd->codec_dai->codec->ignore_pmdown_time; + + return ignore; +} + /** * snd_soc_set_runtime_hwparams - set the runtime hardware parameters * @substream: the pcm substream @@ -496,8 +520,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) cpu_dai->runtime = NULL; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - if (!rtd->pmdown_time || codec->ignore_pmdown_time || - rtd->dai_link->ignore_pmdown_time) { + if (snd_soc_runtime_ignore_pmdown_time(rtd)) { /* powered down playback stream now */ snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, -- cgit v1.2.3 From 24894b76468ed250d03f9718ddfe77b902995cbd Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 5 Mar 2014 13:17:43 +0100 Subject: ASoC: Add helper functions for PCM runtime 'active' management We have the same code that increments and decrements the active field of the various PCM runtime components (all with the same bugs). Factor this out into common helper functions. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 2 ++ sound/soc/soc-compress.c | 62 +++++++--------------------------- sound/soc/soc-pcm.c | 86 +++++++++++++++++++++++++++++++++++------------- 3 files changed, 78 insertions(+), 72 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 93c31c70b90a..53d15e0e6e89 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -414,6 +414,8 @@ struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card, const char *dai_link); bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd); +void snd_soc_runtime_activate(struct snd_soc_pcm_runtime *rtd, int stream); +void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream); /* Utility functions to get clock rates from various things */ int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots); diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index ef585af4081b..91083e6a6b38 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -30,8 +30,6 @@ static int soc_compr_open(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; int ret = 0; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); @@ -52,17 +50,7 @@ static int soc_compr_open(struct snd_compr_stream *cstream) } } - if (cstream->direction == SND_COMPRESS_PLAYBACK) { - cpu_dai->playback_active++; - codec_dai->playback_active++; - } else { - cpu_dai->capture_active++; - codec_dai->capture_active++; - } - - cpu_dai->active++; - codec_dai->active++; - rtd->codec->active++; + snd_soc_runtime_activate(rtd, cstream->direction); mutex_unlock(&rtd->pcm_mutex); @@ -81,8 +69,6 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) struct snd_soc_pcm_runtime *fe = cstream->private_data; struct snd_pcm_substream *fe_substream = fe->pcm->streams[0].substream; struct snd_soc_platform *platform = fe->platform; - struct snd_soc_dai *cpu_dai = fe->cpu_dai; - struct snd_soc_dai *codec_dai = fe->codec_dai; struct snd_soc_dpcm *dpcm; struct snd_soc_dapm_widget_list *list; int stream; @@ -140,17 +126,7 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN; fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; - if (cstream->direction == SND_COMPRESS_PLAYBACK) { - cpu_dai->playback_active++; - codec_dai->playback_active++; - } else { - cpu_dai->capture_active++; - codec_dai->capture_active++; - } - - cpu_dai->active++; - codec_dai->active++; - fe->codec->active++; + snd_soc_runtime_activate(fe, stream); mutex_unlock(&fe->card->mutex); @@ -202,23 +178,18 @@ static int soc_compr_free(struct snd_compr_stream *cstream) struct snd_soc_platform *platform = rtd->platform; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_codec *codec = rtd->codec; + int stream; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); - if (cstream->direction == SND_COMPRESS_PLAYBACK) { - cpu_dai->playback_active--; - codec_dai->playback_active--; - } else { - cpu_dai->capture_active--; - codec_dai->capture_active--; - } + if (cstream->direction == SND_COMPRESS_PLAYBACK) + stream = SNDRV_PCM_STREAM_PLAYBACK; + else + stream = SNDRV_PCM_STREAM_CAPTURE; - snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction); + snd_soc_runtime_deactivate(rtd, stream); - cpu_dai->active--; - codec_dai->active--; - codec->active--; + snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction); if (!cpu_dai->active) cpu_dai->rate = 0; @@ -260,26 +231,17 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *fe = cstream->private_data; struct snd_soc_platform *platform = fe->platform; - struct snd_soc_dai *cpu_dai = fe->cpu_dai; - struct snd_soc_dai *codec_dai = fe->codec_dai; struct snd_soc_dpcm *dpcm; int stream, ret; mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); - if (cstream->direction == SND_COMPRESS_PLAYBACK) { + if (cstream->direction == SND_COMPRESS_PLAYBACK) stream = SNDRV_PCM_STREAM_PLAYBACK; - cpu_dai->playback_active--; - codec_dai->playback_active--; - } else { + else stream = SNDRV_PCM_STREAM_CAPTURE; - cpu_dai->capture_active--; - codec_dai->capture_active--; - } - cpu_dai->active--; - codec_dai->active--; - fe->codec->active--; + snd_soc_runtime_deactivate(fe, stream); fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index f098c8007cbe..1a9857519d65 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -34,6 +34,66 @@ #define DPCM_MAX_BE_USERS 8 +/** + * snd_soc_runtime_activate() - Increment active count for PCM runtime components + * @rtd: ASoC PCM runtime that is activated + * @stream: Direction of the PCM stream + * + * Increments the active count for all the DAIs and components attached to a PCM + * runtime. Should typically be called when a stream is opened. + * + * Must be called with the rtd->pcm_mutex being held + */ +void snd_soc_runtime_activate(struct snd_soc_pcm_runtime *rtd, int stream) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + lockdep_assert_held(&rtd->pcm_mutex); + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + cpu_dai->playback_active++; + codec_dai->playback_active++; + } else { + cpu_dai->capture_active++; + codec_dai->capture_active++; + } + + cpu_dai->active++; + codec_dai->active++; + rtd->codec->active++; +} + +/** + * snd_soc_runtime_deactivate() - Decrement active count for PCM runtime components + * @rtd: ASoC PCM runtime that is deactivated + * @stream: Direction of the PCM stream + * + * Decrements the active count for all the DAIs and components attached to a PCM + * runtime. Should typically be called when a stream is closed. + * + * Must be called with the rtd->pcm_mutex being held + */ +void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + lockdep_assert_held(&rtd->pcm_mutex); + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + cpu_dai->playback_active--; + codec_dai->playback_active--; + } else { + cpu_dai->capture_active--; + codec_dai->capture_active--; + } + + cpu_dai->active--; + codec_dai->active--; + rtd->codec->active--; +} + /** * snd_soc_runtime_ignore_pmdown_time() - Check whether to ignore the power down delay * @rtd: The ASoC PCM runtime that should be checked. @@ -402,16 +462,9 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) runtime->hw.rate_max); dynamic: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - cpu_dai->playback_active++; - codec_dai->playback_active++; - } else { - cpu_dai->capture_active++; - codec_dai->capture_active++; - } - cpu_dai->active++; - codec_dai->active++; - rtd->codec->active++; + + snd_soc_runtime_activate(rtd, substream->stream); + mutex_unlock(&rtd->pcm_mutex); return 0; @@ -483,21 +536,10 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) struct snd_soc_platform *platform = rtd->platform; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_codec *codec = rtd->codec; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - cpu_dai->playback_active--; - codec_dai->playback_active--; - } else { - cpu_dai->capture_active--; - codec_dai->capture_active--; - } - - cpu_dai->active--; - codec_dai->active--; - codec->active--; + snd_soc_runtime_deactivate(rtd, substream->stream); /* clear the corresponding DAIs rate when inactive */ if (!cpu_dai->active) -- cgit v1.2.3 From 5c898e74d135a23ce12e0263c1a3c78eeae1b52b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 5 Mar 2014 13:17:45 +0100 Subject: ASoC: Add helper function to check whether a CODEC is active Instead of directly checking the 'active' field of the CODEC struct add a new helper function that will return either true or false depending on whether the CODEC is active. This will make the migration to the component level easier. The patch also updates all CODEC drivers that check the active attribute to use the new helper function. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 5 +++++ sound/soc/codecs/adav80x.c | 4 ++-- sound/soc/codecs/tlv320aic23.c | 2 +- sound/soc/codecs/tlv320dac33.c | 2 +- sound/soc/codecs/uda1380.c | 2 +- sound/soc/codecs/wl1273.c | 2 +- sound/soc/codecs/wm8711.c | 2 +- sound/soc/codecs/wm8753.c | 4 ++-- 8 files changed, 14 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 53d15e0e6e89..5c2b4f4b5cfa 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1172,6 +1172,11 @@ static inline bool snd_soc_volsw_is_stereo(struct soc_mixer_control *mc) return 1; } +static inline bool snd_soc_codec_is_active(struct snd_soc_codec *codec) +{ + return codec->active != 0; +} + int snd_soc_util_init(void); void snd_soc_util_exit(void); diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c index f78b27a7c461..d50cf5b29a27 100644 --- a/sound/soc/codecs/adav80x.c +++ b/sound/soc/codecs/adav80x.c @@ -722,7 +722,7 @@ static int adav80x_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = dai->codec; struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); - if (!codec->active || !adav80x->rate) + if (!snd_soc_codec_is_active(codec) || !adav80x->rate) return 0; return snd_pcm_hw_constraint_minmax(substream->runtime, @@ -735,7 +735,7 @@ static void adav80x_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = dai->codec; struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); - if (!codec->active) + if (!snd_soc_codec_is_active(codec)) adav80x->rate = 0; } diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c index 5d430cc56f51..458a6aed203e 100644 --- a/sound/soc/codecs/tlv320aic23.c +++ b/sound/soc/codecs/tlv320aic23.c @@ -400,7 +400,7 @@ static void tlv320aic23_shutdown(struct snd_pcm_substream *substream, struct aic23 *aic23 = snd_soc_codec_get_drvdata(codec); /* deactivate */ - if (!codec->active) { + if (!snd_soc_codec_is_active(codec)) { udelay(50); snd_soc_write(codec, TLV320AIC23_ACTIVE, 0x0); } diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 4f358393d6d6..35b2d244e42e 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -461,7 +461,7 @@ static int dac33_set_fifo_mode(struct snd_kcontrol *kcontrol, if (dac33->fifo_mode == ucontrol->value.integer.value[0]) return 0; /* Do not allow changes while stream is running*/ - if (codec->active) + if (snd_soc_codec_is_active(codec)) return -EPERM; if (ucontrol->value.integer.value[0] < 0 || diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index 726df6d43c2b..8e3940dcff20 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -108,7 +108,7 @@ static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg, /* the interpolator & decimator regs must only be written when the * codec DAI is active. */ - if (!codec->active && (reg >= UDA1380_MVOL)) + if (!snd_soc_codec_is_active(codec) && (reg >= UDA1380_MVOL)) return 0; pr_debug("uda1380: hw write %x val %x\n", reg, value); if (codec->hw_write(codec->control_data, data, 3) == 3) { diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c index b7ab2ef567c8..47e96ff30064 100644 --- a/sound/soc/codecs/wl1273.c +++ b/sound/soc/codecs/wl1273.c @@ -197,7 +197,7 @@ static int snd_wl1273_set_audio_route(struct snd_kcontrol *kcontrol, return 0; /* Do not allow changes while stream is running */ - if (codec->active) + if (snd_soc_codec_is_active(codec)) return -EPERM; if (ucontrol->value.integer.value[0] < 0 || diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index d99f948c513c..6efcc40a7cb3 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -201,7 +201,7 @@ static void wm8711_shutdown(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = dai->codec; /* deactivate */ - if (!codec->active) { + if (!snd_soc_codec_is_active(codec)) { udelay(50); snd_soc_write(codec, WM8711_ACTIVE, 0x0); } diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index be85da93a268..5cf4bebc5d89 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -251,7 +251,7 @@ static int wm8753_set_dai(struct snd_kcontrol *kcontrol, if (wm8753->dai_func == ucontrol->value.integer.value[0]) return 0; - if (codec->active) + if (snd_soc_codec_is_active(codec)) return -EBUSY; ioctl = snd_soc_read(codec, WM8753_IOCTL); @@ -1314,7 +1314,7 @@ static int wm8753_mute(struct snd_soc_dai *dai, int mute) /* the digital mute covers the HiFi and Voice DAC's on the WM8753. * make sure we check if they are not both active when we mute */ if (mute && wm8753->dai_func == 1) { - if (!codec->active) + if (!snd_soc_codec_is_active(codec)) snd_soc_write(codec, WM8753_DAC, mute_reg | 0x8); } else { if (mute) -- cgit v1.2.3 From 6106d12947d1b05dc15ca3933eb514347d6ed726 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 5 Mar 2014 13:17:46 +0100 Subject: ASoC: Add component pointer to the DAI struct Keep track of which component registered a DAI. We'll need this as componentization progresses. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dai.h | 1 + sound/soc/soc-core.c | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 71f27c403194..8763e539c487 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -270,6 +270,7 @@ struct snd_soc_dai { /* parent platform/codec */ struct snd_soc_platform *platform; struct snd_soc_codec *codec; + struct snd_soc_component *component; struct snd_soc_card *card; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index fe1df50805a3..6401e97b2e68 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3884,11 +3884,13 @@ static inline char *fmt_multiple_name(struct device *dev, /** * snd_soc_register_dai - Register a DAI with the ASoC core * - * @dai: DAI to register + * @component: The component the DAIs are registered for + * @dai_drv: DAI driver to use for the DAIs */ -static int snd_soc_register_dai(struct device *dev, +static int snd_soc_register_dai(struct snd_soc_component *component, struct snd_soc_dai_driver *dai_drv) { + struct device *dev = component->dev; struct snd_soc_codec *codec; struct snd_soc_dai *dai; @@ -3905,6 +3907,7 @@ static int snd_soc_register_dai(struct device *dev, return -ENOMEM; } + dai->component = component; dai->dev = dev; dai->driver = dai_drv; dai->dapm.dev = dev; @@ -3962,12 +3965,14 @@ found: /** * snd_soc_register_dais - Register multiple DAIs with the ASoC core * - * @dai: Array of DAIs to register + * @component: The component the DAIs are registered for + * @dai_drv: DAI driver to use for the DAIs * @count: Number of DAIs */ -static int snd_soc_register_dais(struct device *dev, +static int snd_soc_register_dais(struct snd_soc_component *component, struct snd_soc_dai_driver *dai_drv, size_t count) { + struct device *dev = component->dev; struct snd_soc_codec *codec; struct snd_soc_dai *dai; int i, ret = 0; @@ -3990,6 +3995,7 @@ static int snd_soc_register_dais(struct device *dev, goto err; } + dai->component = component; dai->dev = dev; dai->driver = &dai_drv[i]; if (dai->driver->id) @@ -4086,9 +4092,9 @@ __snd_soc_register_component(struct device *dev, * since it had been used snd_soc_register_dais(), */ if ((1 == num_dai) && allow_single_dai) - ret = snd_soc_register_dai(dev, dai_drv); + ret = snd_soc_register_dai(cmpnt, dai_drv); else - ret = snd_soc_register_dais(dev, dai_drv, num_dai); + ret = snd_soc_register_dais(cmpnt, dai_drv, num_dai); if (ret < 0) { dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret); goto error_component_name; -- cgit v1.2.3 From cdde4ccb14b4959bd1c96a07367bf02b746328d3 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 5 Mar 2014 13:17:47 +0100 Subject: ASoC: Move active count from CODEC to component There is no reason why active count tracking should only be done for CODECs but not for other components. Moving the active count from the snd_soc_codec struct to the snd_soc_component struct reduces the differences between CODECs and other components and will eventually allow component to component DAI links (Which is a prerequisite for converting CODECs to components). Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 12 ++++++++++-- sound/soc/soc-pcm.c | 10 ++++------ 2 files changed, 14 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 5c2b4f4b5cfa..0495b4aaeb70 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -660,6 +660,9 @@ struct snd_soc_component { const char *name; int id; struct device *dev; + + unsigned int active; + struct list_head list; struct snd_soc_dai_driver *dai_drv; @@ -687,7 +690,6 @@ struct snd_soc_codec { /* runtime */ struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */ - unsigned int active; unsigned int cache_bypass:1; /* Suppress access to the cache */ unsigned int suspended:1; /* Codec is in suspend PM state */ unsigned int probed:1; /* Codec has been probed */ @@ -1172,9 +1174,15 @@ static inline bool snd_soc_volsw_is_stereo(struct soc_mixer_control *mc) return 1; } +static inline bool snd_soc_component_is_active( + struct snd_soc_component *component) +{ + return component->active != 0; +} + static inline bool snd_soc_codec_is_active(struct snd_soc_codec *codec) { - return codec->active != 0; + return snd_soc_component_is_active(&codec->component); } int snd_soc_util_init(void); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 71a01dda1867..98b46295785d 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -61,9 +61,8 @@ void snd_soc_runtime_activate(struct snd_soc_pcm_runtime *rtd, int stream) cpu_dai->active++; codec_dai->active++; - if (cpu_dai->codec) - cpu_dai->codec->active++; - codec_dai->codec->active++; + cpu_dai->component->active++; + codec_dai->component->active++; } /** @@ -93,9 +92,8 @@ void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream) cpu_dai->active--; codec_dai->active--; - if (cpu_dai->codec) - cpu_dai->codec->active--; - codec_dai->codec->active--; + cpu_dai->component->active--; + codec_dai->component->active--; } /** -- cgit v1.2.3 From 3d59400fe47e7e8bfb024cd1651433bef42e268e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 5 Mar 2014 13:17:48 +0100 Subject: ASoC: Move ignore_pmdown_time from CODEC to component In preparation for componentization move the ignore_pmdown_time field from the snd_soc_codec struct to the snd_soc_component struct. Set it to true for non CODEC components for now. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 3 ++- sound/soc/soc-core.c | 4 +++- sound/soc/soc-pcm.c | 10 ++-------- 3 files changed, 7 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 0495b4aaeb70..b14acd8228ab 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -663,6 +663,8 @@ struct snd_soc_component { unsigned int active; + unsigned int ignore_pmdown_time:1; /* pmdown_time is ignored at stop */ + struct list_head list; struct snd_soc_dai_driver *dai_drv; @@ -715,7 +717,6 @@ struct snd_soc_codec { /* dapm */ struct snd_soc_dapm_context dapm; - unsigned int ignore_pmdown_time:1; /* pmdown_time is ignored at stop */ #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_codec_root; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 6401e97b2e68..18aecd2841a8 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4127,6 +4127,8 @@ int snd_soc_register_component(struct device *dev, return -ENOMEM; } + cmpnt->ignore_pmdown_time = true; + return __snd_soc_register_component(dev, cmpnt, cmpnt_drv, dai_drv, num_dai, true); } @@ -4325,7 +4327,7 @@ int snd_soc_register_codec(struct device *dev, codec->volatile_register = codec_drv->volatile_register; codec->readable_register = codec_drv->readable_register; codec->writable_register = codec_drv->writable_register; - codec->ignore_pmdown_time = codec_drv->ignore_pmdown_time; + codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time; codec->dapm.bias_level = SND_SOC_BIAS_OFF; codec->dapm.dev = dev; codec->dapm.codec = codec; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 98b46295785d..2cedf09f6d96 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -107,17 +107,11 @@ void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream) */ bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd) { - bool ignore = true; - if (!rtd->pmdown_time || rtd->dai_link->ignore_pmdown_time) return true; - if (rtd->cpu_dai->codec) - ignore &= rtd->cpu_dai->codec->ignore_pmdown_time; - - ignore &= rtd->codec_dai->codec->ignore_pmdown_time; - - return ignore; + return rtd->cpu_dai->component->ignore_pmdown_time && + rtd->codec_dai->component->ignore_pmdown_time; } /** -- cgit v1.2.3 From 13ff50c85846338bb9820abd3933227b678dc086 Mon Sep 17 00:00:00 2001 From: Nenghua Cao Date: Wed, 19 Feb 2014 18:44:13 +0800 Subject: regmap: add regmap_parse_val api In some cases, we need regmap's format parse_val function to do be/le translation according to the bus configuration. For example, snd_soc_bytes_put() uses regmap to write/read values, and use cpu_to_be() directly to covert MASK into big endian. This is a defect, and should use regmap's format function to do it according to bus configuration. Signed-off-by: Nenghua Cao Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 12 ++++++++++++ include/linux/regmap.h | 9 +++++++++ 2 files changed, 21 insertions(+) (limited to 'include') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 6a19515f8a45..4b2ed0c9e80d 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -2240,6 +2240,18 @@ int regmap_get_val_bytes(struct regmap *map) } EXPORT_SYMBOL_GPL(regmap_get_val_bytes); +int regmap_parse_val(struct regmap *map, const void *buf, + unsigned int *val) +{ + if (!map->format.parse_val) + return -EINVAL; + + *val = map->format.parse_val(buf); + + return 0; +} +EXPORT_SYMBOL_GPL(regmap_parse_val); + static int __init regmap_initcall(void) { regmap_debugfs_initcall(); diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 4149f1a9b003..3e1a2e4a92ad 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -423,6 +423,8 @@ bool regmap_check_range_table(struct regmap *map, unsigned int reg, int regmap_register_patch(struct regmap *map, const struct reg_default *regs, int num_regs); +int regmap_parse_val(struct regmap *map, const void *buf, + unsigned int *val); static inline bool regmap_reg_in_range(unsigned int reg, const struct regmap_range *range) @@ -695,6 +697,13 @@ static inline int regmap_register_patch(struct regmap *map, return -EINVAL; } +static inline int regmap_parse_val(struct regmap *map, const void *buf, + unsigned int *val) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + static inline struct regmap *dev_get_regmap(struct device *dev, const char *name) { -- cgit v1.2.3 From 9dd02e89cb920a727349f8852430fdc92cc729f4 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 7 Mar 2014 16:34:24 +0000 Subject: mfd: arizona: Add support for new fratio encoding Newer IP has an expanded encoding for the fratio bits. As the additional used bits are unused on older IP simply expand the field to the new size. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- include/linux/mfd/arizona/registers.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h index fdf3aa376eb2..3ddaa634b19d 100644 --- a/include/linux/mfd/arizona/registers.h +++ b/include/linux/mfd/arizona/registers.h @@ -1702,9 +1702,9 @@ /* * R373 (0x175) - FLL1 Control 5 */ -#define ARIZONA_FLL1_FRATIO_MASK 0x0700 /* FLL1_FRATIO - [10:8] */ -#define ARIZONA_FLL1_FRATIO_SHIFT 8 /* FLL1_FRATIO - [10:8] */ -#define ARIZONA_FLL1_FRATIO_WIDTH 3 /* FLL1_FRATIO - [10:8] */ +#define ARIZONA_FLL1_FRATIO_MASK 0x0F00 /* FLL1_FRATIO - [11:8] */ +#define ARIZONA_FLL1_FRATIO_SHIFT 8 /* FLL1_FRATIO - [11:8] */ +#define ARIZONA_FLL1_FRATIO_WIDTH 4 /* FLL1_FRATIO - [11:8] */ #define ARIZONA_FLL1_OUTDIV_MASK 0x000E /* FLL1_OUTDIV - [3:1] */ #define ARIZONA_FLL1_OUTDIV_SHIFT 1 /* FLL1_OUTDIV - [3:1] */ #define ARIZONA_FLL1_OUTDIV_WIDTH 3 /* FLL1_OUTDIV - [3:1] */ -- cgit v1.2.3 From 1438c2f60ba955114cff3717f1a334878c7886a9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 9 Mar 2014 17:41:47 +0100 Subject: ASoC: Add a per component dai list Now that every DAI has a component we can track the DAIs on a per component basis. This simplifies the DAI lookup when we are only interested in DAIs of a specific component and also makes it possible to have multiple components with the same parent device and also register DAIs. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 2 ++ sound/soc/soc-core.c | 76 +++++++++++++++++++++++----------------------------- 2 files changed, 36 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index b14acd8228ab..37b470c1e127 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -671,6 +671,8 @@ struct snd_soc_component { int num_dai; const struct snd_soc_component_driver *driver; + + struct list_head dai_list; }; /* SoC Audio Codec device */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index cc522418e9c4..f34f1a01fce1 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -56,7 +56,6 @@ EXPORT_SYMBOL_GPL(snd_soc_debugfs_root); #endif static DEFINE_MUTEX(client_mutex); -static LIST_HEAD(dai_list); static LIST_HEAD(platform_list); static LIST_HEAD(codec_list); static LIST_HEAD(component_list); @@ -370,18 +369,22 @@ static ssize_t dai_list_read_file(struct file *file, char __user *user_buf, { char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); ssize_t len, ret = 0; + struct snd_soc_component *component; struct snd_soc_dai *dai; if (!buf) return -ENOMEM; - list_for_each_entry(dai, &dai_list, list) { - len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n", dai->name); - if (len >= 0) - ret += len; - if (ret > PAGE_SIZE) { - ret = PAGE_SIZE; - break; + list_for_each_entry(component, &component_list, list) { + list_for_each_entry(dai, &component->dai_list, list) { + len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n", + dai->name); + if (len >= 0) + ret += len; + if (ret > PAGE_SIZE) { + ret = PAGE_SIZE; + break; + } } } @@ -855,6 +858,7 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num) { struct snd_soc_dai_link *dai_link = &card->dai_link[num]; struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; + struct snd_soc_component *component; struct snd_soc_codec *codec; struct snd_soc_platform *platform; struct snd_soc_dai *codec_dai, *cpu_dai; @@ -863,18 +867,20 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num) dev_dbg(card->dev, "ASoC: binding %s at idx %d\n", dai_link->name, num); /* Find CPU DAI from registered DAIs*/ - list_for_each_entry(cpu_dai, &dai_list, list) { + list_for_each_entry(component, &component_list, list) { if (dai_link->cpu_of_node && - (cpu_dai->dev->of_node != dai_link->cpu_of_node)) + component->dev->of_node != dai_link->cpu_of_node) continue; if (dai_link->cpu_name && - strcmp(dev_name(cpu_dai->dev), dai_link->cpu_name)) - continue; - if (dai_link->cpu_dai_name && - strcmp(cpu_dai->name, dai_link->cpu_dai_name)) + strcmp(dev_name(component->dev), dai_link->cpu_name)) continue; + list_for_each_entry(cpu_dai, &component->dai_list, list) { + if (dai_link->cpu_dai_name && + strcmp(cpu_dai->name, dai_link->cpu_dai_name)) + continue; - rtd->cpu_dai = cpu_dai; + rtd->cpu_dai = cpu_dai; + } } if (!rtd->cpu_dai) { @@ -899,12 +905,10 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num) * CODEC found, so find CODEC DAI from registered DAIs from * this CODEC */ - list_for_each_entry(codec_dai, &dai_list, list) { - if (codec->dev == codec_dai->dev && - !strcmp(codec_dai->name, - dai_link->codec_dai_name)) { - + list_for_each_entry(codec_dai, &codec->component.dai_list, list) { + if (!strcmp(codec_dai->name, dai_link->codec_dai_name)) { rtd->codec_dai = codec_dai; + break; } } @@ -1128,12 +1132,8 @@ static int soc_probe_codec(struct snd_soc_card *card, driver->num_dapm_widgets); /* Create DAPM widgets for each DAI stream */ - list_for_each_entry(dai, &dai_list, list) { - if (dai->dev != codec->dev) - continue; - + list_for_each_entry(dai, &codec->component.dai_list, list) snd_soc_dapm_new_dai_widgets(&codec->dapm, dai); - } codec->dapm.idle_bias_off = driver->idle_bias_off; @@ -1180,6 +1180,7 @@ static int soc_probe_platform(struct snd_soc_card *card, { int ret = 0; const struct snd_soc_platform_driver *driver = platform->driver; + struct snd_soc_component *component; struct snd_soc_dai *dai; platform->card = card; @@ -1195,11 +1196,11 @@ static int soc_probe_platform(struct snd_soc_card *card, driver->dapm_widgets, driver->num_dapm_widgets); /* Create DAPM widgets for each DAI stream */ - list_for_each_entry(dai, &dai_list, list) { - if (dai->dev != platform->dev) + list_for_each_entry(component, &component_list, list) { + if (component->dev != platform->dev) continue; - - snd_soc_dapm_new_dai_widgets(&platform->dapm, dai); + list_for_each_entry(dai, &component->dai_list, list) + snd_soc_dapm_new_dai_widgets(&platform->dapm, dai); } platform->dapm.idle_bias_off = 1; @@ -3912,21 +3913,14 @@ static inline char *fmt_multiple_name(struct device *dev, */ static void snd_soc_unregister_dais(struct snd_soc_component *component) { - struct snd_soc_dai *dai, *_dai; - - mutex_lock(&client_mutex); - list_for_each_entry_safe(dai, _dai, &dai_list, list) { - if (dai->dev != component->dev) - continue; - - list_del(&dai->list); + struct snd_soc_dai *dai; + list_for_each_entry(dai, &component->dai_list, list) { dev_dbg(component->dev, "ASoC: Unregistered DAI '%s'\n", dai->name); kfree(dai->name); kfree(dai); } - mutex_unlock(&client_mutex); } /** @@ -3990,13 +3984,10 @@ static int snd_soc_register_dais(struct snd_soc_component *component, if (!dai->driver->ops) dai->driver->ops = &null_dai_ops; - if (!dai->codec) dai->dapm.idle_bias_off = 1; - mutex_lock(&client_mutex); - list_add(&dai->list, &dai_list); - mutex_unlock(&client_mutex); + list_add(&dai->list, &component->dai_list); dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name); } @@ -4040,6 +4031,7 @@ __snd_soc_register_component(struct device *dev, cmpnt->driver = cmpnt_drv; cmpnt->dai_drv = dai_drv; cmpnt->num_dai = num_dai; + INIT_LIST_HEAD(&cmpnt->dai_list); ret = snd_soc_register_dais(cmpnt, codec, dai_drv, num_dai, allow_single_dai); -- cgit v1.2.3 From 492c0a18b7b4f171b5af382541e72541de69f545 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 6 Mar 2014 16:15:48 +0800 Subject: ASoC: dapm: Staticise dapm_mark_dirty() The function is not called outside soc-dapm.c so there is no need for it to be exported. Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 1 - sound/soc/soc-dapm.c | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 3b991766a8f2..05aaaf689ac0 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -471,7 +471,6 @@ int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, void snd_soc_dapm_auto_nc_codec_pins(struct snd_soc_codec *codec); /* Mostly internal - should not normally be used */ -void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason); void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm); /* dapm path query */ diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 77743e51420a..5a4376b41926 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -146,7 +146,7 @@ static bool dapm_dirty_widget(struct snd_soc_dapm_widget *w) return !list_empty(&w->dirty); } -void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason) +static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason) { if (!dapm_dirty_widget(w)) { dev_vdbg(w->dapm->dev, "Marking %s dirty due to %s\n", @@ -154,7 +154,6 @@ void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason) list_add_tail(&w->dirty, &w->dapm->card->dapm_dirty); } } -EXPORT_SYMBOL_GPL(dapm_mark_dirty); void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm) { -- cgit v1.2.3