diff --git a/Documentation/devicetree/bindings/sound/ak4613.txt b/Documentation/devicetree/bindings/sound/ak4613.txt new file mode 100644 index 000000000000..15a919522b42 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ak4613.txt @@ -0,0 +1,17 @@ +AK4613 I2C transmitter + +This device supports I2C mode only. + +Required properties: + +- compatible : "asahi-kasei,ak4613" +- reg : The chip select number on the I2C bus + +Example: + +&i2c { + ak4613: ak4613@0x10 { + compatible = "asahi-kasei,ak4613"; + reg = <0x10>; + }; +}; diff --git a/Documentation/devicetree/bindings/sound/ak4642.txt b/Documentation/devicetree/bindings/sound/ak4642.txt index 623d4e70ae11..340784db6808 100644 --- a/Documentation/devicetree/bindings/sound/ak4642.txt +++ b/Documentation/devicetree/bindings/sound/ak4642.txt @@ -7,7 +7,14 @@ Required properties: - compatible : "asahi-kasei,ak4642" or "asahi-kasei,ak4643" or "asahi-kasei,ak4648" - reg : The chip select number on the I2C bus -Example: +Optional properties: + + - #clock-cells : common clock binding; shall be set to 0 + - clocks : common clock binding; MCKI clock + - clock-frequency : common clock binding; frequency of MCKO + - clock-output-names : common clock binding; MCKO clock name + +Example 1: &i2c { ak4648: ak4648@0x12 { @@ -15,3 +22,16 @@ Example: reg = <0x12>; }; }; + +Example 2: + +&i2c { + ak4643: codec@12 { + compatible = "asahi-kasei,ak4643"; + reg = <0x12>; + #clock-cells = <0>; + clocks = <&audio_clock>; + clock-frequency = <12288000>; + clock-output-names = "ak4643_mcko"; + }; +}; diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index bf9ed380bb1c..1cc1f1e0d17f 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -871,14 +871,7 @@ static int atmel_spi_set_xfer_speed(struct atmel_spi *as, * Calculate the lowest divider that satisfies the * constraint, assuming div32/fdiv/mbz == 0. */ - if (xfer->speed_hz) - scbr = DIV_ROUND_UP(bus_hz, xfer->speed_hz); - else - /* - * This can happend if max_speed is null. - * In this case, we set the lowest possible speed - */ - scbr = 0xff; + scbr = DIV_ROUND_UP(bus_hz, xfer->speed_hz); /* * If the resulting divider doesn't fit into the @@ -1300,14 +1293,12 @@ static int atmel_spi_one_transfer(struct spi_master *master, return -EINVAL; } - if (xfer->bits_per_word) { - asd = spi->controller_state; - bits = (asd->csr >> 4) & 0xf; - if (bits != xfer->bits_per_word - 8) { - dev_dbg(&spi->dev, + asd = spi->controller_state; + bits = (asd->csr >> 4) & 0xf; + if (bits != xfer->bits_per_word - 8) { + dev_dbg(&spi->dev, "you can't yet change bits_per_word in transfers\n"); - return -ENOPROTOOPT; - } + return -ENOPROTOOPT; } /* diff --git a/include/sound/soc.h b/include/sound/soc.h index 884e728b09d9..9ffa28514fa7 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -226,6 +226,18 @@ .info = snd_soc_info_volsw, \ .get = xhandler_get, .put = xhandler_put, \ .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, 0) } +#define SOC_SINGLE_RANGE_EXT_TLV(xname, xreg, xshift, xmin, xmax, xinvert, \ + xhandler_get, xhandler_put, 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_range, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(struct soc_mixer_control) \ + {.reg = xreg, .rreg = xreg, .shift = xshift, \ + .rshift = xshift, .min = xmin, .max = xmax, \ + .platform_max = xmax, .invert = xinvert} } #define SOC_DOUBLE_EXT_TLV(xname, xreg, shift_left, shift_right, xmax, xinvert,\ xhandler_get, xhandler_put, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c index aa354e1c6ff7..1933bcd46cca 100644 --- a/sound/soc/atmel/atmel_wm8904.c +++ b/sound/soc/atmel/atmel_wm8904.c @@ -176,6 +176,7 @@ static const struct of_device_id atmel_asoc_wm8904_dt_ids[] = { { .compatible = "atmel,asoc-wm8904", }, { } }; +MODULE_DEVICE_TABLE(of, atmel_asoc_wm8904_dt_ids); #endif static struct platform_driver atmel_asoc_wm8904_driver = { diff --git a/sound/soc/au1x/db1000.c b/sound/soc/au1x/db1000.c index 452f404abfd2..e97c32798e98 100644 --- a/sound/soc/au1x/db1000.c +++ b/sound/soc/au1x/db1000.c @@ -38,14 +38,7 @@ static int db1000_audio_probe(struct platform_device *pdev) { struct snd_soc_card *card = &db1000_ac97; card->dev = &pdev->dev; - return snd_soc_register_card(card); -} - -static int db1000_audio_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - snd_soc_unregister_card(card); - return 0; + return devm_snd_soc_register_card(&pdev->dev, card); } static struct platform_driver db1000_audio_driver = { @@ -54,7 +47,6 @@ static struct platform_driver db1000_audio_driver = { .pm = &snd_soc_pm_ops, }, .probe = db1000_audio_probe, - .remove = db1000_audio_remove, }; module_platform_driver(db1000_audio_driver); diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c index 58c3164802b8..638ca0ba7e6e 100644 --- a/sound/soc/au1x/db1200.c +++ b/sound/soc/au1x/db1200.c @@ -174,14 +174,7 @@ static int db1200_audio_probe(struct platform_device *pdev) card = db1200_cards[pid->driver_data]; card->dev = &pdev->dev; - return snd_soc_register_card(card); -} - -static int db1200_audio_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - snd_soc_unregister_card(card); - return 0; + return devm_snd_soc_register_card(&pdev->dev, card); } static struct platform_driver db1200_audio_driver = { @@ -191,7 +184,6 @@ static struct platform_driver db1200_audio_driver = { }, .id_table = db1200_pids, .probe = db1200_audio_probe, - .remove = db1200_audio_remove, }; module_platform_driver(db1200_audio_driver); diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 0c9733ecd17f..a92e4d4b2eee 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -36,6 +36,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_AK4104 if SPI_MASTER select SND_SOC_AK4535 if I2C select SND_SOC_AK4554 + select SND_SOC_AK4613 if I2C select SND_SOC_AK4641 if I2C select SND_SOC_AK4642 if I2C select SND_SOC_AK4671 if I2C @@ -319,6 +320,10 @@ config SND_SOC_AK4535 config SND_SOC_AK4554 tristate "AKM AK4554 CODEC" +config SND_SOC_AK4613 + tristate "AKM AK4613 CODEC" + depends on I2C + config SND_SOC_AK4641 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 4a32077954ae..5b6c8af38a39 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -26,6 +26,7 @@ snd-soc-ads117x-objs := ads117x.o snd-soc-ak4104-objs := ak4104.o snd-soc-ak4535-objs := ak4535.o snd-soc-ak4554-objs := ak4554.o +snd-soc-ak4613-objs := ak4613.o snd-soc-ak4641-objs := ak4641.o snd-soc-ak4642-objs := ak4642.o snd-soc-ak4671-objs := ak4671.o @@ -216,6 +217,7 @@ obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o obj-$(CONFIG_SND_SOC_AK4554) += snd-soc-ak4554.o +obj-$(CONFIG_SND_SOC_AK4613) += snd-soc-ak4613.o obj-$(CONFIG_SND_SOC_AK4641) += snd-soc-ak4641.o obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c new file mode 100644 index 000000000000..07a266460ec3 --- /dev/null +++ b/sound/soc/codecs/ak4613.c @@ -0,0 +1,497 @@ +/* + * ak4613.c -- Asahi Kasei ALSA Soc Audio driver + * + * Copyright (C) 2015 Renesas Electronics Corporation + * Kuninori Morimoto + * + * Based on ak4642.c by Kuninori Morimoto + * Based on wm8731.c by Richard Purdie + * Based on ak4535.c by Richard Purdie + * Based on wm8753.c by Liam Girdwood + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define PW_MGMT1 0x00 /* Power Management 1 */ +#define PW_MGMT2 0x01 /* Power Management 2 */ +#define PW_MGMT3 0x02 /* Power Management 3 */ +#define CTRL1 0x03 /* Control 1 */ +#define CTRL2 0x04 /* Control 2 */ +#define DEMP1 0x05 /* De-emphasis1 */ +#define DEMP2 0x06 /* De-emphasis2 */ +#define OFD 0x07 /* Overflow Detect */ +#define ZRD 0x08 /* Zero Detect */ +#define ICTRL 0x09 /* Input Control */ +#define OCTRL 0x0a /* Output Control */ +#define LOUT1 0x0b /* LOUT1 Volume Control */ +#define ROUT1 0x0c /* ROUT1 Volume Control */ +#define LOUT2 0x0d /* LOUT2 Volume Control */ +#define ROUT2 0x0e /* ROUT2 Volume Control */ +#define LOUT3 0x0f /* LOUT3 Volume Control */ +#define ROUT3 0x10 /* ROUT3 Volume Control */ +#define LOUT4 0x11 /* LOUT4 Volume Control */ +#define ROUT4 0x12 /* ROUT4 Volume Control */ +#define LOUT5 0x13 /* LOUT5 Volume Control */ +#define ROUT5 0x14 /* ROUT5 Volume Control */ +#define LOUT6 0x15 /* LOUT6 Volume Control */ +#define ROUT6 0x16 /* ROUT6 Volume Control */ + +/* PW_MGMT1 */ +#define RSTN BIT(0) +#define PMDAC BIT(1) +#define PMADC BIT(2) +#define PMVR BIT(3) + +/* PW_MGMT2 */ +#define PMAD_ALL 0x7 + +/* PW_MGMT3 */ +#define PMDA_ALL 0x3f + +/* CTRL1 */ +#define DIF0 BIT(3) +#define DIF1 BIT(4) +#define DIF2 BIT(5) +#define TDM0 BIT(6) +#define TDM1 BIT(7) +#define NO_FMT (0xff) +#define FMT_MASK (0xf8) + +/* CTRL2 */ +#define DFS_NORMAL_SPEED (0 << 2) +#define DFS_DOUBLE_SPEED (1 << 2) +#define DFS_QUAD_SPEED (2 << 2) + +struct ak4613_priv { + struct mutex lock; + + unsigned int fmt; + u8 fmt_ctrl; + int cnt; +}; + +struct ak4613_formats { + unsigned int width; + unsigned int fmt; +}; + +struct ak4613_interface { + struct ak4613_formats capture; + struct ak4613_formats playback; +}; + +/* + * Playback Volume + * + * max : 0x00 : 0 dB + * ( 0.5 dB step ) + * min : 0xFE : -127.0 dB + * mute: 0xFF + */ +static const DECLARE_TLV_DB_SCALE(out_tlv, -12750, 50, 1); + +static const struct snd_kcontrol_new ak4613_snd_controls[] = { + SOC_DOUBLE_R_TLV("Digital Playback Volume1", LOUT1, ROUT1, + 0, 0xFF, 1, out_tlv), + SOC_DOUBLE_R_TLV("Digital Playback Volume2", LOUT2, ROUT2, + 0, 0xFF, 1, out_tlv), + SOC_DOUBLE_R_TLV("Digital Playback Volume3", LOUT3, ROUT3, + 0, 0xFF, 1, out_tlv), + SOC_DOUBLE_R_TLV("Digital Playback Volume4", LOUT4, ROUT4, + 0, 0xFF, 1, out_tlv), + SOC_DOUBLE_R_TLV("Digital Playback Volume5", LOUT5, ROUT5, + 0, 0xFF, 1, out_tlv), + SOC_DOUBLE_R_TLV("Digital Playback Volume6", LOUT6, ROUT6, + 0, 0xFF, 1, out_tlv), +}; + +static const struct reg_default ak4613_reg[] = { + { 0x0, 0x0f }, { 0x1, 0x07 }, { 0x2, 0x3f }, { 0x3, 0x20 }, + { 0x4, 0x20 }, { 0x5, 0x55 }, { 0x6, 0x05 }, { 0x7, 0x07 }, + { 0x8, 0x0f }, { 0x9, 0x07 }, { 0xa, 0x3f }, { 0xb, 0x00 }, + { 0xc, 0x00 }, { 0xd, 0x00 }, { 0xe, 0x00 }, { 0xf, 0x00 }, + { 0x10, 0x00 }, { 0x11, 0x00 }, { 0x12, 0x00 }, { 0x13, 0x00 }, + { 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0x00 }, +}; + +#define AUDIO_IFACE_IDX_TO_VAL(i) (i << 3) +#define AUDIO_IFACE(b, fmt) { b, SND_SOC_DAIFMT_##fmt } +static const struct ak4613_interface ak4613_iface[] = { + /* capture */ /* playback */ + [0] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(16, RIGHT_J) }, + [1] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(20, RIGHT_J) }, + [2] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(24, RIGHT_J) }, + [3] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(24, LEFT_J) }, + [4] = { AUDIO_IFACE(24, I2S), AUDIO_IFACE(24, I2S) }, +}; + +static const struct regmap_config ak4613_regmap_cfg = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x16, + .reg_defaults = ak4613_reg, + .num_reg_defaults = ARRAY_SIZE(ak4613_reg), +}; + +static const struct of_device_id ak4613_of_match[] = { + { .compatible = "asahi-kasei,ak4613", .data = &ak4613_regmap_cfg }, + {}, +}; +MODULE_DEVICE_TABLE(of, ak4613_of_match); + +static const struct i2c_device_id ak4613_i2c_id[] = { + { "ak4613", (kernel_ulong_t)&ak4613_regmap_cfg }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ak4613_i2c_id); + +static const struct snd_soc_dapm_widget ak4613_dapm_widgets[] = { + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("LOUT1"), + SND_SOC_DAPM_OUTPUT("LOUT2"), + SND_SOC_DAPM_OUTPUT("LOUT3"), + SND_SOC_DAPM_OUTPUT("LOUT4"), + SND_SOC_DAPM_OUTPUT("LOUT5"), + SND_SOC_DAPM_OUTPUT("LOUT6"), + + SND_SOC_DAPM_OUTPUT("ROUT1"), + SND_SOC_DAPM_OUTPUT("ROUT2"), + SND_SOC_DAPM_OUTPUT("ROUT3"), + SND_SOC_DAPM_OUTPUT("ROUT4"), + SND_SOC_DAPM_OUTPUT("ROUT5"), + SND_SOC_DAPM_OUTPUT("ROUT6"), + + /* Inputs */ + SND_SOC_DAPM_INPUT("LIN1"), + SND_SOC_DAPM_INPUT("LIN2"), + + SND_SOC_DAPM_INPUT("RIN1"), + SND_SOC_DAPM_INPUT("RIN2"), + + /* DAC */ + SND_SOC_DAPM_DAC("DAC1", NULL, PW_MGMT3, 0, 0), + SND_SOC_DAPM_DAC("DAC2", NULL, PW_MGMT3, 1, 0), + SND_SOC_DAPM_DAC("DAC3", NULL, PW_MGMT3, 2, 0), + SND_SOC_DAPM_DAC("DAC4", NULL, PW_MGMT3, 3, 0), + SND_SOC_DAPM_DAC("DAC5", NULL, PW_MGMT3, 4, 0), + SND_SOC_DAPM_DAC("DAC6", NULL, PW_MGMT3, 5, 0), + + /* ADC */ + SND_SOC_DAPM_ADC("ADC1", NULL, PW_MGMT2, 0, 0), + SND_SOC_DAPM_ADC("ADC2", NULL, PW_MGMT2, 1, 0), +}; + +static const struct snd_soc_dapm_route ak4613_intercon[] = { + {"LOUT1", NULL, "DAC1"}, + {"LOUT2", NULL, "DAC2"}, + {"LOUT3", NULL, "DAC3"}, + {"LOUT4", NULL, "DAC4"}, + {"LOUT5", NULL, "DAC5"}, + {"LOUT6", NULL, "DAC6"}, + + {"ROUT1", NULL, "DAC1"}, + {"ROUT2", NULL, "DAC2"}, + {"ROUT3", NULL, "DAC3"}, + {"ROUT4", NULL, "DAC4"}, + {"ROUT5", NULL, "DAC5"}, + {"ROUT6", NULL, "DAC6"}, + + {"DAC1", NULL, "Playback"}, + {"DAC2", NULL, "Playback"}, + {"DAC3", NULL, "Playback"}, + {"DAC4", NULL, "Playback"}, + {"DAC5", NULL, "Playback"}, + {"DAC6", NULL, "Playback"}, + + {"Capture", NULL, "ADC1"}, + {"Capture", NULL, "ADC2"}, + + {"ADC1", NULL, "LIN1"}, + {"ADC2", NULL, "LIN2"}, + + {"ADC1", NULL, "RIN1"}, + {"ADC2", NULL, "RIN2"}, +}; + +static void ak4613_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec); + struct device *dev = codec->dev; + + mutex_lock(&priv->lock); + priv->cnt--; + if (priv->cnt < 0) { + dev_err(dev, "unexpected counter error\n"); + priv->cnt = 0; + } + if (!priv->cnt) + priv->fmt_ctrl = NO_FMT; + mutex_unlock(&priv->lock); +} + +static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec); + + fmt &= SND_SOC_DAIFMT_FORMAT_MASK; + + switch (fmt) { + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_I2S: + priv->fmt = fmt; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ak4613_dai_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 ak4613_priv *priv = snd_soc_codec_get_drvdata(codec); + const struct ak4613_formats *fmts; + struct device *dev = codec->dev; + unsigned int width = params_width(params); + unsigned int fmt = priv->fmt; + unsigned int rate; + int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + int i, ret; + u8 fmt_ctrl, ctrl2; + + rate = params_rate(params); + switch (rate) { + case 32000: + case 44100: + case 48000: + ctrl2 = DFS_NORMAL_SPEED; + break; + case 88200: + case 96000: + ctrl2 = DFS_DOUBLE_SPEED; + break; + case 176400: + case 192000: + ctrl2 = DFS_QUAD_SPEED; + break; + default: + return -EINVAL; + } + + /* + * FIXME + * + * It doesn't support TDM at this point + */ + fmt_ctrl = NO_FMT; + for (i = 0; i < ARRAY_SIZE(ak4613_iface); i++) { + fmts = (is_play) ? &ak4613_iface[i].playback : + &ak4613_iface[i].capture; + + if (fmts->fmt != fmt) + continue; + + if (fmt == SND_SOC_DAIFMT_RIGHT_J) { + if (fmts->width != width) + continue; + } else { + if (fmts->width < width) + continue; + } + + fmt_ctrl = AUDIO_IFACE_IDX_TO_VAL(i); + break; + } + + ret = -EINVAL; + if (fmt_ctrl == NO_FMT) + goto hw_params_end; + + mutex_lock(&priv->lock); + if ((priv->fmt_ctrl == NO_FMT) || + (priv->fmt_ctrl == fmt_ctrl)) { + priv->fmt_ctrl = fmt_ctrl; + priv->cnt++; + ret = 0; + } + mutex_unlock(&priv->lock); + + if (ret < 0) + goto hw_params_end; + + snd_soc_update_bits(codec, CTRL1, FMT_MASK, fmt_ctrl); + snd_soc_write(codec, CTRL2, ctrl2); + +hw_params_end: + if (ret < 0) + dev_warn(dev, "unsupported data width/format combination\n"); + + return ret; +} + +static int ak4613_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + u8 mgmt1 = 0; + + switch (level) { + case SND_SOC_BIAS_ON: + mgmt1 |= RSTN; + /* fall through */ + case SND_SOC_BIAS_PREPARE: + mgmt1 |= PMADC | PMDAC; + /* fall through */ + case SND_SOC_BIAS_STANDBY: + mgmt1 |= PMVR; + /* fall through */ + case SND_SOC_BIAS_OFF: + default: + break; + } + + snd_soc_write(codec, PW_MGMT1, mgmt1); + + return 0; +} + +static const struct snd_soc_dai_ops ak4613_dai_ops = { + .shutdown = ak4613_dai_shutdown, + .set_fmt = ak4613_dai_set_fmt, + .hw_params = ak4613_dai_hw_params, +}; + +#define AK4613_PCM_RATE (SNDRV_PCM_RATE_32000 |\ + SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_64000 |\ + SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000) +#define AK4613_PCM_FMTBIT (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_driver ak4613_dai = { + .name = "ak4613-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = AK4613_PCM_RATE, + .formats = AK4613_PCM_FMTBIT, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = AK4613_PCM_RATE, + .formats = AK4613_PCM_FMTBIT, + }, + .ops = &ak4613_dai_ops, + .symmetric_rates = 1, +}; + +static int ak4613_resume(struct snd_soc_codec *codec) +{ + struct regmap *regmap = dev_get_regmap(codec->dev, NULL); + + regcache_mark_dirty(regmap); + return regcache_sync(regmap); +} + +static struct snd_soc_codec_driver soc_codec_dev_ak4613 = { + .resume = ak4613_resume, + .set_bias_level = ak4613_set_bias_level, + .controls = ak4613_snd_controls, + .num_controls = ARRAY_SIZE(ak4613_snd_controls), + .dapm_widgets = ak4613_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4613_dapm_widgets), + .dapm_routes = ak4613_intercon, + .num_dapm_routes = ARRAY_SIZE(ak4613_intercon), +}; + +static int ak4613_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct device *dev = &i2c->dev; + struct device_node *np = dev->of_node; + const struct regmap_config *regmap_cfg; + struct regmap *regmap; + struct ak4613_priv *priv; + + regmap_cfg = NULL; + if (np) { + const struct of_device_id *of_id; + + of_id = of_match_device(ak4613_of_match, dev); + if (of_id) + regmap_cfg = of_id->data; + } else { + regmap_cfg = (const struct regmap_config *)id->driver_data; + } + + if (!regmap_cfg) + return -EINVAL; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->fmt_ctrl = NO_FMT; + priv->cnt = 0; + + mutex_init(&priv->lock); + + i2c_set_clientdata(i2c, priv); + + regmap = devm_regmap_init_i2c(i2c, regmap_cfg); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return snd_soc_register_codec(dev, &soc_codec_dev_ak4613, + &ak4613_dai, 1); +} + +static int ak4613_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static struct i2c_driver ak4613_i2c_driver = { + .driver = { + .name = "ak4613-codec", + .owner = THIS_MODULE, + .of_match_table = ak4613_of_match, + }, + .probe = ak4613_i2c_probe, + .remove = ak4613_i2c_remove, + .id_table = ak4613_i2c_id, +}; + +module_i2c_driver(ak4613_i2c_driver); + +MODULE_DESCRIPTION("Soc AK4613 driver"); +MODULE_AUTHOR("Kuninori Morimoto "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 4a90143d0e90..cda27c22812a 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -23,6 +23,8 @@ * AK4648 is tested. */ +#include +#include #include #include #include @@ -128,11 +130,8 @@ #define I2S (3 << 0) /* MD_CTL2 */ -#define FS0 (1 << 0) -#define FS1 (1 << 1) -#define FS2 (1 << 2) -#define FS3 (1 << 5) -#define FS_MASK (FS0 | FS1 | FS2 | FS3) +#define FSs(val) (((val & 0x7) << 0) | ((val & 0x8) << 2)) +#define PSs(val) ((val & 0x3) << 6) /* MD_CTL3 */ #define BST1 (1 << 3) @@ -147,6 +146,7 @@ struct ak4642_drvdata { struct ak4642_priv { const struct ak4642_drvdata *drvdata; + struct clk *mcko; }; /* @@ -430,56 +430,56 @@ static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } +static int ak4642_set_mcko(struct snd_soc_codec *codec, + u32 frequency) +{ + u32 fs_list[] = { + [0] = 8000, + [1] = 12000, + [2] = 16000, + [3] = 24000, + [4] = 7350, + [5] = 11025, + [6] = 14700, + [7] = 22050, + [10] = 32000, + [11] = 48000, + [14] = 29400, + [15] = 44100, + }; + u32 ps_list[] = { + [0] = 256, + [1] = 128, + [2] = 64, + [3] = 32 + }; + int ps, fs; + + for (ps = 0; ps < ARRAY_SIZE(ps_list); ps++) { + for (fs = 0; fs < ARRAY_SIZE(fs_list); fs++) { + if (frequency == ps_list[ps] * fs_list[fs]) { + snd_soc_write(codec, MD_CTL2, + PSs(ps) | FSs(fs)); + return 0; + } + } + } + + return 0; +} + static int ak4642_dai_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; - u8 rate; + struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec); + u32 rate = clk_get_rate(priv->mcko); - switch (params_rate(params)) { - case 7350: - rate = FS2; - break; - case 8000: - rate = 0; - break; - case 11025: - rate = FS2 | FS0; - break; - case 12000: - rate = FS0; - break; - case 14700: - rate = FS2 | FS1; - break; - case 16000: - rate = FS1; - break; - case 22050: - rate = FS2 | FS1 | FS0; - break; - case 24000: - rate = FS1 | FS0; - break; - case 29400: - rate = FS3 | FS2 | FS1; - break; - case 32000: - rate = FS3 | FS1; - break; - case 44100: - rate = FS3 | FS2 | FS1 | FS0; - break; - case 48000: - rate = FS3 | FS1 | FS0; - break; - default: - return -EINVAL; - } - snd_soc_update_bits(codec, MD_CTL2, FS_MASK, rate); + if (!rate) + rate = params_rate(params) * 256; - return 0; + return ak4642_set_mcko(codec, rate); } static int ak4642_set_bias_level(struct snd_soc_codec *codec, @@ -532,7 +532,18 @@ static int ak4642_resume(struct snd_soc_codec *codec) return 0; } +static int ak4642_probe(struct snd_soc_codec *codec) +{ + struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec); + + if (priv->mcko) + ak4642_set_mcko(codec, clk_get_rate(priv->mcko)); + + return 0; +} + static struct snd_soc_codec_driver soc_codec_dev_ak4642 = { + .probe = ak4642_probe, .resume = ak4642_resume, .set_bias_level = ak4642_set_bias_level, .controls = ak4642_snd_controls, @@ -580,19 +591,54 @@ static const struct ak4642_drvdata ak4648_drvdata = { .extended_frequencies = 1, }; +#ifdef CONFIG_COMMON_CLK +static struct clk *ak4642_of_parse_mcko(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct clk *clk; + const char *clk_name = np->name; + const char *parent_clk_name = NULL; + u32 rate; + + if (of_property_read_u32(np, "clock-frequency", &rate)) + return NULL; + + if (of_property_read_bool(np, "clocks")) + parent_clk_name = of_clk_get_parent_name(np, 0); + + of_property_read_string(np, "clock-output-names", &clk_name); + + clk = clk_register_fixed_rate(dev, clk_name, parent_clk_name, + (parent_clk_name) ? 0 : CLK_IS_ROOT, + rate); + if (!IS_ERR(clk)) + of_clk_add_provider(np, of_clk_src_simple_get, clk); + + return clk; +} +#else +#define ak4642_of_parse_mcko(d) 0 +#endif + static const struct of_device_id ak4642_of_match[]; static int ak4642_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { - struct device_node *np = i2c->dev.of_node; + struct device *dev = &i2c->dev; + struct device_node *np = dev->of_node; const struct ak4642_drvdata *drvdata = NULL; struct regmap *regmap; struct ak4642_priv *priv; + struct clk *mcko = NULL; if (np) { const struct of_device_id *of_id; - of_id = of_match_device(ak4642_of_match, &i2c->dev); + mcko = ak4642_of_parse_mcko(dev); + if (IS_ERR(mcko)) + mcko = NULL; + + of_id = of_match_device(ak4642_of_match, dev); if (of_id) drvdata = of_id->data; } else { @@ -600,15 +646,16 @@ static int ak4642_i2c_probe(struct i2c_client *i2c, } if (!drvdata) { - dev_err(&i2c->dev, "Unknown device type\n"); + dev_err(dev, "Unknown device type\n"); return -EINVAL; } - priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->drvdata = drvdata; + priv->mcko = mcko; i2c_set_clientdata(i2c, priv); @@ -616,7 +663,7 @@ static int ak4642_i2c_probe(struct i2c_client *i2c, if (IS_ERR(regmap)) return PTR_ERR(regmap); - return snd_soc_register_codec(&i2c->dev, + return snd_soc_register_codec(dev, &soc_codec_dev_ak4642, &ak4642_dai, 1); } diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 8a2221ab3d10..ac21b85ff75f 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -147,6 +147,8 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w, 0x4f5, 0x0da); } break; + default: + break; } return 0; @@ -689,6 +691,15 @@ static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena) ARIZONA_IN_VU, val); } +bool arizona_input_analog(struct snd_soc_codec *codec, int shift) +{ + unsigned int reg = ARIZONA_IN1L_CONTROL + ((shift / 2) * 8); + unsigned int val = snd_soc_read(codec, reg); + + return !(val & ARIZONA_IN1_MODE_MASK); +} +EXPORT_SYMBOL_GPL(arizona_input_analog); + int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -725,6 +736,9 @@ int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, reg = snd_soc_read(codec, ARIZONA_INPUT_ENABLES); if (reg == 0) arizona_in_set_vu(codec, 0); + break; + default: + break; } return 0; @@ -806,6 +820,8 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w, break; } break; + default: + break; } return 0; diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index ada0a418ff4b..7b68d05a0939 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -294,4 +294,6 @@ extern int arizona_init_dai(struct arizona_priv *priv, int dai); int arizona_set_output_mode(struct snd_soc_codec *codec, int output, bool diff); +extern bool arizona_input_analog(struct snd_soc_codec *codec, int shift); + #endif diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 9756578fc752..c04c0bc6f58a 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -38,6 +38,12 @@ struct wm5110_priv { struct arizona_priv core; struct arizona_fll fll[2]; + + unsigned int in_value; + int in_pre_pending; + int in_post_pending; + + unsigned int in_pga_cache[6]; }; static const struct wm_adsp_region wm5110_dsp1_regions[] = { @@ -428,6 +434,127 @@ err: return ret; } +static int wm5110_in_pga_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_card *card = dapm->card; + int ret; + + /* + * PGA Volume is also used as part of the enable sequence, so + * usage of it should be avoided whilst that is running. + */ + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + + ret = snd_soc_get_volsw_range(kcontrol, ucontrol); + + mutex_unlock(&card->dapm_mutex); + + return ret; +} + +static int wm5110_in_pga_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_card *card = dapm->card; + int ret; + + /* + * PGA Volume is also used as part of the enable sequence, so + * usage of it should be avoided whilst that is running. + */ + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + + ret = snd_soc_put_volsw_range(kcontrol, ucontrol); + + mutex_unlock(&card->dapm_mutex); + + return ret; +} + +static int wm5110_in_analog_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct wm5110_priv *wm5110 = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->arizona; + unsigned int reg, mask; + struct reg_sequence analog_seq[] = { + { 0x80, 0x3 }, + { 0x35d, 0 }, + { 0x80, 0x0 }, + }; + + reg = ARIZONA_IN1L_CONTROL + ((w->shift ^ 0x1) * 4); + mask = ARIZONA_IN1L_PGA_VOL_MASK; + + switch (event) { + case SND_SOC_DAPM_WILL_PMU: + wm5110->in_value |= 0x3 << ((w->shift ^ 0x1) * 2); + wm5110->in_pre_pending++; + wm5110->in_post_pending++; + return 0; + case SND_SOC_DAPM_PRE_PMU: + wm5110->in_pga_cache[w->shift] = snd_soc_read(codec, reg); + + snd_soc_update_bits(codec, reg, mask, + 0x40 << ARIZONA_IN1L_PGA_VOL_SHIFT); + + wm5110->in_pre_pending--; + if (wm5110->in_pre_pending == 0) { + analog_seq[1].def = wm5110->in_value; + regmap_multi_reg_write_bypassed(arizona->regmap, + analog_seq, + ARRAY_SIZE(analog_seq)); + + msleep(55); + + wm5110->in_value = 0; + } + + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, reg, mask, + wm5110->in_pga_cache[w->shift]); + + wm5110->in_post_pending--; + if (wm5110->in_post_pending == 0) + regmap_multi_reg_write_bypassed(arizona->regmap, + analog_seq, + ARRAY_SIZE(analog_seq)); + break; + default: + break; + } + + return 0; +} + +static int wm5110_in_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->arizona; + + switch (arizona->rev) { + case 0 ... 4: + if (arizona_input_analog(codec, w->shift)) + wm5110_in_analog_ev(w, kcontrol, event); + + break; + default: + break; + } + + return arizona_in_ev(w, kcontrol, event); +} + static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); @@ -454,18 +581,24 @@ SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]), SOC_ENUM("IN3 OSR", arizona_in_dmic_osr[2]), SOC_ENUM("IN4 OSR", arizona_in_dmic_osr[3]), -SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL, - ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL, - ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL, - ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL, - ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL, - ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL, - ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_EXT_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, + wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv), +SOC_SINGLE_RANGE_EXT_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, + wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv), +SOC_SINGLE_RANGE_EXT_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, + wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv), +SOC_SINGLE_RANGE_EXT_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL, + ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, + wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv), +SOC_SINGLE_RANGE_EXT_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL, + ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, + wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv), +SOC_SINGLE_RANGE_EXT_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL, + ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, + wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv), SOC_ENUM("IN HPF Cutoff Frequency", arizona_in_hpf_cut_enum), @@ -896,29 +1029,35 @@ SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"), SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"), SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT, - 0, NULL, 0, arizona_in_ev, + 0, NULL, 0, wm5110_in_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_WILL_PMU), SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT, - 0, NULL, 0, arizona_in_ev, + 0, NULL, 0, wm5110_in_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_WILL_PMU), SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT, - 0, NULL, 0, arizona_in_ev, + 0, NULL, 0, wm5110_in_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_WILL_PMU), SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT, - 0, NULL, 0, arizona_in_ev, + 0, NULL, 0, wm5110_in_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_WILL_PMU), SND_SOC_DAPM_PGA_E("IN3L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3L_ENA_SHIFT, - 0, NULL, 0, arizona_in_ev, + 0, NULL, 0, wm5110_in_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_WILL_PMU), SND_SOC_DAPM_PGA_E("IN3R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3R_ENA_SHIFT, - 0, NULL, 0, arizona_in_ev, + 0, NULL, 0, wm5110_in_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_WILL_PMU), SND_SOC_DAPM_PGA_E("IN4L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN4L_ENA_SHIFT, 0, NULL, 0, arizona_in_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |