diff options
Diffstat (limited to 'sound')
51 files changed, 4503 insertions, 1598 deletions
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 0ef0035..12c6f45 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9863,7 +9863,6 @@ static struct snd_pci_quirk alc882_cfg_tbl[] = { SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD), SND_PCI_QUIRK(0x10f1, 0x2350, "TYAN-S2350", ALC888_6ST_DELL), SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch), - SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P35 DS3R", ALC882_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG), @@ -10700,6 +10699,7 @@ enum { PINFIX_LENOVO_Y530, PINFIX_PB_M5210, PINFIX_ACER_ASPIRE_7736, + PINFIX_GIGABYTE_880GM, }; static const struct alc_fixup alc882_fixups[] = { @@ -10731,6 +10731,13 @@ static const struct alc_fixup alc882_fixups[] = { .type = ALC_FIXUP_SKU, .v.sku = ALC_FIXUP_SKU_IGNORE, }, + [PINFIX_GIGABYTE_880GM] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x14, 0x1114410 }, /* set as speaker */ + { } + } + }, }; static struct snd_pci_quirk alc882_fixup_tbl[] = { @@ -10738,6 +10745,7 @@ static struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", PINFIX_LENOVO_Y530), SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX), SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", PINFIX_ACER_ASPIRE_7736), + SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte", PINFIX_GIGABYTE_880GM), {} }; @@ -18774,8 +18782,6 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = { ALC662_3ST_6ch_DIG), SND_PCI_QUIRK(0x1179, 0xff6e, "Toshiba NB20x", ALC662_AUTO), SND_PCI_QUIRK(0x144d, 0xca00, "Samsung NC10", ALC272_SAMSUNG_NC10), - SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte 945GCM-S2L", - ALC662_3ST_6ch_DIG), SND_PCI_QUIRK(0x152d, 0x2304, "Quanta WH1", ALC663_ASUS_H13), SND_PCI_QUIRK(0x1565, 0x820f, "Biostar TA780G M2+", ALC662_3ST_6ch_DIG), SND_PCI_QUIRK(0x1631, 0xc10c, "PB RS65", ALC663_ASUS_M51VA), @@ -19449,6 +19455,7 @@ enum { ALC662_FIXUP_IDEAPAD, ALC272_FIXUP_MARIO, ALC662_FIXUP_CZC_P10T, + ALC662_FIXUP_GIGABYTE, }; static const struct alc_fixup alc662_fixups[] = { @@ -19477,12 +19484,20 @@ static const struct alc_fixup alc662_fixups[] = { {} } }, + [ALC662_FIXUP_GIGABYTE] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x14, 0x1114410 }, /* set as speaker */ + { } + } + }, }; static struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE), SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE), SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD), + SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte", ALC662_FIXUP_GIGABYTE), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD), SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T), diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c index af3c730..28afbbf 100644 --- a/sound/soc/atmel/sam9g20_wm8731.c +++ b/sound/soc/atmel/sam9g20_wm8731.c @@ -184,7 +184,7 @@ static struct snd_soc_dai_link at91sam9g20ek_dai = { .codec_dai_name = "wm8731-hifi", .init = at91sam9g20ek_wm8731_init, .platform_name = "atmel-pcm-audio", - .codec_name = "wm8731-codec.0-001b", + .codec_name = "wm8731.0-001b", .ops = &at91sam9g20ek_ops, }; diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c index cb99f04..1d3e258 100644 --- a/sound/soc/au1x/db1200.c +++ b/sound/soc/au1x/db1200.c @@ -77,7 +77,7 @@ static struct snd_soc_dai_link db1200_i2s_dai = { .codec_dai_name = "wm8731-hifi", .cpu_dai_name = "au1xpsc_i2s.1", .platform_name = "au1xpsc-pcm.1", - .codec_name = "wm8731-codec.0-001b", + .codec_name = "wm8731.0-001b", .ops = &db1200_i2s_wm8731_ops, }; diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c index 5a2fd8a..98b44b3 100644 --- a/sound/soc/blackfin/bf5xx-ac97-pcm.c +++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c @@ -243,6 +243,9 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream) static int bf5xx_pcm_open(struct snd_pcm_substream *substream) { + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai); struct snd_pcm_runtime *runtime = substream->runtime; int ret; @@ -314,6 +317,9 @@ static struct snd_pcm_ops bf5xx_pcm_ac97_ops = { static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) { + struct snd_soc_pcm_runtime *rtd = pcm->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai); struct snd_pcm_substream *substream = pcm->streams[stream].substream; struct snd_dma_buffer *buf = &substream->dma_buffer; size_t size = bf5xx_pcm_hardware.buffer_bytes_max @@ -377,6 +383,9 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm) struct snd_dma_buffer *buf; int stream; #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) + struct snd_soc_pcm_runtime *rtd = pcm->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai); size_t size = bf5xx_pcm_hardware.buffer_bytes_max * sizeof(struct ac97_frame) / 4; #endif @@ -405,8 +414,6 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm) } #endif } - if (sport_handle) - sport_done(sport_handle); } static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32); @@ -458,7 +465,7 @@ static int __devexit bf5xx_soc_platform_remove(struct platform_device *pdev) static struct platform_driver bf5xx_pcm_driver = { .driver = { - .name = "bf5xx-pcm-audio", + .name = "bfin-ac97-pcm-audio", .owner = THIS_MODULE, }, diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c index ffbac26..6d21625 100644 --- a/sound/soc/blackfin/bf5xx-ac97.c +++ b/sound/soc/blackfin/bf5xx-ac97.c @@ -41,48 +41,7 @@ * anomaly does not affect blackfin sound drivers. */ -static int *cmd_count; -static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM; - -#define SPORT_REQ(x) \ - [x] = {P_SPORT##x##_TFS, P_SPORT##x##_DTPRI, P_SPORT##x##_TSCLK, \ - P_SPORT##x##_RFS, P_SPORT##x##_DRPRI, P_SPORT##x##_RSCLK, 0} -static u16 sport_req[][7] = { -#ifdef SPORT0_TCR1 - SPORT_REQ(0), -#endif -#ifdef SPORT1_TCR1 - SPORT_REQ(1), -#endif -#ifdef SPORT2_TCR1 - SPORT_REQ(2), -#endif -#ifdef SPORT3_TCR1 - SPORT_REQ(3), -#endif -}; - -#define SPORT_PARAMS(x) \ - [x] = { \ - .dma_rx_chan = CH_SPORT##x##_RX, \ - .dma_tx_chan = CH_SPORT##x##_TX, \ - .err_irq = IRQ_SPORT##x##_ERROR, \ - .regs = (struct sport_register *)SPORT##x##_TCR1, \ - } -static struct sport_param sport_params[4] = { -#ifdef SPORT0_TCR1 - SPORT_PARAMS(0), -#endif -#ifdef SPORT1_TCR1 - SPORT_PARAMS(1), -#endif -#ifdef SPORT2_TCR1 - SPORT_PARAMS(2), -#endif -#ifdef SPORT3_TCR1 - SPORT_PARAMS(3), -#endif -}; +static struct sport_device *ac97_sport_handle; void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u16 *src, size_t count, unsigned int chan_mask) @@ -140,7 +99,8 @@ static unsigned int sport_tx_curr_frag(struct sport_device *sport) static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data) { - struct sport_device *sport = sport_handle; + struct sport_device *sport = ac97_sport_handle; + int *cmd_count = sport->private_data; int nextfrag = sport_tx_curr_frag(sport); struct ac97_frame *nextwrite; @@ -161,6 +121,7 @@ static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data) static unsigned short bf5xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg) { + struct sport_device *sport_handle = ac97_sport_handle; struct ac97_frame out_frame[2], in_frame[2]; pr_debug("%s enter 0x%x\n", __func__, reg); @@ -185,6 +146,8 @@ static unsigned short bf5xx_ac97_read(struct snd_ac97 *ac97, void bf5xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val) { + struct sport_device *sport_handle = ac97_sport_handle; + pr_debug("%s enter 0x%x:0x%04x\n", __func__, reg, val); if (sport_handle->tx_run) { @@ -203,28 +166,19 @@ void bf5xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, static void bf5xx_ac97_warm_reset(struct snd_ac97 *ac97) { -#if defined(CONFIG_BF54x) || defined(CONFIG_BF561) || \ - (defined(BF537_FAMILY) && (CONFIG_SND_BF5XX_SPORT_NUM == 1)) - -#define CONCAT(a, b, c) a ## b ## c -#define BFIN_SPORT_RFS(x) CONCAT(P_SPORT, x, _RFS) - - u16 per = BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM); - u16 gpio = P_IDENT(BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM)); + struct sport_device *sport_handle = ac97_sport_handle; + u16 gpio = P_IDENT(sport_handle->pin_req[3]); pr_debug("%s enter\n", __func__); - peripheral_free(per); + peripheral_free_list(sport_handle->pin_req); gpio_request(gpio, "bf5xx-ac97"); gpio_direction_output(gpio, 1); udelay(2); gpio_set_value(gpio, 0); udelay(1); gpio_free(gpio); - peripheral_request(per, "soc-audio"); -#else - pr_info("%s: Not implemented\n", __func__); -#endif + peripheral_request_list(sport_handle->pin_req, "soc-audio"); } static void bf5xx_ac97_cold_reset(struct snd_ac97 *ac97) @@ -306,18 +260,32 @@ static int bf5xx_ac97_resume(struct snd_soc_dai *dai) #define bf5xx_ac97_resume NULL #endif -static int bf5xx_ac97_probe(struct snd_soc_dai *dai) +static struct snd_soc_dai_driver bfin_ac97_dai = { + .ac97_control = 1, + .suspend = bf5xx_ac97_suspend, + .resume = bf5xx_ac97_resume, + .playback = { + .stream_name = "AC97 Playback", + .channels_min = 2, +#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT) + .channels_max = 6, +#else + .channels_max = 2, +#endif + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, }, + .capture = { + .stream_name = "AC97 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, }, +}; + +static int __devinit asoc_bfin_ac97_probe(struct platform_device *pdev) { - int ret = 0; - cmd_count = (int *)get_zeroed_page(GFP_KERNEL); - if (cmd_count == NULL) - return -ENOMEM; - - if (peripheral_request_list(sport_req[sport_num], "soc-audio")) { - pr_err("Requesting Peripherals failed\n"); - ret = -EFAULT; - goto peripheral_err; - } + struct sport_device *sport_handle; + int ret; #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET /* Request PB3 as reset pin */ @@ -329,12 +297,14 @@ static int bf5xx_ac97_probe(struct snd_soc_dai *dai) } gpio_direction_output(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1); #endif - sport_handle = sport_init(&sport_params[sport_num], 2, \ - sizeof(struct ac97_frame), NULL); + + sport_handle = sport_init(pdev, 2, sizeof(struct ac97_frame), + PAGE_SIZE); if (!sport_handle) { ret = -ENODEV; goto sport_err; } + /*SPORT works in TDM mode to simulate AC97 transfers*/ #if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT) ret = sport_set_multichannel(sport_handle, 16, 0x3FF, 1); @@ -361,67 +331,37 @@ static int bf5xx_ac97_probe(struct snd_soc_dai *dai) goto sport_config_err; } + ret = snd_soc_register_dai(&pdev->dev, &bfin_ac97_dai); + if (ret) { + pr_err("Failed to register DAI: %d\n", ret); + goto sport_config_err; + } + + ac97_sport_handle = sport_handle; + return 0; sport_config_err: - kfree(sport_handle); + sport_done(sport_handle); sport_err: #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM); gpio_err: #endif - peripheral_free_list(sport_req[sport_num]); -peripheral_err: - free_page((unsigned long)cmd_count); - cmd_count = NULL; return ret; } -static int bf5xx_ac97_remove(struct snd_soc_dai *dai) +static int __devexit asoc_bfin_ac97_remove(struct platform_device *pdev) { - free_page((unsigned long)cmd_count); - cmd_count = NULL; - peripheral_free_list(sport_req[sport_num]); + struct sport_device *sport_handle = platform_get_drvdata(pdev); + + snd_soc_unregister_dai(&pdev->dev); + sport_done(sport_handle); #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM); #endif - return 0; -} - -struct snd_soc_dai_driver bfin_ac97_dai = { - .ac97_control = 1, - .probe = bf5xx_ac97_probe, - .remove = bf5xx_ac97_remove, - .suspend = bf5xx_ac97_suspend, - .resume = bf5xx_ac97_resume, - .playback = { - .stream_name = "AC97 Playback", - .channels_min = 2, -#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT) - .channels_max = 6, -#else - .channels_max = 2, -#endif - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, }, - .capture = { - .stream_name = "AC97 Capture", - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, }, -}; -EXPORT_SYMBOL_GPL(bfin_ac97_dai); - -static __devinit int asoc_bfin_ac97_probe(struct platform_device *pdev) -{ - return snd_soc_register_dai(&pdev->dev, &bfin_ac97_dai); -} -static int __devexit asoc_bfin_ac97_remove(struct platform_device *pdev) -{ - snd_soc_unregister_dai(&pdev->dev); return 0; } diff --git a/sound/soc/blackfin/bf5xx-ad1836.c b/sound/soc/blackfin/bf5xx-ad1836.c index 83012da..ea4951c 100644 --- a/sound/soc/blackfin/bf5xx-ad1836.c +++ b/sound/soc/blackfin/bf5xx-ad1836.c @@ -29,22 +29,12 @@ #include <asm/portmux.h> #include "../codecs/ad1836.h" -#include "bf5xx-sport.h" #include "bf5xx-tdm-pcm.h" #include "bf5xx-tdm.h" static struct snd_soc_card bf5xx_ad1836; -static int bf5xx_ad1836_startup(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - - snd_soc_dai_set_drvdata(cpu_dai, sport_handle); - return 0; -} - static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -75,23 +65,33 @@ static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream, } static struct snd_soc_ops bf5xx_ad1836_ops = { - .startup = bf5xx_ad1836_startup, .hw_params = bf5xx_ad1836_hw_params, }; -static struct snd_soc_dai_link bf5xx_ad1836_dai = { - .name = "ad1836", - .stream_name = "AD1836", - .cpu_dai_name = "bf5xx-tdm", - .codec_dai_name = "ad1836-hifi", - .platform_name = "bf5xx-tdm-pcm-audio", - .codec_name = "ad1836-codec.0", - .ops = &bf5xx_ad1836_ops, +static struct snd_soc_dai_link bf5xx_ad1836_dai[] = { + { + .name = "ad1836", + .stream_name = "AD1836", + .cpu_dai_name = "bfin-tdm.0", + .codec_dai_name = "ad1836-hifi", + .platform_name = "bfin-tdm-pcm-audio", + .codec_name = "ad1836.0", + .ops = &bf5xx_ad1836_ops, + }, + { + .name = "ad1836", + .stream_name = "AD1836", + .cpu_dai_name = "bfin-tdm.1", + .codec_dai_name = "ad1836-hifi", + .platform_name = "bfin-tdm-pcm-audio", + .codec_name = "ad1836.0", + .ops = &bf5xx_ad1836_ops, + }, }; static struct snd_soc_card bf5xx_ad1836 = { - .name = "bf5xx_ad1836", - .dai_link = &bf5xx_ad1836_dai, + .name = "bfin-ad1836", + .dai_link = &bf5xx_ad1836_dai[CONFIG_SND_BF5XX_SPORT_NUM], .num_links = 1, }; diff --git a/sound/soc/blackfin/bf5xx-ad193x.c b/sound/soc/blackfin/bf5xx-ad193x.c index d3ccb92..d6651c0 100644 --- a/sound/soc/blackfin/bf5xx-ad193x.c +++ b/sound/soc/blackfin/bf5xx-ad193x.c @@ -38,30 +38,28 @@ #include <asm/portmux.h> #include "../codecs/ad193x.h" -#include "bf5xx-sport.h" #include "bf5xx-tdm-pcm.h" #include "bf5xx-tdm.h" static struct snd_soc_card bf5xx_ad193x; -static int bf5xx_ad193x_startup(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - - snd_soc_dai_set_drvdata(cpu_dai, sport_handle); - return 0; -} - static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai = rtd->codec_dai; + unsigned int clk = 0; unsigned int channel_map[] = {0, 1, 2, 3, 4, 5, 6, 7}; int ret = 0; + + switch (params_rate(params)) { + case 48000: + clk = 12288000; + break; + } + /* set cpu DAI configuration */ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM); @@ -74,6 +72,12 @@ static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; + /* set the codec system clock for DAC and ADC */ + ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk, + SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + /* set codec DAI slots, 8 channels, all channels are enabled */ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 0xFF, 8, 32); if (ret < 0) @@ -89,23 +93,33 @@ static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream, } static struct snd_soc_ops bf5xx_ad193x_ops = { - .startup = bf5xx_ad193x_startup, .hw_params = bf5xx_ad193x_hw_params, }; -static struct snd_soc_dai_link bf5xx_ad193x_dai = { - .name = "ad193x", - .stream_name = "AD193X", - .cpu_dai_name = "bf5xx-tdm", - .codec_dai_name ="ad193x-hifi", - .platform_name = "bf5xx-tdm-pcm-audio", - .codec_name = "ad193x-codec.5", - .ops = &bf5xx_ad193x_ops, +static struct snd_soc_dai_link bf5xx_ad193x_dai[] = { + { + .name = "ad193x", + .stream_name = "AD193X", + .cpu_dai_name = "bfin-tdm.0", + .codec_dai_name ="ad193x-hifi", + .platform_name = "bfin-tdm-pcm-audio", + .codec_name = "ad193x.5", + .ops = &bf5xx_ad193x_ops, + }, + { + .name = "ad193x", + .stream_name = "AD193X", + .cpu_dai_name = "bfin-tdm.1", + .codec_dai_name ="ad193x-hifi", + .platform_name = "bfin-tdm-pcm-audio", + .codec_name = "ad193x.5", + .ops = &bf5xx_ad193x_ops, + }, }; static struct snd_soc_card bf5xx_ad193x = { - .name = "bf5xx_ad193x", - .dai_link = &bf5xx_ad193x_dai, + .name = "bfin-ad193x", + .dai_link = &bf5xx_ad193x_dai[CONFIG_SND_BF5XX_SPORT_NUM], .num_links = 1, }; diff --git a/sound/soc/blackfin/bf5xx-ad1980.c b/sound/soc/blackfin/bf5xx-ad1980.c index d57c9c9..06a84b2 100644 --- a/sound/soc/blackfin/bf5xx-ad1980.c +++ b/sound/soc/blackfin/bf5xx-ad1980.c @@ -47,39 +47,34 @@ #include <asm/portmux.h> #include "../codecs/ad1980.h" -#include "bf5xx-sport.h" + #include "bf5xx-ac97-pcm.h" #include "bf5xx-ac97.h" static struct snd_soc_card bf5xx_board; -static int bf5xx_board_startup(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - - pr_debug("%s enter\n", __func__); - snd_soc_dai_set_drvdata(cpu_dai, sport_handle); - return 0; -} - -static struct snd_soc_ops bf5xx_board_ops = { - .startup = bf5xx_board_startup, -}; - -static struct snd_soc_dai_link bf5xx_board_dai = { - .name = "AC97", - .stream_name = "AC97 HiFi", - .cpu_dai_name = "bfin-ac97", - .codec_dai_name = "ad1980-hifi", - .platform_name = "bfin-pcm-audio", - .codec_name = "ad1980-codec", - .ops = &bf5xx_board_ops, +static struct snd_soc_dai_link bf5xx_board_dai[] = { + { + .name = "AC97", + .stream_name = "AC97 HiFi", + .cpu_dai_name = "bfin-ac97.0", + .codec_dai_name = "ad1980-hifi", + .platform_name = "bfin-ac97-pcm-audio", + .codec_name = "ad1980", + }, + { + .name = "AC97", + .stream_name = "AC97 HiFi", + .cpu_dai_name = "bfin-ac97.1", + .codec_dai_name = "ad1980-hifi", + .platform_name = "bfin-ac97-pcm-audio", + .codec_name = "ad1980", + }, }; static struct snd_soc_card bf5xx_board = { - .name = "bf5xx-board", - .dai_link = &bf5xx_board_dai, + .name = "bfin-ad1980", + .dai_link = &bf5xx_board_dai[CONFIG_SND_BF5XX_SPORT_NUM], .num_links = 1, }; diff --git a/sound/soc/blackfin/bf5xx-ad73311.c b/sound/soc/blackfin/bf5xx-ad73311.c index 732fb8b..732a247 100644 --- a/sound/soc/blackfin/bf5xx-ad73311.c +++ b/sound/soc/blackfin/bf5xx-ad73311.c @@ -145,16 +145,6 @@ static int bf5xx_probe(struct platform_device *pdev) return 0; } -static int bf5xx_ad73311_startup(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - - pr_debug("%s enter\n", __func__); - snd_soc_dai_set_drvdata(cpu_dai, sport_handle); - return 0; -} - static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -176,24 +166,34 @@ static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream, static struct snd_soc_ops bf5xx_ad73311_ops = { - .startup = bf5xx_ad73311_startup, .hw_params = bf5xx_ad73311_hw_params, }; -static struct snd_soc_dai_link bf5xx_ad73311_dai = { - .name = "ad73311", - .stream_name = "AD73311", - .cpu_dai_name = "bf5xx-i2s", - .codec_dai_name = "ad73311-hifi", - .platform_name = "bfin-pcm-audio", - .codec_name = "ad73311-codec", - .ops = &bf5xx_ad73311_ops, +static struct snd_soc_dai_link bf5xx_ad73311_dai[] = { + { + .name = "ad73311", + .stream_name = "AD73311", + .cpu_dai_name = "bfin-i2s.0", + .codec_dai_name = "ad73311-hifi", + .platform_name = "bfin-i2s-pcm-audio", + .codec_name = "ad73311", + .ops = &bf5xx_ad73311_ops, + }, + { + .name = "ad73311", + .stream_name = "AD73311", + .cpu_dai_name = "bfin-i2s.1", + .codec_dai_name = "ad73311-hifi", + .platform_name = "bfin-i2s-pcm-audio", + .codec_name = "ad73311", + .ops = &bf5xx_ad73311_ops, + }, }; static struct snd_soc_card bf5xx_ad73311 = { - .name = "bf5xx_ad73311", + .name = "bfin-ad73311", .probe = bf5xx_probe, - .dai_link = &bf5xx_ad73311_dai, + .dai_link = &bf5xx_ad73311_dai[CONFIG_SND_BF5XX_SPORT_NUM], .num_links = 1, }; diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c index 890a0dc..b5101ef 100644 --- a/sound/soc/blackfin/bf5xx-i2s-pcm.c +++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c @@ -148,10 +148,15 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream) static int bf5xx_pcm_open(struct snd_pcm_substream *substream) { + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai); struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_dma_buffer *buf = &substream->dma_buffer; int ret; pr_debug("%s enter\n", __func__); + snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware); ret = snd_pcm_hw_constraint_integer(runtime, \ @@ -159,9 +164,14 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream) if (ret < 0) goto out; - if (sport_handle != NULL) + if (sport_handle != NULL) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + sport_handle->tx_buf = buf->area; + else + sport_handle->rx_buf = buf->area; + runtime->private_data = sport_handle; - else { + } else { pr_err("sport_handle is NULL\n"); return -1; } @@ -214,11 +224,6 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) pr_debug("%s, area:%p, size:0x%08lx\n", __func__, buf->area, buf->bytes); - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - sport_handle->tx_buf = buf->area; - else - sport_handle->rx_buf = buf->area; - return 0; } @@ -239,8 +244,6 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm) dma_free_coherent(NULL, buf->bytes, buf->area, 0); buf->area = NULL; } - if (sport_handle) - sport_done(sport_handle); } static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32); @@ -292,7 +295,7 @@ static int __devexit bfin_i2s_soc_platform_remove(struct platform_device *pdev) static struct platform_driver bfin_i2s_pcm_driver = { .driver = { - .name = "bfin-pcm-audio", + .name = "bfin-i2s-pcm-audio", .owner = THIS_MODULE, }, diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c index d453b1e..00cc3e0 100644 --- a/sound/soc/blackfin/bf5xx-i2s.c +++ b/sound/soc/blackfin/bf5xx-i2s.c @@ -51,59 +51,24 @@ struct bf5xx_i2s_port { int configured; }; -static struct bf5xx_i2s_port bf5xx_i2s; -static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM; - -static struct sport_param sport_params[2] = { - { - .dma_rx_chan = CH_SPORT0_RX, - .dma_tx_chan = CH_SPORT0_TX, - .err_irq = IRQ_SPORT0_ERROR, - .regs = (struct sport_register *)SPORT0_TCR1, - }, - { - .dma_rx_chan = CH_SPORT1_RX, - .dma_tx_chan = CH_SPORT1_TX, - .err_irq = IRQ_SPORT1_ERROR, - .regs = (struct sport_register *)SPORT1_TCR1, - } -}; - -/* - * Setting the TFS pin selector for SPORT 0 based on whether the selected - * port id F or G. If the port is F then no conflict should exist for the - * TFS. When Port G is selected and EMAC then there is a conflict between - * the PHY interrupt line and TFS. Current settings prevent the conflict - * by ignoring the TFS pin when Port G is selected. This allows both - * codecs and EMAC using Port G concurrently. - */ -#ifdef CONFIG_BF527_SPORT0_PORTG -#define LOCAL_SPORT0_TFS (0) -#else -#define LOCAL_SPORT0_TFS (P_SPORT0_TFS) -#endif - -static u16 sport_req[][7] = { {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, - P_SPORT0_DRPRI, P_SPORT0_RSCLK, LOCAL_SPORT0_TFS, 0}, - {P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, P_SPORT1_DRPRI, - P_SPORT1_RSCLK, P_SPORT1_TFS, 0} }; - static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { + struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai); + struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data; int ret = 0; /* interface format:support I2S,slave mode */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: - bf5xx_i2s.tcr1 |= TFSR | TCKFE; - bf5xx_i2s.rcr1 |= RFSR | RCKFE; - bf5xx_i2s.tcr2 |= TSFSE; - bf5xx_i2s.rcr2 |= RSFSE; + bf5xx_i2s->tcr1 |= TFSR | TCKFE; + bf5xx_i2s->rcr1 |= RFSR | RCKFE; + bf5xx_i2s->tcr2 |= TSFSE; + bf5xx_i2s->rcr2 |= RSFSE; break; case SND_SOC_DAIFMT_DSP_A: - bf5xx_i2s.tcr1 |= TFSR; - bf5xx_i2s.rcr1 |= RFSR; + bf5xx_i2s->tcr1 |= TFSR; + bf5xx_i2s->rcr1 |= RFSR; break; case SND_SOC_DAIFMT_LEFT_J: ret = -EINVAL; @@ -135,29 +100,35 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { + struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); + struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data; int ret = 0; - bf5xx_i2s.tcr2 &= ~0x1f; - bf5xx_i2s.rcr2 &= ~0x1f; + bf5xx_i2s->tcr2 &= ~0x1f; + bf5xx_i2s->rcr2 &= ~0x1f; switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + bf5xx_i2s->tcr2 |= 7; + bf5xx_i2s->rcr2 |= 7; + sport_handle->wdsize = 1; case SNDRV_PCM_FORMAT_S16_LE: - bf5xx_i2s.tcr2 |= 15; - bf5xx_i2s.rcr2 |= 15; + bf5xx_i2s->tcr2 |= 15; + bf5xx_i2s->rcr2 |= 15; sport_handle->wdsize = 2; break; case SNDRV_PCM_FORMAT_S24_LE: - bf5xx_i2s.tcr2 |= 23; - bf5xx_i2s.rcr2 |= 23; + bf5xx_i2s->tcr2 |= 23; + bf5xx_i2s->rcr2 |= 23; sport_handle->wdsize = 3; break; case SNDRV_PCM_FORMAT_S32_LE: - bf5xx_i2s.tcr2 |= 31; - bf5xx_i2s.rcr2 |= 31; + bf5xx_i2s->tcr2 |= 31; + bf5xx_i2s->rcr2 |= 31; sport_handle->wdsize = 4; break; } - if (!bf5xx_i2s.configured) { + if (!bf5xx_i2s->configured) { /* * TX and RX are not independent,they are enabled at the * same time, even if only one side is running. So, we @@ -166,16 +137,16 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream, * * CPU DAI:slave mode. */ - bf5xx_i2s.configured = 1; - ret = sport_config_rx(sport_handle, bf5xx_i2s.rcr1, - bf5xx_i2s.rcr2, 0, 0); + bf5xx_i2s->configured = 1; + ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1, + bf5xx_i2s->rcr2, 0, 0); if (ret) { pr_err("SPORT is busy!\n"); return -EBUSY; } - ret = sport_config_tx(sport_handle, bf5xx_i2s.tcr1, - bf5xx_i2s.tcr2, 0, 0); + ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1, + bf5xx_i2s->tcr2, 0, 0); if (ret) { pr_err("SPORT is busy!\n"); return -EBUSY; @@ -188,41 +159,19 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream, static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { + struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); + struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data; + pr_debug("%s enter\n", __func__); /* No active stream, SPORT is allowed to be configured again. */ if (!dai->active) - bf5xx_i2s.configured = 0; -} - -static int bf5xx_i2s_probe(struct snd_soc_dai *dai) -{ - pr_debug("%s enter\n", __func__); - if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) { - pr_err("Requesting Peripherals failed\n"); - return -EFAULT; - } - - /* request DMA for SPORT */ - sport_handle = sport_init(&sport_params[sport_num], 4, \ - 2 * sizeof(u32), NULL); - if (!sport_handle) { - peripheral_free_list(&sport_req[sport_num][0]); - return -ENODEV; - } - - return 0; -} - -static int bf5xx_i2s_remove(struct snd_soc_dai *dai) -{ - pr_debug("%s enter\n", __func__); - peripheral_free_list(&sport_req[sport_num][0]); - return 0; + bf5xx_i2s->configured = 0; } #ifdef CONFIG_PM static int bf5xx_i2s_suspend(struct snd_soc_dai *dai) { + struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); pr_debug("%s : sport %d\n", __func__, dai->id); @@ -235,19 +184,21 @@ static int bf5xx_i2s_suspend(struct snd_soc_dai *dai) static int bf5xx_i2s_resume(struct snd_soc_dai *dai) { + struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); + struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data; int ret; pr_debug("%s : sport %d\n", __func__, dai->id); - ret = sport_config_rx(sport_handle, bf5xx_i2s.rcr1, - bf5xx_i2s.rcr2, 0, 0); + ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1, + bf5xx_i2s->rcr2, 0, 0); if (ret) { pr_err("SPORT is busy!\n"); return -EBUSY; } - ret = sport_config_tx(sport_handle, bf5xx_i2s.tcr1, - bf5xx_i2s.tcr2, 0, 0); + ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1, + bf5xx_i2s->tcr2, 0, 0); if (ret) { pr_err("SPORT is busy!\n"); return -EBUSY; @@ -266,8 +217,11 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai) SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \ SNDRV_PCM_RATE_96000) -#define BF5XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\ - SNDRV_PCM_FMTBIT_S32_LE) +#define BF5XX_I2S_FORMATS \ + (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) static struct snd_soc_dai_ops bf5xx_i2s_dai_ops = { .shutdown = bf5xx_i2s_shutdown, @@ -276,8 +230,6 @@ static struct snd_soc_dai_ops bf5xx_i2s_dai_ops = { }; static struct snd_soc_dai_driver bf5xx_i2s_dai = { - .probe = bf5xx_i2s_probe, - .remove = bf5xx_i2s_remove, .suspend = bf5xx_i2s_suspend, .resume = bf5xx_i2s_resume, .playback = { @@ -293,23 +245,45 @@ static struct snd_soc_dai_driver bf5xx_i2s_dai = { .ops = &bf5xx_i2s_dai_ops, }; -static int bfin_i2s_drv_probe(struct platform_device *pdev) +static int __devinit bf5xx_i2s_probe(struct platform_device *pdev) { - return snd_soc_register_dai(&pdev->dev, &bf5xx_i2s_dai); + struct sport_device *sport_handle; + int ret; + + /* configure SPORT for I2S */ + sport_handle = sport_init(pdev, 4, 2 * sizeof(u32), + sizeof(struct bf5xx_i2s_port)); + if (!sport_handle) + return -ENODEV; + + /* register with the ASoC layers */ + ret = snd_soc_register_dai(&pdev->dev, &bf5xx_i2s_dai); + if (ret) { + pr_err("Failed to register DAI: %d\n", ret); + sport_done(sport_handle); + return ret; + } + + return 0; } -static int __devexit bfin_i2s_drv_remove(struct platform_device *pdev) +static int __devexit bf5xx_i2s_remove(struct platform_device *pdev) { + struct sport_device *sport_handle = platform_get_drvdata(pdev); + + pr_debug("%s enter\n", __func__); + snd_soc_unregister_dai(&pdev->dev); + sport_done(sport_handle); + return 0; } static struct platform_driver bfin_i2s_driver = { - .probe = bfin_i2s_drv_probe, - .remove = __devexit_p(bfin_i2s_drv_remove), - + .probe = bf5xx_i2s_probe, + .remove = __devexit_p(bf5xx_i2s_remove), .driver = { - .name = "bf5xx-i2s", + .name = "bfin-i2s", .owner = THIS_MODULE, }, }; diff --git a/sound/soc/blackfin/bf5xx-sport.c b/sound/soc/blackfin/bf5xx-sport.c index 99051ff..a2d4034 100644 --- a/sound/soc/blackfin/bf5xx-sport.c +++ b/sound/soc/blackfin/bf5xx-sport.c @@ -42,8 +42,6 @@ /* delay between frame sync pulse and first data bit in multichannel mode */ #define FRAME_DELAY (1<<12) -struct sport_device *sport_handle; -EXPORT_SYMBOL(sport_handle); /* note: multichannel is in units of 8 channels, * tdm_count is # channels NOT / 8 ! */ int sport_set_multichannel(struct sport_device *sport, @@ -798,86 +796,164 @@ int sport_set_err_callback(struct sport_device *sport, } EXPORT_SYMBOL(sport_set_err_callback); -struct sport_device *sport_init(struct sport_param *param, unsigned wdsize, - unsigned dummy_count, void *private_data) +static int sport_config_pdev(struct platform_device *pdev, struct sport_param *param) { - int ret; + /* Extract settings from platform data */ + struct device *dev = &pdev->dev; + struct bfin_snd_platform_data *pdata = dev->platform_data; + struct resource *res; + + param->num = pdev->id; + + if (!pdata) { + dev_err(dev, "no platform_data\n"); + return -ENODEV; + } + param->pin_req = pdata->pin_req; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "no MEM resource\n"); + return -ENODEV; + } + param->regs = (struct sport_register *)res->start; + + /* first RX, then TX */ + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!res) { + dev_err(dev, "no rx DMA resource\n"); + return -ENODEV; + } + param->dma_rx_chan = res->start; + + res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (!res) { + dev_err(dev, "no tx DMA resource\n"); + return -ENODEV; + } + param->dma_tx_chan = res->start; + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(dev, "no irq resource\n"); + return -ENODEV; + } + param->err_irq = res->start; + + return 0; +} + +struct sport_device *sport_init(struct platform_device *pdev, + unsigned int wdsize, unsigned int dummy_count, size_t priv_size) +{ + struct device *dev = &pdev->dev; + struct sport_param param; struct sport_device *sport; - pr_debug("%s enter\n", __func__); - BUG_ON(param == NULL); - BUG_ON(wdsize == 0 || dummy_count == 0); - sport = kmalloc(sizeof(struct sport_device), GFP_KERNEL); - if (!sport) { - pr_err("Failed to allocate for sport device\n"); + int ret; + + dev_dbg(dev, "%s enter\n", __func__); + + param.wdsize = wdsize; + param.dummy_count = dummy_count; + BUG_ON(param.wdsize == 0 || param.dummy_count == 0); + + ret = sport_config_pdev(pdev, ¶m); + if (ret) + return NULL; + + if (peripheral_request_list(param.pin_req, "soc-audio")) { + dev_err(dev, "requesting Peripherals failed\n"); return NULL; } - memset(sport, 0, sizeof(struct sport_device)); - sport->dma_rx_chan = param->dma_rx_chan; - sport->dma_tx_chan = param->dma_tx_chan; - sport->err_irq = param->err_irq; - sport->regs = param->regs; - sport->private_data = private_data; + sport = kzalloc(sizeof(*sport), GFP_KERNEL); + if (!sport) { + dev_err(dev, "failed to allocate for sport device\n"); + goto __init_err0; + } + + sport->num = param.num; + sport->dma_rx_chan = param.dma_rx_chan; + sport->dma_tx_chan = param.dma_tx_chan; + sport->err_irq = param.err_irq; + sport->regs = param.regs; + sport->pin_req = param.pin_req; if (request_dma(sport->dma_rx_chan, "SPORT RX Data") == -EBUSY) { - pr_err("Failed to request RX dma %d\n", \ - sport->dma_rx_chan); + dev_err(dev, "failed to request RX dma %d\n", sport->dma_rx_chan); goto __init_err1; } if (set_dma_callback(sport->dma_rx_chan, rx_handler, sport) != 0) { - pr_err("Failed to request RX irq %d\n", \ - sport->dma_rx_chan); + dev_err(dev, "failed to request RX irq %d\n", sport->dma_rx_chan); goto __init_err2; } if (request_dma(sport->dma_tx_chan, "SPORT TX Data") == -EBUSY) { - pr_err("Failed to request TX dma %d\n", \ - sport->dma_tx_chan); + dev_err(dev, "failed to request TX dma %d\n", sport->dma_tx_chan); goto __init_err2; } if (set_dma_callback(sport->dma_tx_chan, tx_handler, sport) != 0) { - pr_err("Failed to request TX irq %d\n", \ - sport->dma_tx_chan); + dev_err(dev, "failed to request TX irq %d\n", sport->dma_tx_chan); goto __init_err3; } if (request_irq(sport->err_irq, err_handler, IRQF_SHARED, "SPORT err", sport) < 0) { - pr_err("Failed to request err irq:%d\n", \ - sport->err_irq); + dev_err(dev, "failed to request err irq %d\n", sport->err_irq); goto __init_err3; } - pr_err("dma rx:%d tx:%d, err irq:%d, regs:%p\n", + dev_info(dev, "dma rx:%d tx:%d, err irq:%d, regs:%p\n", sport->dma_rx_chan, sport->dma_tx_chan, sport->err_irq, sport->regs); - sport->wdsize = wdsize; - sport->dummy_count = dummy_count; + sport->wdsize = param.wdsize; + sport->dummy_count = param.dummy_count; + + sport->private_data = kzalloc(priv_size, GFP_KERNEL); + if (!sport->private_data) { + dev_err(dev, "could not alloc priv data %zu bytes\n", priv_size); + goto __init_err4; + } if (L1_DATA_A_LENGTH) - sport->dummy_buf = l1_data_sram_zalloc(dummy_count * 2); + sport->dummy_buf = l1_data_sram_zalloc(param.dummy_count * 2); else - sport->dummy_buf = kzalloc(dummy_count * 2, GFP_KERNEL); + sport->dummy_buf = kzalloc(param.dummy_count * 2, GFP_KERNEL); if (sport->dummy_buf == NULL) { - pr_err("Failed to allocate dummy buffer\n"); - goto __error; + dev_err(dev, "failed to allocate dummy buffer\n"); + goto __error1; } ret = sport_config_rx_dummy(sport); if (ret) { - pr_err("Failed to config rx dummy ring\n"); - goto __error; + dev_err(dev, "failed to config rx dummy ring\n"); + goto __error2; } ret = sport_config_tx_dummy(sport); if (ret) { - pr_err("Failed to config tx dummy ring\n"); - goto __error; + dev_err(dev, "failed to config tx dummy ring\n"); + goto __error3; } + platform_set_drvdata(pdev, sport); + return sport; -__error: +__error3: + if (L1_DATA_A_LENGTH) + l1_data_sram_free(sport->dummy_rx_desc); + else + dma_free_coherent(NULL, 2*sizeof(struct dmasg), + sport->dummy_rx_desc, 0); +__error2: + if (L1_DATA_A_LENGTH) + l1_data_sram_free(sport->dummy_buf); + else + kfree(sport->dummy_buf); +__error1: + kfree(sport->private_data); +__init_err4: free_irq(sport->err_irq, sport); __init_err3: free_dma(sport->dma_tx_chan); @@ -885,6 +961,8 @@ __init_err2: free_dma(sport->dma_rx_chan); __init_err1: kfree(sport); +__init_err0: + peripheral_free_list(param.pin_req); return NULL; } EXPORT_SYMBOL(sport_init); @@ -917,8 +995,9 @@ void sport_done(struct sport_device *sport) free_dma(sport->dma_tx_chan); free_irq(sport->err_irq, sport); + kfree(sport->private_data); + peripheral_free_list(sport->pin_req); kfree(sport); - sport = NULL; } EXPORT_SYMBOL(sport_done); diff --git a/sound/soc/blackfin/bf5xx-sport.h b/sound/soc/blackfin/bf5xx-sport.h index a86e8cc..5ab60bd 100644 --- a/sound/soc/blackfin/bf5xx-sport.h +++ b/sound/soc/blackfin/bf5xx-sport.h @@ -1,5 +1,5 @@ /* - * File: bf5xx_ac97_sport.h + * File: bf5xx_sport.h * Based on: * Author: Roy Huang <roy.huang@analog.com> * @@ -33,15 +33,18 @@ #include <linux/types.h> #include <linux/wait.h> #include <linux/workqueue.h> +#include <linux/platform_device.h> #include <asm/dma.h> #include <asm/bfin_sport.h> #define DESC_ELEMENT_COUNT 9 struct sport_device { + int num; int dma_rx_chan; int dma_tx_chan; int err_irq; + const unsigned short *pin_req; struct sport_register *regs; unsigned char *rx_buf; @@ -103,17 +106,20 @@ struct sport_device { void *private_data; }; -extern struct sport_device *sport_handle; - struct sport_param { + int num; int dma_rx_chan; int dma_tx_chan; int err_irq; + const unsigned short *pin_req; struct sport_register *regs; + unsigned int wdsize; + unsigned int dummy_count; + void *private_data; }; -struct sport_device *sport_init(struct sport_param *param, unsigned wdsize, - unsigned dummy_count, void *private_data); +struct sport_device *sport_init(struct platform_device *pdev, + unsigned int wdsize, unsigned int dummy_count, size_t priv_size); void sport_done(struct sport_device *sport); diff --git a/sound/soc/blackfin/bf5xx-ssm2602.c b/sound/soc/blackfin/bf5xx-ssm2602.c index ad28663..767e772 100644 --- a/sound/soc/blackfin/bf5xx-ssm2602.c +++ b/sound/soc/blackfin/bf5xx-ssm2602.c @@ -44,16 +44,6 @@ static struct snd_soc_card bf5xx_ssm2602; -static int bf5xx_ssm2602_startup(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - - pr_debug("%s enter\n", __func__); - snd_soc_dai_set_drvdata(cpu_dai, sport_handle); - return 0; -} - static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -109,23 +99,33 @@ static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream, } static struct snd_soc_ops bf5xx_ssm2602_ops = { - .startup = bf5xx_ssm2602_startup, .hw_params = bf5xx_ssm2602_hw_params, }; -static struct snd_soc_dai_link bf5xx_ssm2602_dai = { - .name = "ssm2602", - .stream_name = "SSM2602", - .cpu_dai_name = "bf5xx-i2s", - .codec_dai_name = "ssm2602-hifi", - .platform_name = "bf5xx-pcm-audio", - .codec_name = "ssm2602-codec.0-001b", - .ops = &bf5xx_ssm2602_ops, +static struct snd_soc_dai_link bf5xx_ssm2602_dai[] = { + { + .name = "ssm2602", + .stream_name = "SSM2602", + .cpu_dai_name = "bfin-i2s.0", + .codec_dai_name = "ssm2602-hifi", + .platform_name = "bfin-i2s-pcm-audio", + .codec_name = "ssm2602.0-001b", + .ops = &bf5xx_ssm2602_ops, + }, + { + .name = "ssm2602", + .stream_name = "SSM2602", + .cpu_dai_name = "bfin-i2s.1", + .codec_dai_name = "ssm2602-hifi", + .platform_name = "bfin-i2s-pcm-audio", + .codec_name = "ssm2602.0-001b", + .ops = &bf5xx_ssm2602_ops, + }, }; static struct snd_soc_card bf5xx_ssm2602 = { - .name = "bf5xx_ssm2602", - .dai_link = &bf5xx_ssm2602_dai, + .name = "bfin-ssm2602", + .dai_link = &bf5xx_ssm2602_dai[CONFIG_SND_BF5XX_SPORT_NUM], .num_links = 1, }; diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.c b/sound/soc/blackfin/bf5xx-tdm-pcm.c index 74cf759..07cfc7a 100644 --- a/sound/soc/blackfin/bf5xx-tdm-pcm.c +++ b/sound/soc/blackfin/bf5xx-tdm-pcm.c @@ -154,7 +154,12 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream) static int bf5xx_pcm_open(struct snd_pcm_substream *substream) { + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai); struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_dma_buffer *buf = &substream->dma_buffer; + int ret = 0; snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware); @@ -164,9 +169,14 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream) if (ret < 0) goto out; - if (sport_handle != NULL) + if (sport_handle != NULL) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + sport_handle->tx_buf = buf->area; + else + sport_handle->rx_buf = buf->area; + runtime->private_data = sport_handle; - else { + } else { pr_err("sport_handle is NULL\n"); ret = -ENODEV; } @@ -249,11 +259,6 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) } buf->bytes = size; - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - sport_handle->tx_buf = buf->area; - else - sport_handle->rx_buf = buf->area; - return 0; } @@ -274,8 +279,6 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm) dma_free_coherent(NULL, buf->bytes, buf->area, 0); buf->area = NULL; } - if (sport_handle) - sport_done(sport_handle); } static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32); @@ -326,7 +329,7 @@ static int __devexit bf5xx_soc_platform_remove(struct platform_device *pdev) static struct platform_driver bfin_tdm_driver = { .driver = { - .name = "bf5xx-tdm-pcm-audio", + .name = "bfin-tdm-pcm-audio", .owner = THIS_MODULE, }, diff --git a/sound/soc/blackfin/bf5xx-tdm.c b/sound/soc/blackfin/bf5xx-tdm.c index 5515ac9..a822d1e 100644 --- a/sound/soc/blackfin/bf5xx-tdm.c +++ b/sound/soc/blackfin/bf5xx-tdm.c @@ -46,43 +46,6 @@ #include "bf5xx-sport.h" #include "bf5xx-tdm.h" -static struct bf5xx_tdm_port bf5xx_tdm; -static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM; - -static struct sport_param sport_params[2] = { - { - .dma_rx_chan = CH_SPORT0_RX, - .dma_tx_chan = CH_SPORT0_TX, - .err_irq = IRQ_SPORT0_ERROR, - .regs = (struct sport_register *)SPORT0_TCR1, - }, - { - .dma_rx_chan = CH_SPORT1_RX, - .dma_tx_chan = CH_SPORT1_TX, - .err_irq = IRQ_SPORT1_ERROR, - .regs = (struct sport_register *)SPORT1_TCR1, - } -}; - -/* - * Setting the TFS pin selector for SPORT 0 based on whether the selected - * port id F or G. If the port is F then no conflict should exist for the - * TFS. When Port G is selected and EMAC then there is a conflict between - * the PHY interrupt line and TFS. Current settings prevent the conflict - * by ignoring the TFS pin when Port G is selected. This allows both - * codecs and EMAC using Port G concurrently. - */ -#ifdef CONFIG_BF527_SPORT0_PORTG -#define LOCAL_SPORT0_TFS (0) -#else -#define LOCAL_SPORT0_TFS (P_SPORT0_TFS) -#endif - -static u16 sport_req[][7] = { {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, - P_SPORT0_DRPRI, P_SPORT0_RSCLK, LOCAL_SPORT0_TFS, 0}, - {P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, P_SPORT1_DRPRI, - P_SPORT1_RSCLK, P_SPORT1_TFS, 0} }; - static int bf5xx_tdm_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { @@ -119,14 +82,16 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { + struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); + struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data; int ret = 0; - bf5xx_tdm.tcr2 &= ~0x1f; - bf5xx_tdm.rcr2 &= ~0x1f; + bf5xx_tdm->tcr2 &= ~0x1f; + bf5xx_tdm->rcr2 &= ~0x1f; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S32_LE: - bf5xx_tdm.tcr2 |= 31; - bf5xx_tdm.rcr2 |= 31; + bf5xx_tdm->tcr2 |= 31; + bf5xx_tdm->rcr2 |= 31; sport_handle->wdsize = 4; break; /* at present, we only support 32bit transfer */ @@ -136,7 +101,7 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream, break; } - if (!bf5xx_tdm.configured) { + if (!bf5xx_tdm->configured) { /* * TX and RX are not independent,they are enabled at the * same time, even if only one side is running. So, we @@ -145,21 +110,21 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream, * * CPU DAI:slave mode. */ - ret = sport_config_rx(sport_handle, bf5xx_tdm.rcr1, - bf5xx_tdm.rcr2, 0, 0); + ret = sport_config_rx(sport_handle, bf5xx_tdm->rcr1, + bf5xx_tdm->rcr2, 0, 0); if (ret) { pr_err("SPORT is busy!\n"); return -EBUSY; } - ret = sport_config_tx(sport_handle, bf5xx_tdm.tcr1, - bf5xx_tdm.tcr2, 0, 0); + ret = sport_config_tx(sport_handle, bf5xx_tdm->tcr1, + bf5xx_tdm->tcr2, 0, 0); if (ret) { pr_err("SPORT is busy!\n"); return -EBUSY; } - bf5xx_tdm.configured = 1; + bf5xx_tdm->configured = 1; } return 0; @@ -168,15 +133,20 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream, static void bf5xx_tdm_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { + struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); + struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data; + /* No active stream, SPORT is allowed to be configured again. */ if (!dai->active) - bf5xx_tdm.configured = 0; + bf5xx_tdm->configured = 0; } static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_num, unsigned int *tx_slot, unsigned int rx_num, unsigned int *rx_slot) { + struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); + struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data; int i; unsigned int slot; unsigned int tx_mapped = 0, rx_mapped = 0; @@ -189,7 +159,7 @@ static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai, slot = tx_slot[i]; if ((slot < BFIN_TDM_DAI_MAX_SLOTS) && (!(tx_mapped & (1 << slot)))) { - bf5xx_tdm.tx_map[i] = slot; + bf5xx_tdm->tx_map[i] = slot; tx_mapped |= 1 << slot; } else return -EINVAL; @@ -198,7 +168,7 @@ static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai, slot = rx_slot[i]; if ((slot < BFIN_TDM_DAI_MAX_SLOTS) && (!(rx_mapped & (1 << slot)))) { - bf5xx_tdm.rx_map[i] = slot; + bf5xx_tdm->rx_map[i] = slot; rx_mapped |= 1 << slot; } else return -EINVAL; @@ -212,12 +182,14 @@ static int bf5xx_tdm_suspend(struct snd_soc_dai *dai) { struct sport_device *sport = snd_soc_dai_get_drvdata(dai); - if (!dai->active) - return 0; - if (dai->capture_active) - sport_rx_stop(sport); if (dai->playback_active) sport_tx_stop(sport); + if (dai->capture_active) + sport_rx_stop(sport); + + /* isolate sync/clock pins from codec while sports resume */ + peripheral_free_list(sport->pin_req); + return 0; } @@ -226,9 +198,6 @@ static int bf5xx_tdm_resume(struct snd_soc_dai *dai) int ret; struct sport_device *sport = snd_soc_dai_get_drvdata(dai); - if (!dai->active) - return 0; - ret = sport_set_multichannel(sport, 8, 0xFF, 1); if (ret) { pr_err("SPORT is busy!\n"); @@ -247,6 +216,8 @@ static int bf5xx_tdm_resume(struct snd_soc_dai *dai) ret = -EBUSY; } + peripheral_request_list(sport->pin_req, "soc-audio"); + return 0; } @@ -280,20 +251,14 @@ static struct snd_soc_dai_driver bf5xx_tdm_dai = { static int __devinit bfin_tdm_probe(struct platform_device *pdev) { - int ret = 0; - - if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) { - pr_err("Requesting Peripherals failed\n"); - return -EFAULT; - } + struct sport_device *sport_handle; + int ret; - /* request DMA for SPORT */ - sport_handle = sport_init(&sport_params[sport_num], 4, \ - 8 * sizeof(u32), NULL); - if (!sport_handle) { - peripheral_free_list(&sport_req[sport_num][0]); + /* configure SPORT for TDM */ + sport_handle = sport_init(pdev, 4, 8 * sizeof(u32), + sizeof(struct bf5xx_tdm_port)); + if (!sport_handle) return -ENODEV; - } /* SPORT works in TDM mode */ ret = sport_set_multichannel(sport_handle, 8, 0xFF, 1); @@ -323,18 +288,19 @@ static int __devinit bfin_tdm_probe(struct platform_device *pdev) goto sport_config_err; } - sport_handle->private_data = &bf5xx_tdm; return 0; sport_config_err: - peripheral_free_list(&sport_req[sport_num][0]); + sport_done(sport_handle); return ret; } static int __devexit bfin_tdm_remove(struct platform_device *pdev) { - peripheral_free_list(&sport_req[sport_num][0]); + struct sport_device *sport_handle = platform_get_drvdata(pdev); + snd_soc_unregister_dai(&pdev->dev); + sport_done(sport_handle); return 0; } diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 6943e24..ee7374e 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -16,8 +16,8 @@ config SND_SOC_ALL_CODECS select SND_SOC_AD1836 if SPI_MASTER select SND_SOC_AD193X if SND_SOC_I2C_AND_SPI select SND_SOC_AD1980 if SND_SOC_AC97_BUS + select SND_SOC_AD73311 select SND_SOC_ADS117X - select SND_SOC_AD73311 if I2C select SND_SOC_AK4104 if SPI_MASTER select SND_SOC_AK4535 if I2C select SND_SOC_AK4642 if I2C @@ -33,13 +33,14 @@ config SND_SOC_ALL_CODECS select SND_SOC_JZ4740_CODEC if SOC_JZ4740 select SND_SOC_LM4857 if I2C select SND_SOC_MAX98088 if I2C + select SND_SOC_MAX98095 if I2C select SND_SOC_MAX9850 if I2C select SND_SOC_MAX9877 if I2C select SND_SOC_PCM3008 select SND_SOC_SGTL5000 if I2C select SND_SOC_SN95031 if INTEL_SCU_IPC select SND_SOC_SPDIF - select SND_SOC_SSM2602 if I2C + select SND_SOC_SSM2602 if SND_SOC_I2C_AND_SPI select SND_SOC_STAC9766 if SND_SOC_AC97_BUS select SND_SOC_TLV320AIC23 if I2C select SND_SOC_TLV320AIC26 if SPI_MASTER @@ -187,6 +188,9 @@ config SND_SOC_DMIC config SND_SOC_MAX98088 tristate +config SND_SOC_MAX98095 + tristate + config SND_SOC_MAX9850 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 379bc55..f030c18 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -19,6 +19,7 @@ snd-soc-dfbmcs320-objs := dfbmcs320.o snd-soc-dmic-objs := dmic.o snd-soc-l3-objs := l3.o snd-soc-max98088-objs := max98088.o +snd-soc-max98095-objs := max98095.o snd-soc-max9850-objs := max9850.o snd-soc-pcm3008-objs := pcm3008.o snd-soc-sgtl5000-objs := sgtl5000.o @@ -69,7 +70,7 @@ snd-soc-wm8988-objs := wm8988.o snd-soc-wm8990-objs := wm8990.o snd-soc-wm8991-objs := wm8991.o snd-soc-wm8993-objs := wm8993.o -snd-soc-wm8994-objs := wm8994.o wm8994-tables.o +snd-soc-wm8994-objs := wm8994.o wm8994-tables.o wm8958-dsp2.o snd-soc-wm8995-objs := wm8995.o snd-soc-wm9081-objs := wm9081.o snd-soc-wm9705-objs := wm9705.o @@ -108,6 +109,7 @@ obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o +obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c index da46479..2374ca5 100644 --- a/sound/soc/codecs/ad193x.c +++ b/sound/soc/codecs/ad193x.c @@ -23,8 +23,7 @@ /* codec private data */ struct ad193x_priv { - enum snd_soc_control_type bus_type; - void *control_data; + enum snd_soc_control_type control_type; int sysclk; }; @@ -354,14 +353,12 @@ static int ad193x_probe(struct snd_soc_codec *codec) struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; - codec->control_data = ad193x->control_data; - if (ad193x->bus_type == SND_SOC_I2C) - ret = snd_soc_codec_set_cache_io(codec, 8, 8, ad193x->bus_type); + if (ad193x->control_type == SND_SOC_I2C) + ret = snd_soc_codec_set_cache_io(codec, 8, 8, ad193x->control_type); else - ret = snd_soc_codec_set_cache_io(codec, 16, 8, ad193x->bus_type); + ret = snd_soc_codec_set_cache_io(codec, 16, 8, ad193x->control_type); if (ret < 0) { - dev_err(codec->dev, "failed to set cache I/O: %d\n", - ret); + dev_err(codec->dev, "failed to set cache I/O: %d\n", ret); return ret; } @@ -408,8 +405,7 @@ static int __devinit ad193x_spi_probe(struct spi_device *spi) return -ENOMEM; spi_set_drvdata(spi, ad193x); - ad193x->control_data = spi; - ad193x->bus_type = SND_SOC_SPI; + ad193x->control_type = SND_SOC_SPI; ret = snd_soc_register_codec(&spi->dev, &soc_codec_dev_ad193x, &ad193x_dai, 1); @@ -427,7 +423,7 @@ static int __devexit ad193x_spi_remove(struct spi_device *spi) static struct spi_driver ad193x_spi_driver = { .driver = { - .name = "ad193x-codec", + .name = "ad193x", .owner = THIS_MODULE, }, .probe = ad193x_spi_probe, @@ -454,8 +450,7 @@ static int __devinit ad193x_i2c_probe(struct i2c_client *client, return -ENOMEM; i2c_set_clientdata(client, ad193x); - ad193x->control_data = client; - ad193x->bus_type = SND_SOC_I2C; + ad193x->control_type = SND_SOC_I2C; ret = snd_soc_register_codec(&client->dev, &soc_codec_dev_ad193x, &ad193x_dai, 1); @@ -473,7 +468,7 @@ static int __devexit ad193x_i2c_remove(struct i2c_client *client) static struct i2c_driver ad193x_i2c_driver = { .driver = { - .name = "ad193x-codec", + .name = "ad193x", }, .probe = ad193x_i2c_probe, .remove = __devexit_p(ad193x_i2c_remove), diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c index 34cb51e..923b364 100644 --- a/sound/soc/codecs/ad1980.c +++ b/sound/soc/codecs/ad1980.c @@ -266,7 +266,7 @@ static int __devexit ad1980_remove(struct platform_device *pdev) static struct platform_driver ad1980_codec_driver = { .driver = { - .name = "ad1980-codec", + .name = "ad1980", .owner = THIS_MODULE, }, diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c index de799cd..8d793e9 100644 --- a/sound/soc/codecs/ad73311.c +++ b/sound/soc/codecs/ad73311.c @@ -55,7 +55,7 @@ static int __devexit ad73311_remove(struct platform_device *pdev) static struct platform_driver ad73311_codec_driver = { .driver = { - .name = "ad73311-codec", + .name = "ad73311", .owner = THIS_MODULE, }, diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c index 8b38739..e1a214e 100644 --- a/sound/soc/codecs/ak4535.c +++ b/sound/soc/codecs/ak4535.c @@ -230,7 +230,7 @@ static const struct snd_soc_dapm_widget ak4535_dapm_widgets[] = { SND_SOC_DAPM_INPUT("AIN"), }; -static const struct snd_soc_dapm_route audio_map[] = { +static const struct snd_soc_dapm_route ak4535_audio_map[] = { /*stereo mixer */ {"Stereo Mixer", "Playback Switch", "DAC"}, {"Stereo Mixer", "Mic Sidetone Switch", "Mic"}, @@ -287,17 +287,6 @@ static const struct snd_soc_dapm_route audio_map[] = { {"Input Mixer", "Aux Capture Switch", "Aux In"}, }; -static int ak4535_add_widgets(struct snd_soc_codec *codec) -{ - struct snd_soc_dapm_context *dapm = &codec->dapm; - - snd_soc_dapm_new_controls(dapm, ak4535_dapm_widgets, - ARRAY_SIZE(ak4535_dapm_widgets)); - snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - - return 0; -} - static int ak4535_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) { @@ -457,8 +446,6 @@ static int ak4535_probe(struct snd_soc_codec *codec) snd_soc_add_controls(codec, ak4535_snd_controls, ARRAY_SIZE(ak4535_snd_controls)); - ak4535_add_widgets(codec); - return 0; } @@ -480,6 +467,10 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4535 = { .reg_cache_size = ARRAY_SIZE(ak4535_reg), .reg_word_size = sizeof(u8), .reg_cache_default = ak4535_reg, + .dapm_widgets = ak4535_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4535_dapm_widgets), + .dapm_routes = ak4535_audio_map, + .num_dapm_routes = ARRAY_SIZE(ak4535_audio_map), }; #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c index 2ec75ab..88b29f8 100644 --- a/sound/soc/codecs/ak4671.c +++ b/sound/soc/codecs/ak4671.c @@ -352,7 +352,7 @@ static const struct snd_soc_dapm_widget ak4671_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("PMPLL", AK4671_PLL_MODE_SELECT1, 0, 0, NULL, 0), }; -static const struct snd_soc_dapm_route intercon[] = { +static const struct snd_soc_dapm_route ak4671_intercon[] = { {"DAC Left", "NULL", "PMPLL"}, {"DAC Right", "NULL", "PMPLL"}, {"ADC Left", "NULL", "PMPLL"}, @@ -433,17 +433,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"ROUT3 Mixer", "RINS4", "RIN4 Mixing Circuit"}, }; -static int ak4671_add_widgets(struct snd_soc_codec *codec) -{ - struct snd_soc_dapm_context *dapm = &codec->dapm; - - snd_soc_dapm_new_controls(dapm, ak4671_dapm_widgets, - ARRAY_SIZE(ak4671_dapm_widgets)); - snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); - - return 0; -} - static int ak4671_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -650,7 +639,6 @@ static int ak4671_probe(struct snd_soc_codec *codec) snd_soc_add_controls(codec, ak4671_snd_controls, ARRAY_SIZE(ak4671_snd_controls)); - ak4671_add_widgets(codec); ak4671_set_bias_level(codec, SND_SOC_BIAS_STANDBY); @@ -670,6 +658,10 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4671 = { .reg_cache_size = AK4671_CACHEREGNUM, .reg_word_size = sizeof(u8), .reg_cache_default = ak4671_reg, + .dapm_widgets = ak4671_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4671_dapm_widgets), + .dapm_routes = ak4671_intercon, + .num_dapm_routes = ARRAY_SIZE(ak4671_intercon), }; static int __devinit ak4671_i2c_probe(struct i2c_client *client, diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c index 0bb424a..d68ea53 100644 --- a/sound/soc/codecs/cx20442.c +++ b/sound/soc/codecs/cx20442.c @@ -86,18 +86,6 @@ static const struct snd_soc_dapm_route cx20442_audio_map[] = { {"ADC", NULL, "Input Mixer"}, }; -static int cx20442_add_widgets(struct snd_soc_codec *codec) -{ - struct snd_soc_dapm_context *dapm = &codec->dapm; - - snd_soc_dapm_new_controls(dapm, cx20442_dapm_widgets, - ARRAY_SIZE(cx20442_dapm_widgets)); - snd_soc_dapm_add_routes(dapm, cx20442_audio_map, - ARRAY_SIZE(cx20442_audio_map)); - - return 0; -} - static unsigned int cx20442_read_reg_cache(struct snd_soc_codec *codec, unsigned int reg) { @@ -344,8 +332,6 @@ static int cx20442_codec_probe(struct snd_soc_codec *codec) return -ENOMEM; snd_soc_codec_set_drvdata(codec, cx20442); - cx20442_add_widgets(codec); - cx20442->control_data = NULL; codec->hw_write = NULL; codec->card->pop_time = 0; @@ -377,6 +363,10 @@ static struct snd_soc_codec_driver cx20442_codec_dev = { .reg_word_size = sizeof(u8), .read = cx20442_read_reg_cache, .write = cx20442_write, + .dapm_widgets = cx20442_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cx20442_dapm_widgets), + .dapm_routes = cx20442_audio_map, + .num_dapm_routes = ARRAY_SIZE(cx20442_audio_map), }; static int cx20442_platform_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index bd0517c..bb58bdb 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -1112,7 +1112,7 @@ static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = { SND_SOC_DAPM_INPUT("INB2"), }; -static const struct snd_soc_dapm_route audio_map[] = { +static const struct snd_soc_dapm_route max98088_audio_map[] = { /* Left headphone output mixer */ {"Left HP Mixer", "Left DAC1 Switch", "DACL1"}, {"Left HP Mixer", "Left DAC2 Switch", "DACL2"}, @@ -1226,22 +1226,6 @@ static const struct snd_soc_dapm_route audio_map[] = { {"MIC2 Input", NULL, "MIC2"}, }; -static int max98088_add_widgets(struct snd_soc_codec *codec) -{ - struct snd_soc_dapm_context *dapm = &codec->dapm; - - snd_soc_dapm_new_controls(dapm, max98088_dapm_widgets, - ARRAY_SIZE(max98088_dapm_widgets)); - - snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - - snd_soc_add_controls(codec, max98088_snd_controls, - ARRAY_SIZE(max98088_snd_controls)); - - snd_soc_dapm_new_widgets(dapm); - return 0; -} - /* codec mclk clock divider coefficients */ static const struct { u32 rate; @@ -2010,7 +1994,8 @@ static int max98088_probe(struct snd_soc_codec *codec) max98088_handle_pdata(codec); - max98088_add_widgets(codec); + snd_soc_add_controls(codec, max98088_snd_controls, + ARRAY_SIZE(max98088_snd_controls)); err_access: return ret; @@ -2036,6 +2021,10 @@ static struct snd_soc_codec_driver soc_codec_dev_max98088 = { .reg_word_size = sizeof(u8), .reg_cache_default = max98088_reg, .volatile_register = max98088_volatile_register, + .dapm_widgets = max98088_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98088_dapm_widgets), + .dapm_routes = max98088_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98088_audio_map), }; static int max98088_i2c_probe(struct i2c_client *i2c, diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c new file mode 100644 index 0000000..9c77f17 --- /dev/null +++ b/sound/soc/codecs/max98095.c @@ -0,0 +1,2009 @@ +/* + * max98095.c -- MAX98095 ALSA SoC Audio driver + * + * Copyright 2011 Maxim Integrated Products + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <linux/slab.h> +#include <asm/div64.h> +#include <sound/max98095.h> +#include "max98095.h" + +enum max98095_type { + MAX98095, +}; + +struct max98095_cdata { + unsigned int rate; + unsigned int fmt; +}; + +struct max98095_priv { + enum max98095_type devtype; + void *control_data; + struct max98095_pdata *pdata; + unsigned int sysclk; + struct max98095_cdata dai[3]; + u8 lin_state; + unsigned int mic1pre; + unsigned int mic2pre; +}; + +static const u8 max98095_reg_def[M98095_REG_CNT] = { + 0x00, /* 00 */ + 0x00, /* 01 */ + 0x00, /* 02 */ + 0x00, /* 03 */ + 0x00, /* 04 */ + 0x00, /* 05 */ + 0x00, /* 06 */ + 0x00, /* 07 */ + 0x00, /* 08 */ + 0x00, /* 09 */ + 0x00, /* 0A */ + 0x00, /* 0B */ + 0x00, /* 0C */ + 0x00, /* 0D */ + 0x00, /* 0E */ + 0x00, /* 0F */ + 0x00, /* 10 */ + 0x00, /* 11 */ + 0x00, /* 12 */ + 0x00, /* 13 */ + 0x00, /* 14 */ + 0x00, /* 15 */ + 0x00, /* 16 */ + 0x00, /* 17 */ + 0x00, /* 18 */ + 0x00, /* 19 */ + 0x00, /* 1A */ + 0x00, /* 1B */ + 0x00, /* 1C */ + 0x00, /* 1D */ + 0x00, /* 1E */ + 0x00, /* 1F */ + 0x00, /* 20 */ + 0x00, /* 21 */ + 0x00, /* 22 */ + 0x00, /* 23 */ + 0x00, /* 24 */ + 0x00, /* 25 */ + 0x00, /* 26 */ + 0x00, /* 27 */ + 0x00, /* 28 */ + 0x00, /* 29 */ + 0x00, /* 2A */ + 0x00, /* 2B */ + 0x00, /* 2C */ + 0x00, /* 2D */ + 0x00, /* 2E */ + 0x00, /* 2F */ + 0x00, /* 30 */ + 0x00, /* 31 */ + 0x00, /* 32 */ + 0x00, /* 33 */ + 0x00, /* 34 */ + 0x00, /* 35 */ + 0x00, /* 36 */ + 0x00, /* 37 */ + 0x00, /* 38 */ + 0x00, /* 39 */ + 0x00, /* 3A */ + 0x00, /* 3B */ + 0x00, /* 3C */ + 0x00, /* 3D */ + 0x00, /* 3E */ + 0x00, /* 3F */ + 0x00, /* 40 */ + 0x00, /* 41 */ + 0x00, /* 42 */ + 0x00, /* 43 */ + 0x00, /* 44 */ + 0x00, /* 45 */ + 0x00, /* 46 */ + 0x00, /* 47 */ + 0x00, /* 48 */ + 0x00, /* 49 */ + 0x00, /* 4A */ + 0x00, /* 4B */ + 0x00, /* 4C */ + 0x00, /* 4D */ + 0x00, /* 4E */ + 0x00, /* 4F */ + 0x00, /* 50 */ + 0x00, /* 51 */ + 0x00, /* 52 */ + 0x00, /* 53 */ + 0x00, /* 54 */ + 0x00, /* 55 */ + 0x00, /* 56 */ + 0x00, /* 57 */ + 0x00, /* 58 */ + 0x00, /* 59 */ + 0x00, /* 5A */ + 0x00, /* 5B */ + 0x00, /* 5C */ + 0x00, /* 5D */ + 0x00, /* 5E */ + 0x00, /* 5F */ + 0x00, /* 60 */ + 0x00, /* 61 */ + 0x00, /* 62 */ + 0x00, /* 63 */ + 0x00, /* 64 */ + 0x00, /* 65 */ + 0x00, /* 66 */ + 0x00, /* 67 */ + 0x00, /* 68 */ + 0x00, /* 69 */ + 0x00, /* 6A */ + 0x00, /* 6B */ + 0x00, /* 6C */ + 0x00, /* 6D */ + 0x00, /* 6E */ + 0x00, /* 6F */ + 0x00, /* 70 */ + 0x00, /* 71 */ + 0x00, /* 72 */ + 0x00, /* 73 */ + 0x00, /* 74 */ + 0x00, /* 75 */ + 0x00, /* 76 */ + 0x00, /* 77 */ + 0x00, /* 78 */ + 0x00, /* 79 */ + 0x00, /* 7A */ + 0x00, /* 7B */ + 0x00, /* 7C */ + 0x00, /* 7D */ + 0x00, /* 7E */ + 0x00, /* 7F */ + 0x00, /* 80 */ + 0x00, /* 81 */ + 0x00, /* 82 */ + 0x00, /* 83 */ + 0x00, /* 84 */ + 0x00, /* 85 */ + 0x00, /* 86 */ + 0x00, /* 87 */ + 0x00, /* 88 */ + 0x00, /* 89 */ + 0x00, /* 8A */ + 0x00, /* 8B */ + 0x00, /* 8C */ + 0x00, /* 8D */ + 0x00, /* 8E */ + 0x00, /* 8F */ + 0x00, /* 90 */ + 0x00, /* 91 */ + 0x30, /* 92 */ + 0xF0, /* 93 */ + 0x00, /* 94 */ + 0x00, /* 95 */ + 0x3F, /* 96 */ + 0x00, /* 97 */ + 0x00, /* 98 */ + 0x00, /* 99 */ + 0x00, /* 9A */ + 0x00, /* 9B */ + 0x00, /* 9C */ + 0x00, /* 9D */ + 0x00, /* 9E */ + 0x00, /* 9F */ + 0x00, /* A0 */ + 0x00, /* A1 */ + 0x00, /* A2 */ + 0x00, /* A3 */ + 0x00, /* A4 */ + 0x00, /* A5 */ + 0x00, /* A6 */ + 0x00, /* A7 */ + 0x00, /* A8 */ + 0x00, /* A9 */ + 0x00, /* AA */ + 0x00, /* AB */ + 0x00, /* AC */ + 0x00, /* AD */ + 0x00, /* AE */ + 0x00, /* AF */ + 0x00, /* B0 */ + 0x00, /* B1 */ + 0x00, /* B2 */ + 0x00, /* B3 */ + 0x00, /* B4 */ + 0x00, /* B5 */ + 0x00, /* B6 */ + 0x00, /* B7 */ + 0x00, /* B8 */ + 0x00, /* B9 */ + 0x00, /* BA */ + 0x00, /* BB */ + 0x00, /* BC */ + 0x00, /* BD */ + 0x00, /* BE */ + 0x00, /* BF */ + 0x00, /* C0 */ + 0x00, /* C1 */ + 0x00, /* C2 */ + 0x00, /* C3 */ + 0x00, /* C4 */ + 0x00, /* C5 */ + 0x00, /* C6 */ + 0x00, /* C7 */ + 0x00, /* C8 */ + 0x00, /* C9 */ + 0x00, /* CA */ + 0x00, /* CB */ + 0x00, /* CC */ + 0x00, /* CD */ + 0x00, /* CE */ + 0x00, /* CF */ + 0x00, /* D0 */ + 0x00, /* D1 */ + 0x00, /* D2 */ + 0x00, /* D3 */ + 0x00, /* D4 */ + 0x00, /* D5 */ + 0x00, /* D6 */ + 0x00, /* D7 */ + 0x00, /* D8 */ + 0x00, /* D9 */ + 0x00, /* DA */ + 0x00, /* DB */ + 0x00, /* DC */ + 0x00, /* DD */ + 0x00, /* DE */ + 0x00, /* DF */ + 0x00, /* E0 */ + 0x00, /* E1 */ + 0x00, /* E2 */ + 0x00, /* E3 */ + 0x00, /* E4 */ + 0x00, /* E5 */ + 0x00, /* E6 */ + 0x00, /* E7 */ + 0x00, /* E8 */ + 0x00, /* E9 */ + 0x00, /* EA */ + 0x00, /* EB */ + 0x00, /* EC */ + 0x00, /* ED */ + 0x00, /* EE */ + 0x00, /* EF */ + 0x00, /* F0 */ + 0x00, /* F1 */ + 0x00, /* F2 */ + 0x00, /* F3 */ + 0x00, /* F4 */ + 0x00, /* F5 */ + 0x00, /* F6 */ + 0x00, /* F7 */ + 0x00, /* F8 */ + 0x00, /* F9 */ + 0x00, /* FA */ + 0x00, /* FB */ + 0x00, /* FC */ + 0x00, /* FD */ + 0x00, /* FE */ + 0x00, /* FF */ +}; + +static struct { + int readable; + int writable; +} max98095_access[M98095_REG_CNT] = { + { 0x00, 0x00 }, /* 00 */ + { 0xFF, 0x00 }, /* 01 */ + { 0xFF, 0x00 }, /* 02 */ + { 0xFF, 0x00 }, /* 03 */ + { 0xFF, 0x00 }, /* 04 */ + { 0xFF, 0x00 }, /* 05 */ + { 0xFF, 0x00 }, /* 06 */ + { 0xFF, 0x00 }, /* 07 */ + { 0xFF, 0x00 }, /* 08 */ + { 0xFF, 0x00 }, /* 09 */ + { 0xFF, 0x00 }, /* 0A */ + { 0xFF, 0x00 }, /* 0B */ + { 0xFF, 0x00 }, /* 0C */ + { 0xFF, 0x00 }, /* 0D */ + { 0xFF, 0x00 }, /* 0E */ + { 0xFF, 0x9F }, /* 0F */ + { 0xFF, 0xFF }, /* 10 */ + { 0xFF, 0xFF }, /* 11 */ + { 0xFF, 0xFF }, /* 12 */ + { 0xFF, 0xFF }, /* 13 */ + { 0xFF, 0xFF }, /* 14 */ + { 0xFF, 0xFF }, /* 15 */ + { 0xFF, 0xFF }, /* 16 */ + { 0xFF, 0xFF }, /* 17 */ + { 0xFF, 0xFF }, /* 18 */ + { 0xFF, 0xFF }, /* 19 */ + { 0xFF, 0xFF }, /* 1A */ + { 0xFF, 0xFF }, /* 1B */ + { 0xFF, 0xFF }, /* 1C */ + { 0xFF, 0xFF }, /* 1D */ + { 0xFF, 0x77 }, /* 1E */ + { 0xFF, 0x77 }, /* 1F */ + { 0xFF, 0x77 }, /* 20 */ + { 0xFF, 0x77 }, /* 21 */ + { 0xFF, 0x77 }, /* 22 */ + { 0xFF, 0x77 }, /* 23 */ + { 0xFF, 0xFF }, /* 24 */ + { 0xFF, 0x7F }, /* 25 */ + { 0xFF, 0x31 }, /* 26 */ + { 0xFF, 0xFF }, /* 27 */ + { 0xFF, 0xFF }, /* 28 */ + { 0xFF, 0xFF }, /* 29 */ + { 0xFF, 0xF7 }, /* 2A */ + { 0xFF, 0x2F }, /* 2B */ + { 0xFF, 0xEF }, /* 2C */ + { 0xFF, 0xFF }, /* 2D */ + { 0xFF, 0xFF }, /* 2E */ + { 0xFF, 0xFF }, /* 2F */ + { 0xFF, 0xFF }, /* 30 */ + { 0xFF, 0xFF }, /* 31 */ + { 0xFF, 0xFF }, /* 32 */ + { 0xFF, 0xFF }, /* 33 */ + { 0xFF, 0xF7 }, /* 34 */ + { 0xFF, 0x2F }, /* 35 */ + { 0xFF, 0xCF }, /* 36 */ + { 0xFF, 0xFF }, /* 37 */ + { 0xFF, 0xFF }, /* 38 */ + { 0xFF, 0xFF }, /* 39 */ + { 0xFF, 0xFF }, /* 3A */ + { 0xFF, 0xFF }, /* 3B */ + { 0xFF, 0xFF }, /* 3C */ + { 0xFF, 0xFF }, /* 3D */ + { 0xFF, 0xF7 }, /* 3E */ + { 0xFF, 0x2F }, /* 3F */ + { 0xFF, 0xCF }, /* 40 */ + { 0xFF, 0xFF }, /* 41 */ + { 0xFF, 0x77 }, /* 42 */ + { 0xFF, 0xFF }, /* 43 */ + { 0xFF, 0xFF }, /* 44 */ + { 0xFF, 0xFF }, /* 45 */ + { 0xFF, 0xFF }, /* 46 */ + { 0xFF, 0xFF }, /* 47 */ + { 0xFF, 0xFF }, /* 48 */ + { 0xFF, 0x0F }, /* 49 */ + { 0xFF, 0xFF }, /* 4A */ + { 0xFF, 0xFF }, /* 4B */ + { 0xFF, 0x3F }, /* 4C */ + { 0xFF, 0x3F }, /* 4D */ + { 0xFF, 0x3F }, /* 4E */ + { 0xFF, 0xFF }, /* 4F */ + { 0xFF, 0x7F }, /* 50 */ + { 0xFF, 0x7F }, /* 51 */ + { 0xFF, 0x0F }, /* 52 */ + { 0xFF, 0x3F }, /* 53 */ + { 0xFF, 0x3F }, /* 54 */ + { 0xFF, 0x3F }, /* 55 */ + { 0xFF, 0xFF }, /* 56 */ + { 0xFF, 0xFF }, /* 57 */ + { 0xFF, 0xBF }, /* 58 */ + { 0xFF, 0x1F }, /* 59 */ + { 0xFF, 0xBF }, /* 5A */ + { 0xFF, 0x1F }, /* 5B */ + { 0xFF, 0xBF }, /* 5C */ + { 0xFF, 0x3F }, /* 5D */ + { 0xFF, 0x3F }, /* 5E */ + { 0xFF, 0x7F }, /* 5F */ + { 0xFF, 0x7F }, /* 60 */ + { 0xFF, 0x47 }, /* 61 */ + { 0xFF, 0x9F }, /* 62 */ + { 0xFF, 0x9F }, /* 63 */ + { 0xFF, 0x9F }, /* 64 */ + { 0xFF, 0x9F }, /* 65 */ + { 0xFF, 0x9F }, /* 66 */ + { 0xFF, 0xBF }, /* 67 */ + { 0xFF, 0xBF }, /* 68 */ + { 0xFF, 0xFF }, /* 69 */ + { 0xFF, 0xFF }, /* 6A */ + { 0xFF, 0x7F }, /* 6B */ + { 0xFF, 0xF7 }, /* 6C */ + { 0xFF, 0xFF }, /* 6D */ + { 0xFF, 0xFF }, /* 6E */ + { 0xFF, 0x1F }, /* 6F */ + { 0xFF, 0xF7 }, /* 70 */ + { 0xFF, 0xFF }, /* 71 */ + { 0xFF, 0xFF }, /* 72 */ + { 0xFF, 0x1F }, /* 73 */ + { 0xFF, 0xF7 }, /* 74 */ + { 0xFF, 0xFF }, /* 75 */ + { 0xFF, 0xFF }, /* 76 */ + { 0xFF, 0x1F }, /* 77 */ + { 0xFF, 0xF7 }, /* 78 */ + { 0xFF, 0xFF }, /* 79 */ + { 0xFF, 0xFF }, /* 7A */ + { 0xFF, 0x1F }, /* 7B */ + { 0xFF, 0xF7 }, /* 7C */ + { 0xFF, 0xFF }, /* 7D */ + { 0xFF, 0xFF }, /* 7E */ + { 0xFF, 0x1F }, /* 7F */ + { 0xFF, 0xF7 }, /* 80 */ + { 0xFF, 0xFF }, /* 81 */ + { 0xFF, 0xFF }, /* 82 */ + { 0xFF, 0x1F }, /* 83 */ + { 0xFF, 0x7F }, /* 84 */ + { 0xFF, 0x0F }, /* 85 */ + { 0xFF, 0xD8 }, /* 86 */ + { 0xFF, 0xFF }, /* 87 */ + { 0xFF, 0xEF }, /* 88 */ + { 0xFF, 0xFE }, /* 89 */ + { 0xFF, 0xFE }, /* 8A */ + { 0xFF, 0xFF }, /* 8B */ + { 0xFF, 0xFF }, /* 8C */ + { 0xFF, 0x3F }, /* 8D */ + { 0xFF, 0xFF }, /* 8E */ + { 0xFF, 0x3F }, /* 8F */ + { 0xFF, 0x8F }, /* 90 */ + { 0xFF, 0xFF }, /* 91 */ + { 0xFF, 0x3F }, /* 92 */ + { 0xFF, 0xFF }, /* 93 */ + { 0xFF, 0xFF }, /* 94 */ + { 0xFF, 0x0F }, /* 95 */ + { 0xFF, 0x3F }, /* 96 */ + { 0xFF, 0x8C }, /* 97 */ + { 0x00, 0x00 }, /* 98 */ + { 0x00, 0x00 }, /* 99 */ + { 0x00, 0x00 }, /* 9A */ + { 0x00, 0x00 }, /* 9B */ + { 0x00, 0x00 }, /* 9C */ + { 0x00, 0x00 }, /* 9D */ + { 0x00, 0x00 }, /* 9E */ + { 0x00, 0x00 }, /* 9F */ + { 0x00, 0x00 }, /* A0 */ + { 0x00, 0x00 }, /* A1 */ + { 0x00, 0x00 }, /* A2 */ + { 0x00, 0x00 }, /* A3 */ + { 0x00, 0x00 }, /* A4 */ + { 0x00, 0x00 }, /* A5 */ + { 0x00, 0x00 }, /* A6 */ + { 0x00, 0x00 }, /* A7 */ + { 0x00, 0x00 }, /* A8 */ + { 0x00, 0x00 }, /* A9 */ + { 0x00, 0x00 }, /* AA */ + { 0x00, 0x00 }, /* AB */ + { 0x00, 0x00 }, /* AC */ + { 0x00, 0x00 }, /* AD */ + { 0x00, 0x00 }, /* AE */ + { 0x00, 0x00 }, /* AF */ + { 0x00, 0x00 }, /* B0 */ + { 0x00, 0x00 }, /* B1 */ + { 0x00, 0x00 }, /* B2 */ + { 0x00, 0x00 }, /* B3 */ + { 0x00, 0x00 }, /* B4 */ + { 0x00, 0x00 }, /* B5 */ + { 0x00, 0x00 }, /* B6 */ + { 0x00, 0x00 }, /* B7 */ + { 0x00, 0x00 }, /* B8 */ + { 0x00, 0x00 }, /* B9 */ + { 0x00, 0x00 }, /* BA */ + { 0x00, 0x00 }, /* BB */ + { 0x00, 0x00 }, /* BC */ + { 0x00, 0x00 }, /* BD */ + { 0x00, 0x00 }, /* BE */ + { 0x00, 0x00 }, /* BF */ + { 0x00, 0x00 }, /* C0 */ + { 0x00, 0x00 }, /* C1 */ + { 0x00, 0x00 }, /* C2 */ + { 0x00, 0x00 }, /* C3 */ + { 0x00, 0x00 }, /* C4 */ + { 0x00, 0x00 }, /* C5 */ + { 0x00, 0x00 }, /* C6 */ + { 0x00, 0x00 }, /* C7 */ + { 0x00, 0x00 }, /* C8 */ + { 0x00, 0x00 }, /* C9 */ + { 0x00, 0x00 }, /* CA */ + { 0x00, 0x00 }, /* CB */ + { 0x00, 0x00 }, /* CC */ + { 0x00, 0x00 }, /* CD */ + { 0x00, 0x00 }, /* CE */ + { 0x00, 0x00 }, /* CF */ + { 0x00, 0x00 }, /* D0 */ + { 0x00, 0x00 }, /* D1 */ + { 0x00, 0x00 }, /* D2 */ + { 0x00, 0x00 }, /* D3 */ + { 0x00, 0x00 }, /* D4 */ + { 0x00, 0x00 }, /* D5 */ + { 0x00, 0x00 }, /* D6 */ + { 0x00, 0x00 }, /* D7 */ + { 0x00, 0x00 }, /* D8 */ + { 0x00, 0x00 }, /* D9 */ + { 0x00, 0x00 }, /* DA */ + { 0x00, 0x00 }, /* DB */ + { 0x00, 0x00 }, /* DC */ + { 0x00, 0x00 }, /* DD */ + { 0x00, 0x00 }, /* DE */ + { 0x00, 0x00 }, /* DF */ + { 0x00, 0x00 }, /* E0 */ + { 0x00, 0x00 }, /* E1 */ + { 0x00, 0x00 }, /* E2 */ + { 0x00, 0x00 }, /* E3 */ + { 0x00, 0x00 }, /* E4 */ + { 0x00, 0x00 }, /* E5 */ + { 0x00, 0x00 }, /* E6 */ + { 0x00, 0x00 }, /* E7 */ + { 0x00, 0x00 }, /* E8 */ + { 0x00, 0x00 }, /* E9 */ + { 0x00, 0x00 }, /* EA */ + { 0x00, 0x00 }, /* EB */ + { 0x00, 0x00 }, /* EC */ + { 0x00, 0x00 }, /* ED */ + { 0x00, 0x00 }, /* EE */ + { 0x00, 0x00 }, /* EF */ + { 0x00, 0x00 }, /* F0 */ + { 0x00, 0x00 }, /* F1 */ + { 0x00, 0x00 }, /* F2 */ + { 0x00, 0x00 }, /* F3 */ + { 0x00, 0x00 }, /* F4 */ + { 0x00, 0x00 }, /* F5 */ + { 0x00, 0x00 }, /* F6 */ + { 0x00, 0x00 }, /* F7 */ + { 0x00, 0x00 }, /* F8 */ + { 0x00, 0x00 }, /* F9 */ + { 0x00, 0x00 }, /* FA */ + { 0x00, 0x00 }, /* FB */ + { 0x00, 0x00 }, /* FC */ + { 0x00, 0x00 }, /* FD */ + { 0x00, 0x00 }, /* FE */ + { 0xFF, 0x00 }, /* FF */ +}; + +static int max98095_readable(struct snd_soc_codec *codec, unsigned int reg) +{ + if (reg >= M98095_REG_CNT) + return 0; + return max98095_access[reg].readable != 0; +} + +static int max98095_volatile(struct snd_soc_codec *codec, unsigned int reg) +{ + if (reg > M98095_REG_MAX_CACHED) + return 1; + + switch (reg) { + case M98095_000_HOST_DATA: + case M98095_001_HOST_INT_STS: + case M98095_002_HOST_RSP_STS: + case M98095_003_HOST_CMD_STS: + case M98095_004_CODEC_STS: + case M98095_005_DAI1_ALC_STS: + case M98095_006_DAI2_ALC_STS: + case M98095_007_JACK_AUTO_STS: + case M98095_008_JACK_MANUAL_STS: + case M98095_009_JACK_VBAT_STS: + case M98095_00A_ACC_ADC_STS: + case M98095_00B_MIC_NG_AGC_STS: + case M98095_00C_SPK_L_VOLT_STS: + case M98095_00D_SPK_R_VOLT_STS: + case M98095_00E_TEMP_SENSOR_STS: + return 1; + } + + return 0; +} + +static const char * const max98095_fltr_mode[] = { "Voice", "Music" }; +static const struct soc_enum max98095_dai1_filter_mode_enum[] = { + SOC_ENUM_SINGLE(M98095_02E_DAI1_FILTERS, 7, 2, max98095_fltr_mode), +}; +static const struct soc_enum max98095_dai2_filter_mode_enum[] = { + SOC_ENUM_SINGLE(M98095_038_DAI2_FILTERS, 7, 2, max98095_fltr_mode), +}; + +static const char * const max98095_extmic_text[] = { "None", "MIC1", "MIC2" }; + +static const struct soc_enum max98095_extmic_enum = + SOC_ENUM_SINGLE(M98095_087_CFG_MIC, 0, 3, max98095_extmic_text); + +static const struct snd_kcontrol_new max98095_extmic_mux = + SOC_DAPM_ENUM("External MIC Mux", max98095_extmic_enum); + +static const char * const max98095_linein_text[] = { "INA", "INB" }; + +static const struct soc_enum max98095_linein_enum = + SOC_ENUM_SINGLE(M98095_086_CFG_LINE, 6, 2, max98095_linein_text); + +static const struct snd_kcontrol_new max98095_linein_mux = + SOC_DAPM_ENUM("Linein Input Mux", max98095_linein_enum); + +static const char * const max98095_line_mode_text[] = { + "Stereo", "Differential"}; + +static const struct soc_enum max98095_linein_mode_enum = + SOC_ENUM_SINGLE(M98095_086_CFG_LINE, 7, 2, max98095_line_mode_text); + +static const struct soc_enum max98095_lineout_mode_enum = + SOC_ENUM_SINGLE(M98095_086_CFG_LINE, 4, 2, max98095_line_mode_text); + +static const char * const max98095_dai_fltr[] = { + "Off", "Elliptical-HPF-16k", "Butterworth-HPF-16k", + "Elliptical-HPF-8k", "Butterworth-HPF-8k", "Butterworth-HPF-Fs/240"}; +static const struct soc_enum max98095_dai1_dac_filter_enum[] = { + SOC_ENUM_SINGLE(M98095_02E_DAI1_FILTERS, 0, 6, max98095_dai_fltr), +}; +static const struct soc_enum max98095_dai2_dac_filter_enum[] = { + SOC_ENUM_SINGLE(M98095_038_DAI2_FILTERS, 0, 6, max98095_dai_fltr), +}; +static const struct soc_enum max98095_dai3_dac_filter_enum[] = { + SOC_ENUM_SINGLE(M98095_042_DAI3_FILTERS, 0, 6, max98095_dai_fltr), +}; + +static int max98095_mic1pre_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); + unsigned int sel = ucontrol->value.integer.value[0]; + + max98095->mic1pre = sel; + snd_soc_update_bits(codec, M98095_05F_LVL_MIC1, M98095_MICPRE_MASK, + (1+sel)<<M98095_MICPRE_SHIFT); + + return 0; +} + +static int max98095_mic1pre_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = max98095->mic1pre; + return 0; +} + +static int max98095_mic2pre_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); + unsigned int sel = ucontrol->value.integer.value[0]; + + max98095->mic2pre = sel; + snd_soc_update_bits(codec, M98095_060_LVL_MIC2, M98095_MICPRE_MASK, + (1+sel)<<M98095_MICPRE_SHIFT); + + return 0; +} + +static int max98095_mic2pre_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = max98095->mic2pre; + return 0; +} + +static const unsigned int max98095_micboost_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0), + 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0), +}; + +static const DECLARE_TLV_DB_SCALE(max98095_mic_tlv, 0, 100, 0); +static const DECLARE_TLV_DB_SCALE(max98095_adc_tlv, -1200, 100, 0); +static const DECLARE_TLV_DB_SCALE(max98095_adcboost_tlv, 0, 600, 0); + +static const unsigned int max98095_hp_tlv[] = { + TLV_DB_RANGE_HEAD(5), + 0, 6, TLV_DB_SCALE_ITEM(-6700, 400, 0), + 7, 14, TLV_DB_SCALE_ITEM(-4000, 300, 0), + 15, 21, TLV_DB_SCALE_ITEM(-1700, 200, 0), + 22, 27, TLV_DB_SCALE_ITEM(-400, 100, 0), + 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0), +}; + +static const unsigned int max98095_spk_tlv[] = { + TLV_DB_RANGE_HEAD(4), + 0, 10, TLV_DB_SCALE_ITEM(-5900, 400, 0), + 11, 18, TLV_DB_SCALE_ITEM(-1700, 200, 0), + 19, 27, TLV_DB_SCALE_ITEM(-200, 100, 0), + 28, 39, TLV_DB_SCALE_ITEM(650, 50, 0), +}; + +static const unsigned int max98095_rcv_lout_tlv[] = { + TLV_DB_RANGE_HEAD(5), + 0, 6, TLV_DB_SCALE_ITEM(-6200, 400, 0), + 7, 14, TLV_DB_SCALE_ITEM(-3500, 300, 0), + 15, 21, TLV_DB_SCALE_ITEM(-1200, 200, 0), + 22, 27, TLV_DB_SCALE_ITEM(100, 100, 0), + 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0), +}; + +static const unsigned int max98095_lin_tlv[] = { + TLV_DB_RANGE_HEAD(3), + 0, 2, TLV_DB_SCALE_ITEM(-600, 300, 0), + 3, 3, TLV_DB_SCALE_ITEM(300, 1100, 0), + 4, 5, TLV_DB_SCALE_ITEM(1400, 600, 0), +}; + +static const struct snd_kcontrol_new max98095_snd_controls[] = { + + SOC_DOUBLE_R_TLV("Headphone Volume", M98095_064_LVL_HP_L, + M98095_065_LVL_HP_R, 0, 31, 0, max98095_hp_tlv), + + SOC_DOUBLE_R_TLV("Speaker Volume", M98095_067_LVL_SPK_L, + M98095_068_LVL_SPK_R, 0, 39, 0, max98095_spk_tlv), + + SOC_SINGLE_TLV("Receiver Volume", M98095_066_LVL_RCV, + 0, 31, 0, max98095_rcv_lout_tlv), + + SOC_DOUBLE_R_TLV("Lineout Volume", M98095_062_LVL_LINEOUT1, + M98095_063_LVL_LINEOUT2, 0, 31, 0, max98095_rcv_lout_tlv), + + SOC_DOUBLE_R("Headphone Switch", M98095_064_LVL_HP_L, + M98095_065_LVL_HP_R, 7, 1, 1), + + SOC_DOUBLE_R("Speaker Switch", M98095_067_LVL_SPK_L, + M98095_068_LVL_SPK_R, 7, 1, 1), + + SOC_SINGLE("Receiver Switch", M98095_066_LVL_RCV, 7, 1, 1), + + SOC_DOUBLE_R("Lineout Switch", M98095_062_LVL_LINEOUT1, + M98095_063_LVL_LINEOUT2, 7, 1, 1), + + SOC_SINGLE_TLV("MIC1 Volume", M98095_05F_LVL_MIC1, 0, 20, 1, + max98095_mic_tlv), + + SOC_SINGLE_TLV("MIC2 Volume", M98095_060_LVL_MIC2, 0, 20, 1, + max98095_mic_tlv), + + SOC_SINGLE_EXT_TLV("MIC1 Boost Volume", + M98095_05F_LVL_MIC1, 5, 2, 0, + max98095_mic1pre_get, max98095_mic1pre_set, + max98095_micboost_tlv), + SOC_SINGLE_EXT_TLV("MIC2 Boost Volume", + M98095_060_LVL_MIC2, 5, 2, 0, + max98095_mic2pre_get, max98095_mic2pre_set, + max98095_micboost_tlv), + + SOC_SINGLE_TLV("Linein Volume", M98095_061_LVL_LINEIN, 0, 5, 1, + max98095_lin_tlv), + + SOC_SINGLE_TLV("ADCL Volume", M98095_05D_LVL_ADC_L, 0, 15, 1, + max98095_adc_tlv), + SOC_SINGLE_TLV("ADCR Volume", M98095_05E_LVL_ADC_R, 0, 15, 1, + max98095_adc_tlv), + + SOC_SINGLE_TLV("ADCL Boost Volume", M98095_05D_LVL_ADC_L, 4, 3, 0, + max98095_adcboost_tlv), + SOC_SINGLE_TLV("ADCR Boost Volume", M98095_05E_LVL_ADC_R, 4, 3, 0, + max98095_adcboost_tlv), + + SOC_ENUM("DAI1 Filter Mode", max98095_dai1_filter_mode_enum), + SOC_ENUM("DAI2 Filter Mode", max98095_dai2_filter_mode_enum), + SOC_ENUM("DAI1 DAC Filter", max98095_dai1_dac_filter_enum), + SOC_ENUM("DAI2 DAC Filter", max98095_dai2_dac_filter_enum), + SOC_ENUM("DAI3 DAC Filter", max98095_dai3_dac_filter_enum), + + SOC_ENUM("Linein Mode", max98095_linein_mode_enum), + SOC_ENUM("Lineout Mode", max98095_lineout_mode_enum), +}; + +/* Left speaker mixer switch */ +static const struct snd_kcontrol_new max98095_left_speaker_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_050_MIX_SPK_LEFT, 0, 1, 0), + SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_050_MIX_SPK_LEFT, 6, 1, 0), + SOC_DAPM_SINGLE("Mono DAC2 Switch", M98095_050_MIX_SPK_LEFT, 3, 1, 0), + SOC_DAPM_SINGLE("Mono DAC3 Switch", M98095_050_MIX_SPK_LEFT, 3, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98095_050_MIX_SPK_LEFT, 4, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98095_050_MIX_SPK_LEFT, 5, 1, 0), + SOC_DAPM_SINGLE("IN1 Switch", M98095_050_MIX_SPK_LEFT, 1, 1, 0), + SOC_DAPM_SINGLE("IN2 Switch", M98095_050_MIX_SPK_LEFT, 2, 1, 0), +}; + +/* Right speaker mixer switch */ +static const struct snd_kcontrol_new max98095_right_speaker_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_051_MIX_SPK_RIGHT, 6, 1, 0), + SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_051_MIX_SPK_RIGHT, 0, 1, 0), + SOC_DAPM_SINGLE("Mono DAC2 Switch", M98095_051_MIX_SPK_RIGHT, 3, 1, 0), + SOC_DAPM_SINGLE("Mono DAC3 Switch", M98095_051_MIX_SPK_RIGHT, 3, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98095_051_MIX_SPK_RIGHT, 5, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98095_051_MIX_SPK_RIGHT, 4, 1, 0), + SOC_DAPM_SINGLE("IN1 Switch", M98095_051_MIX_SPK_RIGHT, 1, 1, 0), + SOC_DAPM_SINGLE("IN2 Switch", M98095_051_MIX_SPK_RIGHT, 2, 1, 0), +}; + +/* Left headphone mixer switch */ +static const struct snd_kcontrol_new max98095_left_hp_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_04C_MIX_HP_LEFT, 0, 1, 0), + SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_04C_MIX_HP_LEFT, 5, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98095_04C_MIX_HP_LEFT, 3, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98095_04C_MIX_HP_LEFT, 4, 1, 0), + SOC_DAPM_SINGLE("IN1 Switch", M98095_04C_MIX_HP_LEFT, 1, 1, 0), + SOC_DAPM_SINGLE("IN2 Switch", M98095_04C_MIX_HP_LEFT, 2, 1, 0), +}; + +/* Right headphone mixer switch */ +static const struct snd_kcontrol_new max98095_right_hp_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_04D_MIX_HP_RIGHT, 5, 1, 0), + SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_04D_MIX_HP_RIGHT, 0, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98095_04D_MIX_HP_RIGHT, 3, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98095_04D_MIX_HP_RIGHT, 4, 1, 0), + SOC_DAPM_SINGLE("IN1 Switch", M98095_04D_MIX_HP_RIGHT, 1, 1, 0), + SOC_DAPM_SINGLE("IN2 Switch", M98095_04D_MIX_HP_RIGHT, 2, 1, 0), +}; + +/* Receiver earpiece mixer switch */ +static const struct snd_kcontrol_new max98095_mono_rcv_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_04F_MIX_RCV, 0, 1, 0), + SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_04F_MIX_RCV, 5, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98095_04F_MIX_RCV, 3, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98095_04F_MIX_RCV, 4, 1, 0), + SOC_DAPM_SINGLE("IN1 Switch", M98095_04F_MIX_RCV, 1, 1, 0), + SOC_DAPM_SINGLE("IN2 Switch", M98095_04F_MIX_RCV, 2, 1, 0), +}; + +/* Left lineout mixer switch */ +static const struct snd_kcontrol_new max98095_left_lineout_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_053_MIX_LINEOUT1, 5, 1, 0), + SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_053_MIX_LINEOUT1, 0, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98095_053_MIX_LINEOUT1, 3, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98095_053_MIX_LINEOUT1, 4, 1, 0), + SOC_DAPM_SINGLE("IN1 Switch", M98095_053_MIX_LINEOUT1, 1, 1, 0), + SOC_DAPM_SINGLE("IN2 Switch", M98095_053_MIX_LINEOUT1, 2, 1, 0), +}; + +/* Right lineout mixer switch */ +static const struct snd_kcontrol_new max98095_right_lineout_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_054_MIX_LINEOUT2, 0, 1, 0), + SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_054_MIX_LINEOUT2, 5, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98095_054_MIX_LINEOUT2, 3, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98095_054_MIX_LINEOUT2, 4, 1, 0), + SOC_DAPM_SINGLE("IN1 Switch", M98095_054_MIX_LINEOUT2, 1, 1, 0), + SOC_DAPM_SINGLE("IN2 Switch", M98095_054_MIX_LINEOUT2, 2, 1, 0), +}; + +/* Left ADC mixer switch */ +static const struct snd_kcontrol_new max98095_left_ADC_mixer_controls[] = { + SOC_DAPM_SINGLE("MIC1 Switch", M98095_04A_MIX_ADC_LEFT, 7, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98095_04A_MIX_ADC_LEFT, 6, 1, 0), + SOC_DAPM_SINGLE("IN1 Switch", M98095_04A_MIX_ADC_LEFT, 3, 1, 0), + SOC_DAPM_SINGLE("IN2 Switch", M98095_04A_MIX_ADC_LEFT, 2, 1, 0), +}; + +/* Right ADC mixer switch */ +static const struct snd_kcontrol_new max98095_right_ADC_mixer_controls[] = { + SOC_DAPM_SINGLE("MIC1 Switch", M98095_04B_MIX_ADC_RIGHT, 7, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98095_04B_MIX_ADC_RIGHT, 6, 1, 0), + SOC_DAPM_SINGLE("IN1 Switch", M98095_04B_MIX_ADC_RIGHT, 3, 1, 0), + SOC_DAPM_SINGLE("IN2 Switch", M98095_04B_MIX_ADC_RIGHT, 2, 1, 0), +}; + +static int max98095_mic_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (w->reg == M98095_05F_LVL_MIC1) { + snd_soc_update_bits(codec, w->reg, M98095_MICPRE_MASK, + (1+max98095->mic1pre)<<M98095_MICPRE_SHIFT); + } else { + snd_soc_update_bits(codec, w->reg, M98095_MICPRE_MASK, + (1+max98095->mic2pre)<<M98095_MICPRE_SHIFT); + } + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, w->reg, M98095_MICPRE_MASK, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +/* + * The line inputs are stereo inputs with the left and right + * channels sharing a common PGA power control signal. + */ +static int max98095_line_pga(struct snd_soc_dapm_widget *w, + int event, u8 channel) +{ + struct snd_soc_codec *codec = w->codec; + struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); + u8 *state; + + BUG_ON(!((channel == 1) || (channel == 2))); + + state = &max98095->lin_state; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + *state |= channel; + snd_soc_update_bits(codec, w->reg, + (1 << w->shift), (1 << w->shift)); + break; + case SND_SOC_DAPM_POST_PMD: + *state &= ~channel; + if (*state == 0) { + snd_soc_update_bits(codec, w->reg, + (1 << w->shift), 0); + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static int max98095_pga_in1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + return max98095_line_pga(w, event, 1); +} + +static int max98095_pga_in2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + return max98095_line_pga(w, event, 2); +} + +/* + * The stereo line out mixer outputs to two stereo line outs. + * The 2nd pair has a separate set of enables. + */ +static int max98095_lineout_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, w->reg, + (1 << (w->shift+2)), (1 << (w->shift+2))); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, w->reg, + (1 << (w->shift+2)), 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dapm_widget max98095_dapm_widgets[] = { + + SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", M98095_090_PWR_EN_IN, 0, 0), + SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", M98095_090_PWR_EN_IN, 1, 0), + + SND_SOC_DAPM_DAC("DACL1", "HiFi Playback", + M98095_091_PWR_EN_OUT, 0, 0), + SND_SOC_DAPM_DAC("DACR1", "HiFi Playback", + M98095_091_PWR_EN_OUT, 1, 0), + SND_SOC_DAPM_DAC("DACM2", "Aux Playback", + M98095_091_PWR_EN_OUT, 2, 0), + SND_SOC_DAPM_DAC("DACM3", "Voice Playback", + M98095_091_PWR_EN_OUT, 2, 0), + + SND_SOC_DAPM_PGA("HP Left Out", M98095_091_PWR_EN_OUT, + 6, 0, NULL, 0), + SND_SOC_DAPM_PGA("HP Right Out", M98095_091_PWR_EN_OUT, + 7, 0, NULL, 0), + + SND_SOC_DAPM_PGA("SPK Left Out", M98095_091_PWR_EN_OUT, + 4, 0, NULL, 0), + SND_SOC_DAPM_PGA("SPK Right Out", M98095_091_PWR_EN_OUT, + 5, 0, NULL, 0), + + SND_SOC_DAPM_PGA("RCV Mono Out", M98095_091_PWR_EN_OUT, + 3, 0, NULL, 0), + + SND_SOC_DAPM_PGA_E("LINE Left Out", M98095_092_PWR_EN_OUT, + 0, 0, NULL, 0, max98095_lineout_event, SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_E("LINE Right Out", M98095_092_PWR_EN_OUT, + 1, 0, NULL, 0, max98095_lineout_event, SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_MUX("External MIC", SND_SOC_NOPM, 0, 0, + &max98095_extmic_mux), + + SND_SOC_DAPM_MUX("Linein Mux", SND_SOC_NOPM, 0, 0, + &max98095_linein_mux), + + SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0, + &max98095_left_hp_mixer_controls[0], + ARRAY_SIZE(max98095_left_hp_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right Headphone Mixer", SND_SOC_NOPM, 0, 0, + &max98095_right_hp_mixer_controls[0], + ARRAY_SIZE(max98095_right_hp_mixer_controls)), + + SND_SOC_DAPM_MIXER("Left Speaker Mixer", SND_SOC_NOPM, 0, 0, + &max98095_left_speaker_mixer_controls[0], + ARRAY_SIZE(max98095_left_speaker_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right Speaker Mixer", SND_SOC_NOPM, 0, 0, + &max98095_right_speaker_mixer_controls[0], + ARRAY_SIZE(max98095_right_speaker_mixer_controls)), + + SND_SOC_DAPM_MIXER("Receiver Mixer", SND_SOC_NOPM, 0, 0, + &max98095_mono_rcv_mixer_controls[0], + ARRAY_SIZE(max98095_mono_rcv_mixer_controls)), + + SND_SOC_DAPM_MIXER("Left Lineout Mixer", SND_SOC_NOPM, 0, 0, + &max98095_left_lineout_mixer_controls[0], + ARRAY_SIZE(max98095_left_lineout_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right Lineout Mixer", SND_SOC_NOPM, 0, 0, + &max98095_right_lineout_mixer_controls[0], + ARRAY_SIZE(max98095_right_lineout_mixer_controls)), + + SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0, + &max98095_left_ADC_mixer_controls[0], + ARRAY_SIZE(max98095_left_ADC_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0, + &max98095_right_ADC_mixer_controls[0], + ARRAY_SIZE(max98095_right_ADC_mixer_controls)), + + SND_SOC_DAPM_PGA_E("MIC1 Input", M98095_05F_LVL_MIC1, + 5, 0, NULL, 0, max98095_mic_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_E("MIC2 Input", M98095_060_LVL_MIC2, + 5, 0, NULL, 0, max98095_mic_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_E("IN1 Input", M98095_090_PWR_EN_IN, + 7, 0, NULL, 0, max98095_pga_in1_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_E("IN2 Input", M98095_090_PWR_EN_IN, + 7, 0, NULL, 0, max98095_pga_in2_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MICBIAS("MICBIAS1", M98095_090_PWR_EN_IN, 2, 0), + SND_SOC_DAPM_MICBIAS("MICBIAS2", M98095_090_PWR_EN_IN, 3, 0), + + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("SPKL"), + SND_SOC_DAPM_OUTPUT("SPKR"), + SND_SOC_DAPM_OUTPUT("RCV"), + SND_SOC_DAPM_OUTPUT("OUT1"), + SND_SOC_DAPM_OUTPUT("OUT2"), + SND_SOC_DAPM_OUTPUT("OUT3"), + SND_SOC_DAPM_OUTPUT("OUT4"), + + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("INA1"), + SND_SOC_DAPM_INPUT("INA2"), + SND_SOC_DAPM_INPUT("INB1"), + SND_SOC_DAPM_INPUT("INB2"), +}; + +static const struct snd_soc_dapm_route max98095_audio_map[] = { + /* Left headphone output mixer */ + {"Left Headphone Mixer", "Left DAC1 Switch", "DACL1"}, + {"Left Headphone Mixer", "Right DAC1 Switch", "DACR1"}, + {"Left Headphone Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left Headphone Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Left Headphone Mixer", "IN1 Switch", "IN1 Input"}, + {"Left Headphone Mixer", "IN2 Switch", "IN2 Input"}, + + /* Right headphone output mixer */ + {"Right Headphone Mixer", "Left DAC1 Switch", "DACL1"}, + {"Right Headphone Mixer", "Right DAC1 Switch", "DACR1"}, + {"Right Headphone Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right Headphone Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Right Headphone Mixer", "IN1 Switch", "IN1 Input"}, + {"Right Headphone Mixer", "IN2 Switch", "IN2 Input"}, + + /* Left speaker output mixer */ + {"Left Speaker Mixer", "Left DAC1 Switch", "DACL1"}, + {"Left Speaker Mixer", "Right DAC1 Switch", "DACR1"}, + {"Left Speaker Mixer", "Mono DAC2 Switch", "DACM2"}, + {"Left Speaker Mixer", "Mono DAC3 Switch", "DACM3"}, + {"Left Speaker Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left Speaker Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Left Speaker Mixer", "IN1 Switch", "IN1 Input"}, + {"Left Speaker Mixer", "IN2 Switch", "IN2 Input"}, + + /* Right speaker output mixer */ + {"Right Speaker Mixer", "Left DAC1 Switch", "DACL1"}, + {"Right Speaker Mixer", "Right DAC1 Switch", "DACR1"}, + {"Right Speaker Mixer", "Mono DAC2 Switch", "DACM2"}, + {"Right Speaker Mixer", "Mono DAC3 Switch", "DACM3"}, + {"Right Speaker Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right Speaker Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Right Speaker Mixer", "IN1 Switch", "IN1 Input"}, + {"Right Speaker Mixer", "IN2 Switch", "IN2 Input"}, + + /* Earpiece/Receiver output mixer */ + {"Receiver Mixer", "Left DAC1 Switch", "DACL1"}, + {"Receiver Mixer", "Right DAC1 Switch", "DACR1"}, + {"Receiver Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Receiver Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Receiver Mixer", "IN1 Switch", "IN1 Input"}, + {"Receiver Mixer", "IN2 Switch", "IN2 Input"}, + + /* Left Lineout output mixer */ + {"Left Lineout Mixer", "Left DAC1 Switch", "DACL1"}, + {"Left Lineout Mixer", "Right DAC1 Switch", "DACR1"}, + {"Left Lineout Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left Lineout Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Left Lineout Mixer", "IN1 Switch", "IN1 Input"}, + {"Left Lineout Mixer", "IN2 Switch", "IN2 Input"}, + + /* Right lineout output mixer */ + {"Right Lineout Mixer", "Left DAC1 Switch", "DACL1"}, + {"Right Lineout Mixer", "Right DAC1 Switch", "DACR1"}, + {"Right Lineout Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right Lineout Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Right Lineout Mixer", "IN1 Switch", "IN1 Input"}, + {"Right Lineout Mixer", "IN2 Switch", "IN2 Input"}, + + {"HP Left Out", NULL, "Left Headphone Mixer"}, + {"HP Right Out", NULL, "Right Headphone Mixer"}, + {"SPK Left Out", NULL, "Left Speaker Mixer"}, + {"SPK Right Out", NULL, "Right Speaker Mixer"}, + {"RCV Mono Out", NULL, "Receiver Mixer"}, + {"LINE Left Out", NULL, "Left Lineout Mixer"}, + {"LINE Right Out", NULL, "Right Lineout Mixer"}, + + {"HPL", NULL, "HP Left Out"}, + {"HPR", NULL, "HP Right Out"}, + {"SPKL", NULL, "SPK Left Out"}, + {"SPKR", NULL, "SPK Right Out"}, + {"RCV", NULL, "RCV Mono Out"}, + {"OUT1", NULL, "LINE Left Out"}, + {"OUT2", NULL, "LINE Right Out"}, + {"OUT3", NULL, "LINE Left Out"}, + {"OUT4", NULL, "LINE Right Out"}, + + /* Left ADC input mixer */ + {"Left ADC Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left ADC Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Left ADC Mixer", "IN1 Switch", "IN1 Input"}, + {"Left ADC Mixer", "IN2 Switch", "IN2 Input"}, + + /* Right ADC input mixer */ + {"Right ADC Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right ADC Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Right ADC Mixer", "IN1 Switch", "IN1 Input"}, + {"Right ADC Mixer", "IN2 Switch", "IN2 Input"}, + + /* Inputs */ + {"ADCL", NULL, "Left ADC Mixer"}, + {"ADCR", NULL, "Right ADC Mixer"}, + + {"IN1 Input", NULL, "INA1"}, + {"IN2 Input", NULL, "INA2"}, + + {"MIC1 Input", NULL, "MIC1"}, + {"MIC2 Input", NULL, "MIC2"}, +}; + +static int max98095_add_widgets(struct snd_soc_codec *codec) +{ + snd_soc_add_controls(codec, max98095_snd_controls, + ARRAY_SIZE(max98095_snd_controls)); + + return 0; +} + +/* codec mclk clock divider coefficients */ +static const struct { + u32 rate; + u8 sr; +} rate_table[] = { + {8000, 0x01}, + {11025, 0x02}, + {16000, 0x03}, + {22050, 0x04}, + {24000, 0x05}, + {32000, 0x06}, + {44100, 0x07}, + {48000, 0x08}, + {88200, 0x09}, + {96000, 0x0A}, +}; + +static int rate_value(int rate, u8 *value) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rate_table); i++) { + if (rate_table[i].rate >= rate) { + *value = rate_table[i].sr; + return 0; + } + } + *value = rate_table[0].sr; + return -EINVAL; +} + +static int max98095_dai1_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 max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); + struct max98095_cdata *cdata; + unsigned long long ni; + unsigned int rate; + u8 regval; + + cdata = &max98095->dai[0]; + + rate = params_rate(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT, + M98095_DAI_WS, 0); + break; + case SNDRV_PCM_FORMAT_S24_LE: + snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT, + M98095_DAI_WS, M98095_DAI_WS); + break; + default: + return -EINVAL; + } + + if (rate_value(rate, ®val)) + return -EINVAL; + + snd_soc_update_bits(codec, M98095_027_DAI1_CLKMODE, + M98095_CLKMODE_MASK, regval); + cdata->rate = rate; + + /* Configure NI when operating as master */ + if (snd_soc_read(codec, M98095_02A_DAI1_FORMAT) & M98095_DAI_MAS) { + if (max98095->sysclk == 0) { + dev_err(codec->dev, "Invalid system clock frequency\n"); + return -EINVAL; + } + ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL) + * (unsigned long long int)rate; + do_div(ni, (unsigned long long int)max98095->sysclk); + snd_soc_write(codec, M98095_028_DAI1_CLKCFG_HI, + (ni >> 8) & 0x7F); + snd_soc_write(codec, M98095_029_DAI1_CLKCFG_LO, + ni & 0xFF); + } + + /* Update sample rate mode */ + if (rate < 50000) + snd_soc_update_bits(codec, M98095_02E_DAI1_FILTERS, + M98095_DAI_DHF, 0); + else + snd_soc_update_bits(codec, M98095_02E_DAI1_FILTERS, + M98095_DAI_DHF, M98095_DAI_DHF); + + return 0; +} + +static int max98095_dai2_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 max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); + struct max98095_cdata *cdata; + unsigned long long ni; + unsigned int rate; + u8 regval; + + cdata = &max98095->dai[1]; + + rate = params_rate(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + snd_soc_update_bits(codec, M98095_034_DAI2_FORMAT, + M98095_DAI_WS, 0); + break; + case SNDRV_PCM_FORMAT_S24_LE: + snd_soc_update_bits(codec, M98095_034_DAI2_FORMAT, + M98095_DAI_WS, M98095_DAI_WS); + break; + default: + return -EINVAL; + } + + if (rate_value(rate, ®val)) + return -EINVAL; + + snd_soc_update_bits(codec, M98095_031_DAI2_CLKMODE, + M98095_CLKMODE_MASK, regval); + cdata->rate = rate; + + /* Configure NI when operating as master */ + if (snd_soc_read(codec, M98095_034_DAI2_FORMAT) & M98095_DAI_MAS) { + if (max98095->sysclk == 0) { + dev_err(codec->dev, "Invalid system clock frequency\n"); + return -EINVAL; + } + ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL) + * (unsigned long long int)rate; + do_div(ni, (unsigned long long int)max98095->sysclk); + snd_soc_write(codec, M98095_032_DAI2_CLKCFG_HI, + (ni >> 8) & 0x7F); + snd_soc_write(codec, M98095_033_DAI2_CLKCFG_LO, + ni & 0xFF); + } + + /* Update sample rate mode */ + if (rate < 50000) + snd_soc_update_bits(codec, M98095_038_DAI2_FILTERS, + M98095_DAI_DHF, 0); + else + snd_soc_update_bits(codec, M98095_038_DAI2_FILTERS, + M98095_DAI_DHF, M98095_DAI_DHF); + + return 0; +} + +static int max98095_dai3_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 max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); + struct max98095_cdata *cdata; + unsigned long long ni; + unsigned int rate; + u8 regval; + + cdata = &max98095->dai[2]; + + rate = params_rate(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + snd_soc_update_bits(codec, M98095_03E_DAI3_FORMAT, + M98095_DAI_WS, 0); + break; + case SNDRV_PCM_FORMAT_S24_LE: + snd_soc_update_bits(codec, M98095_03E_DAI3_FORMAT, + M98095_DAI_WS, M98095_DAI_WS); + break; + default: + return -EINVAL; + } + + if (rate_value(rate, ®val)) + return -EINVAL; + + snd_soc_update_bits(codec, M98095_03B_DAI3_CLKMODE, + M98095_CLKMODE_MASK, regval); + cdata->rate = rate; + + /* Configure NI when operating as master */ + if (snd_soc_read(codec, M98095_03E_DAI3_FORMAT) & M98095_DAI_MAS) { + if (max98095->sysclk == 0) { + dev_err(codec->dev, "Invalid system clock frequency\n"); + return -EINVAL; + } + ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL) + * (unsigned long long int)rate; + do_div(ni, (unsigned long long int)max98095->sysclk); + snd_soc_write(codec, M98095_03C_DAI3_CLKCFG_HI, + (ni >> 8) & 0x7F); + snd_soc_write(codec, M98095_03D_DAI3_CLKCFG_LO, + ni & 0xFF); + } + + /* Update sample rate mode */ + if (rate < 50000) + snd_soc_update_bits(codec, M98095_042_DAI3_FILTERS, + M98095_DAI_DHF, 0); + else + snd_soc_update_bits(codec, M98095_042_DAI3_FILTERS, + M98095_DAI_DHF, M98095_DAI_DHF); + + return 0; +} + +static int max98095_dai_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); + + /* Requested clock frequency is already setup */ + if (freq == max98095->sysclk) + return 0; + + max98095->sysclk = freq; /* remember current sysclk */ + + /* Setup clocks for slave mode, and using the PLL + * PSCLK = 0x01 (when master clk is 10MHz to 20MHz) + * 0x02 (when master clk is 20MHz to 40MHz).. + * 0x03 (when master clk is 40MHz to 60MHz).. + */ + if ((freq >= 10000000) && (freq < 20000000)) { + snd_soc_write(codec, M98095_026_SYS_CLK, 0x10); + } else if ((freq >= 20000000) && (freq < 40000000)) { + snd_soc_write(codec, M98095_026_SYS_CLK, 0x20); + } else if ((freq >= 40000000) && (freq < 60000000)) { + snd_soc_write(codec, M98095_026_SYS_CLK, 0x30); + } else { + dev_err(codec->dev, "Invalid master clock frequency\n"); + return -EINVAL; + } + + dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq); + + max98095->sysclk = freq; + return 0; +} + +static int max98095_dai1_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); + struct max98095_cdata *cdata; + u8 regval = 0; + + cdata = &max98095->dai[0]; + + if (fmt != cdata->fmt) { + cdata->fmt = fmt; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* Slave mode PLL */ + snd_soc_write(codec, M98095_028_DAI1_CLKCFG_HI, + 0x80); + snd_soc_write(codec, M98095_029_DAI1_CLKCFG_LO, + 0x00); + break; + case SND_SOC_DAIFMT_CBM_CFM: + /* Set to master mode */ + regval |= M98095_DAI_MAS; + break; + case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBM_CFS: + default: + dev_err(codec->dev, "Clock mode unsupported"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + regval |= M98095_DAI_DLY; + break; + case SND_SOC_DAIFMT_LEFT_J: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + regval |= M98095_DAI_WCI; + break; + case SND_SOC_DAIFMT_IB_NF: + regval |= M98095_DAI_BCI; + break; + case SND_SOC_DAIFMT_IB_IF: + regval |= M98095_DAI_BCI|M98095_DAI_WCI; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT, + M98095_DAI_MAS | M98095_DAI_DLY | M98095_DAI_BCI | + M98095_DAI_WCI, regval); + + snd_soc_write(codec, M98095_02B_DAI1_CLOCK, M98095_DAI_BSEL64); + } + + return 0; +} + +static int max98095_dai2_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); + struct max98095_cdata *cdata; + u8 regval = 0; + + cdata = &max98095->dai[1]; + + if (fmt != cdata->fmt) { + cdata->fmt = fmt; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* Slave mode PLL */ + snd_soc_write(codec, M98095_032_DAI2_CLKCFG_HI, + 0x80); + snd_soc_write(codec, M98095_033_DAI2_CLKCFG_LO, + 0x00); + break; + case SND_SOC_DAIFMT_CBM_CFM: + /* Set to master mode */ + regval |= M98095_DAI_MAS; + break; + case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBM_CFS: + default: + dev_err(codec->dev, "Clock mode unsupported"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + regval |= M98095_DAI_DLY; + break; + case SND_SOC_DAIFMT_LEFT_J: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + regval |= M98095_DAI_WCI; + break; + case SND_SOC_DAIFMT_IB_NF: + regval |= M98095_DAI_BCI; + break; + case SND_SOC_DAIFMT_IB_IF: + regval |= M98095_DAI_BCI|M98095_DAI_WCI; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, M98095_034_DAI2_FORMAT, + M98095_DAI_MAS | M98095_DAI_DLY | M98095_DAI_BCI | + M98095_DAI_WCI, regval); + + snd_soc_write(codec, M98095_035_DAI2_CLOCK, + M98095_DAI_BSEL64); + } + + return 0; +} + +static int max98095_dai3_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); + struct max98095_cdata *cdata; + u8 regval = 0; + + cdata = &max98095->dai[2]; + + if (fmt != cdata->fmt) { + cdata->fmt = fmt; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* Slave mode PLL */ + snd_soc_write(codec, M98095_03C_DAI3_CLKCFG_HI, + 0x80); + snd_soc_write(codec, M98095_03D_DAI3_CLKCFG_LO, + 0x00); + break; + case SND_SOC_DAIFMT_CBM_CFM: + /* Set to master mode */ + regval |= M98095_DAI_MAS; + break; + case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBM_CFS: + default: + dev_err(codec->dev, "Clock mode unsupported"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + regval |= M98095_DAI_DLY; + break; + case SND_SOC_DAIFMT_LEFT_J: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + regval |= M98095_DAI_WCI; + break; + case SND_SOC_DAIFMT_IB_NF: + regval |= M98095_DAI_BCI; + break; + case SND_SOC_DAIFMT_IB_IF: + regval |= M98095_DAI_BCI|M98095_DAI_WCI; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, M98095_03E_DAI3_FORMAT, + M98095_DAI_MAS | M98095_DAI_DLY | M98095_DAI_BCI | + M98095_DAI_WCI, regval); + + snd_soc_write(codec, M98095_03F_DAI3_CLOCK, + M98095_DAI_BSEL64); + } + + return 0; +} + +static int max98095_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + int ret; + + 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 = snd_soc_cache_sync(codec); + + if (ret != 0) { + dev_err(codec->dev, "Failed to sync cache: %d\n", ret); + return ret; + } + } + + snd_soc_update_bits(codec, M98095_090_PWR_EN_IN, + M98095_MBEN, M98095_MBEN); + break; + + case SND_SOC_BIAS_OFF: + snd_soc_update_bits(codec, M98095_090_PWR_EN_IN, + M98095_MBEN, 0); + codec->cache_sync = 1; + break; + } + codec->dapm.bias_level = level; + return 0; +} + +#define MAX98095_RATES SNDRV_PCM_RATE_8000_96000 +#define MAX98095_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_ops max98095_dai1_ops = { + .set_sysclk = max98095_dai_set_sysclk, + .set_fmt = max98095_dai1_set_fmt, + .hw_params = max98095_dai1_hw_params, +}; + +static struct snd_soc_dai_ops max98095_dai2_ops = { + .set_sysclk = max98095_dai_set_sysclk, + .set_fmt = max98095_dai2_set_fmt, + .hw_params = max98095_dai2_hw_params, +}; + +static struct snd_soc_dai_ops max98095_dai3_ops = { + .set_sysclk = max98095_dai_set_sysclk, + .set_fmt = max98095_dai3_set_fmt, + .hw_params = max98095_dai3_hw_params, +}; + +static struct snd_soc_dai_driver max98095_dai[] = { +{ + .name = "HiFi", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98095_RATES, + .formats = MAX98095_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98095_RATES, + .formats = MAX98095_FORMATS, + }, + .ops = &max98095_dai1_ops, +}, +{ + .name = "Aux", + .playback = { + .stream_name = "Aux Playback", + .channels_min = 1, + .channels_max = 1, + .rates = MAX98095_RATES, + .formats = MAX98095_FORMATS, + }, + .ops = &max98095_dai2_ops, +}, +{ + .name = "Voice", + .playback = { + .stream_name = "Voice Playback", + .channels_min = 1, + .channels_max = 1, + .rates = MAX98095_RATES, + .formats = MAX98095_FORMATS, + }, + .ops = &max98095_dai3_ops, +} + +}; + +static void max98095_handle_pdata(struct snd_soc_codec *codec) +{ + struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); + struct max98095_pdata *pdata = max98095->pdata; + u8 regval = 0; + + if (!pdata) { + dev_dbg(codec->dev, "No platform data\n"); + return; + } + + /* Configure mic for analog/digital mic mode */ + if (pdata->digmic_left_mode) + regval |= M98095_DIGMIC_L; + + if (pdata->digmic_right_mode) + regval |= M98095_DIGMIC_R; + + snd_soc_write(codec, M98095_087_CFG_MIC, regval); +} + +#ifdef CONFIG_PM +static int max98095_suspend(struct snd_soc_codec *codec, pm_message_t state) +{ + max98095_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +static int max98095_resume(struct snd_soc_codec *codec) +{ + max98095_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} +#else +#define max98095_suspend NULL +#define max98095_resume NULL +#endif + +static int max98095_reset(struct snd_soc_codec *codec) +{ + int i, ret; + + /* Gracefully reset the DSP core and the codec hardware + * in a proper sequence */ + ret = snd_soc_write(codec, M98095_00F_HOST_CFG, 0); + if (ret < 0) { + dev_err(codec->dev, "Failed to reset DSP: %d\n", ret); + return ret; + } + + ret = snd_soc_write(codec, M98095_097_PWR_SYS, 0); + if (ret < 0) { + dev_err(codec->dev, "Failed to reset codec: %d\n", ret); + return ret; + } + + /* Reset to hardware default for registers, as there is not + * a soft reset hardware control register */ + for (i = M98095_010_HOST_INT_CFG; i < M98095_REG_MAX_CACHED; i++) { + ret = snd_soc_write(codec, i, max98095_reg_def[i]); + if (ret < 0) { + dev_err(codec->dev, "Failed to reset: %d\n", ret); + return ret; + } + } + + return ret; +} + +static int max98095_probe(struct snd_soc_codec *codec) +{ + struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); + struct max98095_cdata *cdata; + int ret = 0; + + ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C); + if (ret != 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + + /* reset the codec, the DSP core, and disable all interrupts */ + max98095_reset(codec); + + /* initialize private data */ + + max98095->sysclk = (unsigned)-1; + + cdata = &max98095->dai[0]; + cdata->rate = (unsigned)-1; + cdata->fmt = (unsigned)-1; + + cdata = &max98095->dai[1]; + cdata->rate = (unsigned)-1; + cdata->fmt = (unsigned)-1; + + cdata = &max98095->dai[2]; + cdata->rate = (unsigned)-1; + cdata->fmt = (unsigned)-1; + + max98095->lin_state = 0; + max98095->mic1pre = 0; + max98095->mic2pre = 0; + + ret = snd_soc_read(codec, M98095_0FF_REV_ID); + if (ret < 0) { + dev_err(codec->dev, "Failed to read device revision: %d\n", + ret); + goto err_access; + } + dev_info(codec->dev, "revision %c\n", ret + 'A'); + + snd_soc_write(codec, M98095_097_PWR_SYS, M98095_PWRSV); + + /* initialize registers cache to hardware default */ + max98095_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + snd_soc_write(codec, M98095_048_MIX_DAC_LR, + M98095_DAI1L_TO_DACL|M98095_DAI1R_TO_DACR); + + snd_soc_write(codec, M98095_049_MIX_DAC_M, + M98095_DAI2M_TO_DACM|M98095_DAI3M_TO_DACM); + + snd_soc_write(codec, M98095_092_PWR_EN_OUT, M98095_SPK_SPREADSPECTRUM); + snd_soc_write(codec, M98095_045_CFG_DSP, M98095_DSPNORMAL); + snd_soc_write(codec, M98095_04E_CFG_HP, M98095_HPNORMAL); + + snd_soc_write(codec, M98095_02C_DAI1_IOCFG, + M98095_S1NORMAL|M98095_SDATA); + + snd_soc_write(codec, M98095_036_DAI2_IOCFG, + M98095_S2NORMAL|M98095_SDATA); + + snd_soc_write(codec, M98095_040_DAI3_IOCFG, + M98095_S3NORMAL|M98095_SDATA); + + max98095_handle_pdata(codec); + + /* take the codec out of the shut down */ + snd_soc_update_bits(codec, M98095_097_PWR_SYS, M98095_SHDNRUN, + M98095_SHDNRUN); + + max98095_add_widgets(codec); + +err_access: + return ret; +} + +static int max98095_remove(struct snd_soc_codec *codec) +{ + max98095_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_max98095 = { + .probe = max98095_probe, + .remove = max98095_remove, + .suspend = max98095_suspend, + .resume = max98095_resume, + .set_bias_level = max98095_set_bias_level, + .reg_cache_size = ARRAY_SIZE(max98095_reg_def), + .reg_word_size = sizeof(u8), + .reg_cache_default = max98095_reg_def, + .readable_register = max98095_readable, + .volatile_register = max98095_volatile, + .dapm_widgets = max98095_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98095_dapm_widgets), + .dapm_routes = max98095_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98095_audio_map), +}; + +static int max98095_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct max98095_priv *max98095; + int ret; + + max98095 = kzalloc(sizeof(struct max98095_priv), GFP_KERNEL); + if (max98095 == NULL) + return -ENOMEM; + + max98095->devtype = id->driver_data; + i2c_set_clientdata(i2c, max98095); + max98095->control_data = i2c; + max98095->pdata = i2c->dev.platform_data; + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_max98095, &max98095_dai[0], 3); + if (ret < 0) + kfree(max98095); + return ret; +} + +static int __devexit max98095_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + kfree(i2c_get_clientdata(client)); + + return 0; +} + +static const struct i2c_device_id max98095_i2c_id[] = { + { "max98095", MAX98095 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max98095_i2c_id); + +static struct i2c_driver max98095_i2c_driver = { + .driver = { + .name = "max98095", + .owner = THIS_MODULE, + }, + .probe = max98095_i2c_probe, + .remove = __devexit_p(max98095_i2c_remove), + .id_table = max98095_i2c_id, +}; + +static int __init max98095_init(void) +{ + int ret; + + ret = i2c_add_driver(&max98095_i2c_driver); + if (ret) + pr_err("Failed to register max98095 I2C driver: %d\n", ret); + + return ret; +} +module_init(max98095_init); + +static void __exit max98095_exit(void) +{ + i2c_del_driver(&max98095_i2c_driver); +} +module_exit(max98095_exit); + +MODULE_DESCRIPTION("ALSA SoC MAX98095 driver"); +MODULE_AUTHOR("Peter Hsiang"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98095.h b/sound/soc/codecs/max98095.h new file mode 100644 index 0000000..5b22bc8 --- /dev/null +++ b/sound/soc/codecs/max98095.h @@ -0,0 +1,284 @@ +/* + * max98095.h -- MAX98095 ALSA SoC Audio driver + * + * Copyright 2011 Maxim Integrated Products + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _MAX98095_H +#define _MAX98095_H + +/* + * MAX98095 Registers Definition + */ + +#define M98095_000_HOST_DATA 0x00 +#define M98095_001_HOST_INT_STS 0x01 +#define M98095_002_HOST_RSP_STS 0x02 +#define M98095_003_HOST_CMD_STS 0x03 +#define M98095_004_CODEC_STS 0x04 +#define M98095_005_DAI1_ALC_STS 0x05 +#define M98095_006_DAI2_ALC_STS 0x06 +#define M98095_007_JACK_AUTO_STS 0x07 +#define M98095_008_JACK_MANUAL_STS 0x08 +#define M98095_009_JACK_VBAT_STS 0x09 +#define M98095_00A_ACC_ADC_STS 0x0A +#define M98095_00B_MIC_NG_AGC_STS 0x0B +#define M98095_00C_SPK_L_VOLT_STS 0x0C +#define M98095_00D_SPK_R_VOLT_STS 0x0D +#define M98095_00E_TEMP_SENSOR_STS 0x0E +#define M98095_00F_HOST_CFG 0x0F +#define M98095_010_HOST_INT_CFG 0x10 +#define M98095_011_HOST_INT_EN 0x11 +#define M98095_012_CODEC_INT_EN 0x12 +#define M98095_013_JACK_INT_EN 0x13 +#define M98095_014_JACK_INT_EN 0x14 +#define M98095_015_DEC 0x15 +#define M98095_016_RESERVED 0x16 +#define M98095_017_RESERVED 0x17 +#define M98095_018_KEYCODE3 0x18 +#define M98095_019_KEYCODE2 0x19 +#define M98095_01A_KEYCODE1 0x1A +#define M98095_01B_KEYCODE0 0x1B +#define M98095_01C_OEMCODE1 0x1C +#define M98095_01D_OEMCODE0 0x1D +#define M98095_01E_XCFG1 0x1E +#define M98095_01F_XCFG2 0x1F +#define M98095_020_XCFG3 0x20 +#define M98095_021_XCFG4 0x21 +#define M98095_022_XCFG5 0x22 +#define M98095_023_XCFG6 0x23 +#define M98095_024_XGPIO 0x24 +#define M98095_025_XCLKCFG 0x25 +#define M98095_026_SYS_CLK 0x26 +#define M98095_027_DAI1_CLKMODE 0x27 +#define M98095_028_DAI1_CLKCFG_HI 0x28 +#define M98095_029_DAI1_CLKCFG_LO 0x29 +#define M98095_02A_DAI1_FORMAT 0x2A +#define M98095_02B_DAI1_CLOCK 0x2B +#define M98095_02C_DAI1_IOCFG 0x2C +#define M98095_02D_DAI1_TDM 0x2D +#define M98095_02E_DAI1_FILTERS 0x2E +#define M98095_02F_DAI1_LVL1 0x2F +#define M98095_030_DAI1_LVL2 0x30 +#define M98095_031_DAI2_CLKMODE 0x31 +#define M98095_032_DAI2_CLKCFG_HI 0x32 +#define M98095_033_DAI2_CLKCFG_LO 0x33 +#define M98095_034_DAI2_FORMAT 0x34 +#define M98095_035_DAI2_CLOCK 0x35 +#define M98095_036_DAI2_IOCFG 0x36 +#define M98095_037_DAI2_TDM 0x37 +#define M98095_038_DAI2_FILTERS 0x38 +#define M98095_039_DAI2_LVL1 0x39 +#define M98095_03A_DAI2_LVL2 0x3A +#define M98095_03B_DAI3_CLKMODE 0x3B +#define M98095_03C_DAI3_CLKCFG_HI 0x3C +#define M98095_03D_DAI3_CLKCFG_LO 0x3D +#define M98095_03E_DAI3_FORMAT 0x3E +#define M98095_03F_DAI3_CLOCK 0x3F +#define M98095_040_DAI3_IOCFG 0x40 +#define M98095_041_DAI3_TDM 0x41 +#define M98095_042_DAI3_FILTERS 0x42 +#define M98095_043_DAI3_LVL1 0x43 +#define M98095_044_DAI3_LVL2 0x44 +#define M98095_045_CFG_DSP 0x45 +#define M98095_046_DAC_CTRL1 0x46 +#define M98095_047_DAC_CTRL2 0x47 +#define M98095_048_MIX_DAC_LR 0x48 +#define M98095_049_MIX_DAC_M 0x49 +#define M98095_04A_MIX_ADC_LEFT 0x4A +#define M98095_04B_MIX_ADC_RIGHT 0x4B +#define M98095_04C_MIX_HP_LEFT 0x4C +#define M98095_04D_MIX_HP_RIGHT 0x4D +#define M98095_04E_CFG_HP 0x4E +#define M98095_04F_MIX_RCV 0x4F +#define M98095_050_MIX_SPK_LEFT 0x50 +#define M98095_051_MIX_SPK_RIGHT 0x51 +#define M98095_052_MIX_SPK_CFG 0x52 +#define M98095_053_MIX_LINEOUT1 0x53 +#define M98095_054_MIX_LINEOUT2 0x54 +#define M98095_055_MIX_LINEOUT_CFG 0x55 +#define M98095_056_LVL_SIDETONE_DAI12 0x56 +#define M98095_057_LVL_SIDETONE_DAI3 0x57 +#define M98095_058_LVL_DAI1_PLAY 0x58 +#define M98095_059_LVL_DAI1_EQ 0x59 +#define M98095_05A_LVL_DAI2_PLAY 0x5A +#define M98095_05B_LVL_DAI2_EQ 0x5B +#define M98095_05C_LVL_DAI3_PLAY 0x5C +#define M98095_05D_LVL_ADC_L 0x5D +#define M98095_05E_LVL_ADC_R 0x5E +#define M98095_05F_LVL_MIC1 0x5F +#define M98095_060_LVL_MIC2 0x60 +#define M98095_061_LVL_LINEIN 0x61 +#define M98095_062_LVL_LINEOUT1 0x62 +#define M98095_063_LVL_LINEOUT2 0x63 +#define M98095_064_LVL_HP_L 0x64 +#define M98095_065_LVL_HP_R 0x65 +#define M98095_066_LVL_RCV 0x66 +#define M98095_067_LVL_SPK_L 0x67 +#define M98095_068_LVL_SPK_R 0x68 +#define M98095_069_MICAGC_CFG 0x69 +#define M98095_06A_MICAGC_THRESH 0x6A +#define M98095_06B_SPK_NOISEGATE 0x6B +#define M98095_06C_DAI1_ALC1_TIME 0x6C +#define M98095_06D_DAI1_ALC1_COMP 0x6D +#define M98095_06E_DAI1_ALC1_EXPN 0x6E +#define M98095_06F_DAI1_ALC1_GAIN 0x6F +#define M98095_070_DAI1_ALC2_TIME 0x70 +#define M98095_071_DAI1_ALC2_COMP 0x71 +#define M98095_072_DAI1_ALC2_EXPN 0x72 +#define M98095_073_DAI1_ALC2_GAIN 0x73 +#define M98095_074_DAI1_ALC3_TIME 0x74 +#define M98095_075_DAI1_ALC3_COMP 0x75 +#define M98095_076_DAI1_ALC3_EXPN 0x76 +#define M98095_077_DAI1_ALC3_GAIN 0x77 +#define M98095_078_DAI2_ALC1_TIME 0x78 +#define M98095_079_DAI2_ALC1_COMP 0x79 +#define M98095_07A_DAI2_ALC1_EXPN 0x7A +#define M98095_07B_DAI2_ALC1_GAIN 0x7B +#define M98095_07C_DAI2_ALC2_TIME 0x7C +#define M98095_07D_DAI2_ALC2_COMP 0x7D +#define M98095_07E_DAI2_ALC2_EXPN 0x7E +#define M98095_07F_DAI2_ALC2_GAIN 0x7F +#define M98095_080_DAI2_ALC3_TIME 0x80 +#define M98095_081_DAI2_ALC3_COMP 0x81 +#define M98095_082_DAI2_ALC3_EXPN 0x82 +#define M98095_083_DAI2_ALC3_GAIN 0x83 +#define M98095_084_HP_NOISE_GATE 0x84 +#define M98095_085_AUX_ADC 0x85 +#define M98095_086_CFG_LINE 0x86 +#define M98095_087_CFG_MIC 0x87 +#define M98095_088_CFG_LEVEL 0x88 +#define M98095_089_JACK_DET_AUTO 0x89 +#define M98095_08A_JACK_DET_MANUAL 0x8A +#define M98095_08B_JACK_KEYSCAN_DBC 0x8B +#define M98095_08C_JACK_KEYSCAN_DLY 0x8C +#define M98095_08D_JACK_KEY_THRESH 0x8D +#define M98095_08E_JACK_DC_SLEW 0x8E +#define M98095_08F_JACK_TEST_CFG 0x8F +#define M98095_090_PWR_EN_IN 0x90 +#define M98095_091_PWR_EN_OUT 0x91 +#define M98095_092_PWR_EN_OUT 0x92 +#define M98095_093_BIAS_CTRL 0x93 +#define M98095_094_PWR_DAC_21 0x94 +#define M98095_095_PWR_DAC_03 0x95 +#define M98095_096_PWR_DAC_CK 0x96 +#define M98095_097_PWR_SYS 0x97 + +#define M98095_0FF_REV_ID 0xFF + +#define M98095_REG_CNT (0xFF+1) +#define M98095_REG_MAX_CACHED 0X97 + +/* MAX98095 Registers Bit Fields */ + +/* M98095_00F_HOST_CFG */ + #define M98095_SEG (1<<0) + #define M98095_XTEN (1<<1) + #define M98095_MDLLEN (1<<2) + +/* M98095_027_DAI1_CLKMODE, M98095_031_DAI2_CLKMODE, M98095_03B_DAI3_CLKMODE */ + #define M98095_CLKMODE_MASK 0xFF + +/* M98095_02A_DAI1_FORMAT, M98095_034_DAI2_FORMAT, M98095_03E_DAI3_FORMAT */ + #define M98095_DAI_MAS (1<<7) + #define M98095_DAI_WCI (1<<6) + #define M98095_DAI_BCI (1<<5) + #define M98095_DAI_DLY (1<<4) + #define M98095_DAI_TDM (1<<2) + #define M98095_DAI_FSW (1<<1) + #define M98095_DAI_WS (1<<0) + +/* M98095_02B_DAI1_CLOCK, M98095_035_DAI2_CLOCK, M98095_03F_DAI3_CLOCK */ + #define M98095_DAI_BSEL64 (1<<0) + #define M98095_DAI_DOSR_DIV2 (0<<5) + #define M98095_DAI_DOSR_DIV4 (1<<5) + +/* M98095_02C_DAI1_IOCFG, M98095_036_DAI2_IOCFG, M98095_040_DAI3_IOCFG */ + #define M98095_S1NORMAL (1<<6) + #define M98095_S2NORMAL (2<<6) + #define M98095_S3NORMAL (3<<6) + #define M98095_SDATA (3<<0) + +/* M98095_02E_DAI1_FILTERS, M98095_038_DAI2_FILTERS, M98095_042_DAI3_FILTERS */ + #define M98095_DAI_DHF (1<<3) + +/* M98095_045_DSP_CFG */ + #define M98095_DSPNORMAL (5<<4) + +/* M98095_048_MIX_DAC_LR */ + #define M98095_DAI1L_TO_DACR (1<<7) + #define M98095_DAI1R_TO_DACR (1<<6) + #define M98095_DAI2M_TO_DACR (1<<5) + #define M98095_DAI1L_TO_DACL (1<<3) + #define M98095_DAI1R_TO_DACL (1<<2) + #define M98095_DAI2M_TO_DACL (1<<1) + #define M98095_DAI3M_TO_DACL (1<<0) + +/* M98095_049_MIX_DAC_M */ + #define M98095_DAI1L_TO_DACM (1<<3) + #define M98095_DAI1R_TO_DACM (1<<2) + #define M98095_DAI2M_TO_DACM (1<<1) + #define M98095_DAI3M_TO_DACM (1<<0) + +/* M98095_04E_MIX_HP_CFG */ + #define M98095_HPNORMAL (3<<4) + +/* M98095_05F_LVL_MIC1, M98095_060_LVL_MIC2 */ + #define M98095_MICPRE_MASK (3<<5) + #define M98095_MICPRE_SHIFT 5 + +/* M98095_064_LVL_HP_L, M98095_065_LVL_HP_R */ + #define M98095_HP_MUTE (1<<7) + +/* M98095_066_LVL_RCV */ + #define M98095_REC_MUTE (1<<7) + +/* M98095_067_LVL_SPK_L, M98095_068_LVL_SPK_R */ + #define M98095_SP_MUTE (1<<7) + +/* M98095_087_CFG_MIC */ + #define M98095_MICSEL_MASK (3<<0) + #define M98095_DIGMIC_L (1<<2) + #define M98095_DIGMIC_R (1<<3) + #define M98095_DIGMIC2L (1<<4) + #define M98095_DIGMIC2R (1<<5) + +/* M98095_088_CFG_LEVEL */ + #define M98095_VSEN (1<<6) + #define M98095_ZDEN (1<<5) + #define M98095_EQ2EN (1<<1) + #define M98095_EQ1EN (1<<0) + +/* M98095_090_PWR_EN_IN */ + #define M98095_INEN (1<<7) + #define M98095_MB2EN (1<<3) + #define M98095_MB1EN (1<<2) + #define M98095_MBEN (3<<2) + #define M98095_ADREN (1<<1) + #define M98095_ADLEN (1<<0) + +/* M98095_091_PWR_EN_OUT */ + #define M98095_HPLEN (1<<7) + #define M98095_HPREN (1<<6) + #define M98095_SPLEN (1<<5) + #define M98095_SPREN (1<<4) + #define M98095_RECEN (1<<3) + #define M98095_DALEN (1<<1) + #define M98095_DAREN (1<<0) + +/* M98095_092_PWR_EN_OUT */ + #define M98095_SPK_FIXEDSPECTRUM (0<<4) + #define M98095_SPK_SPREADSPECTRUM (1<<4) + +/* M98095_097_PWR_SYS */ + #define M98095_SHDNRUN (1<<7) + #define M98095_PERFMODE (1<<3) + #define M98095_HPPLYBACK (1<<2) + #define M98095_PWRSV8K (1<<1) + #define M98095_PWRSV (1<<0) + +#endif diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c index f74d497..f70977d 100644 --- a/sound/soc/codecs/sn95031.c +++ b/sound/soc/codecs/sn95031.c @@ -825,8 +825,6 @@ EXPORT_SYMBOL_GPL(sn95031_jack_detection); /* codec registration */ static int sn95031_codec_probe(struct snd_soc_codec *codec) { - int ret; - pr_debug("codec_probe called\n"); codec->dapm.bias_level = SND_SOC_BIAS_OFF; @@ -877,16 +875,7 @@ static int sn95031_codec_probe(struct snd_soc_codec *codec) snd_soc_add_controls(codec, sn95031_snd_controls, ARRAY_SIZE(sn95031_snd_controls)); - ret = snd_soc_dapm_new_controls(&codec->dapm, sn95031_dapm_widgets, - ARRAY_SIZE(sn95031_dapm_widgets)); - if (ret) - pr_err("soc_dapm_new_control failed %d", ret); - ret = snd_soc_dapm_add_routes(&codec->dapm, sn95031_audio_map, - ARRAY_SIZE(sn95031_audio_map)); - if (ret) - pr_err("soc_dapm_add_routes failed %d", ret); - - return ret; + return 0; } static int sn95031_codec_remove(struct snd_soc_codec *codec) @@ -903,6 +892,10 @@ struct snd_soc_codec_driver sn95031_codec = { .read = sn95031_read, .write = sn95031_write, .set_bias_level = sn95031_set_vaud_bias, + .dapm_widgets = sn95031_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sn95031_dapm_widgets), + .dapm_routes = sn95031_audio_map, + .num_dapm_routes = ARRAY_SIZE(sn95031_audio_map), }; static int __devinit sn95031_device_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index 2727bef..7e21949 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -32,6 +32,7 @@ #include <linux/delay.h> #include <linux/pm.h> #include <linux/i2c.h> +#include <linux/spi/spi.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <sound/core.h> @@ -48,7 +49,6 @@ struct ssm2602_priv { unsigned int sysclk; enum snd_soc_control_type control_type; - void *control_data; struct snd_pcm_substream *master_substream; struct snd_pcm_substream *slave_substream; }; @@ -65,55 +65,7 @@ static const u16 ssm2602_reg[SSM2602_CACHEREGNUM] = { 0x0000, 0x0000 }; -/* - * read ssm2602 register cache - */ -static inline unsigned int ssm2602_read_reg_cache(struct snd_soc_codec *codec, - unsigned int reg) -{ - u16 *cache = codec->reg_cache; - if (reg == SSM2602_RESET) - return 0; - if (reg >= SSM2602_CACHEREGNUM) - return -1; - return cache[reg]; -} - -/* - * write ssm2602 register cache - */ -static inline void ssm2602_write_reg_cache(struct snd_soc_codec *codec, - u16 reg, unsigned int value) -{ - u16 *cache = codec->reg_cache; - if (reg >= SSM2602_CACHEREGNUM) - return; - cache[reg] = value; -} - -/* - * write to the ssm2602 register space - */ -static int ssm2602_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - u8 data[2]; - - /* data is - * D15..D9 ssm2602 register offset - * D8...D0 register data - */ - data[0] = (reg << 1) | ((value >> 8) & 0x0001); - data[1] = value & 0x00ff; - - ssm2602_write_reg_cache(codec, reg, value); - if (codec->hw_write(codec->control_data, data, 2) == 2) - return 0; - else - return -EIO; -} - -#define ssm2602_reset(c) ssm2602_write(c, SSM2602_RESET, 0) +#define ssm2602_reset(c) snd_soc_write(c, SSM2602_RESET, 0) /*Appending several "None"s just for OSS mixer use*/ static const char *ssm2602_input_select[] = { @@ -278,12 +230,11 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_codec *codec = rtd->codec; struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec); - struct i2c_client *i2c = codec->control_data; - u16 iface = ssm2602_read_reg_cache(codec, SSM2602_IFACE) & 0xfff3; + u16 iface = snd_soc_read(codec, SSM2602_IFACE) & 0xfff3; int i = get_coeff(ssm2602->sysclk, params_rate(params)); if (substream == ssm2602->slave_substream) { - dev_dbg(&i2c->dev, "Ignoring hw_params for slave substream\n"); + dev_dbg(codec->dev, "Ignoring hw_params for slave substream\n"); return 0; } @@ -294,8 +245,8 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream, srate = (coeff_div[i].sr << 2) | (coeff_div[i].bosr << 1) | coeff_div[i].usb; - ssm2602_write(codec, SSM2602_ACTIVE, 0); - ssm2602_write(codec, SSM2602_SRATE, srate); + snd_soc_write(codec, SSM2602_ACTIVE, 0); + snd_soc_write(codec, SSM2602_SRATE, srate); /* bit size */ switch (params_format(params)) { @@ -311,8 +262,8 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream, iface |= 0x000c; break; } - ssm2602_write(codec, SSM2602_IFACE, iface); - ssm2602_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC); + snd_soc_write(codec, SSM2602_IFACE, iface); + snd_soc_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC); return 0; } @@ -360,7 +311,7 @@ static int ssm2602_pcm_prepare(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_codec *codec = rtd->codec; /* set active */ - ssm2602_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC); + snd_soc_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC); return 0; } @@ -374,7 +325,7 @@ static void ssm2602_shutdown(struct snd_pcm_substream *substream, /* deactivate */ if (!codec->active) - ssm2602_write(codec, SSM2602_ACTIVE, 0); + snd_soc_write(codec, SSM2602_ACTIVE, 0); if (ssm2602->master_substream == substream) ssm2602->master_substream = ssm2602->slave_substream; @@ -385,12 +336,12 @@ static void ssm2602_shutdown(struct snd_pcm_substream *substream, static int ssm2602_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; - u16 mute_reg = ssm2602_read_reg_cache(codec, SSM2602_APDIGI) & ~APDIGI_ENABLE_DAC_MUTE; + u16 mute_reg = snd_soc_read(codec, SSM2602_APDIGI) & ~APDIGI_ENABLE_DAC_MUTE; if (mute) - ssm2602_write(codec, SSM2602_APDIGI, + snd_soc_write(codec, SSM2602_APDIGI, mute_reg | APDIGI_ENABLE_DAC_MUTE); else - ssm2602_write(codec, SSM2602_APDIGI, mute_reg); + snd_soc_write(codec, SSM2602_APDIGI, mute_reg); return 0; } @@ -466,30 +417,30 @@ static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai, } /* set iface */ - ssm2602_write(codec, SSM2602_IFACE, iface); + snd_soc_write(codec, SSM2602_IFACE, iface); return 0; } static int ssm2602_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - u16 reg = ssm2602_read_reg_cache(codec, SSM2602_PWR) & 0xff7f; + u16 reg = snd_soc_read(codec, SSM2602_PWR) & 0xff7f; switch (level) { case SND_SOC_BIAS_ON: /* vref/mid, osc on, dac unmute */ - ssm2602_write(codec, SSM2602_PWR, reg); + snd_soc_write(codec, SSM2602_PWR, reg); break; case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: /* everything off except vref/vmid, */ - ssm2602_write(codec, SSM2602_PWR, reg | PWR_CLK_OUT_PDN); + snd_soc_write(codec, SSM2602_PWR, reg | PWR_CLK_OUT_PDN); break; case SND_SOC_BIAS_OFF: /* everything off, dac mute, inactive */ - ssm2602_write(codec, SSM2602_ACTIVE, 0); - ssm2602_write(codec, SSM2602_PWR, 0xffff); + snd_soc_write(codec, SSM2602_ACTIVE, 0); + snd_soc_write(codec, SSM2602_PWR, 0xffff); break; } @@ -539,17 +490,10 @@ static int ssm2602_suspend(struct snd_soc_codec *codec, pm_message_t state) static int ssm2602_resume(struct snd_soc_codec *codec) { - int i; - u8 data[2]; - u16 *cache = codec->reg_cache; - - /* Sync reg_cache with the hardware */ - for (i = 0; i < ARRAY_SIZE(ssm2602_reg); i++) { - data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001); - data[1] = cache[i] & 0x00ff; - codec->hw_write(codec->control_data, data, 2); - } + snd_soc_cache_sync(codec); + ssm2602_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + return 0; } @@ -560,31 +504,39 @@ static int ssm2602_probe(struct snd_soc_codec *codec) pr_info("ssm2602 Audio Codec %s", SSM2602_VERSION); - codec->control_data = ssm2602->control_data; + ret = snd_soc_codec_set_cache_io(codec, 7, 9, ssm2602->control_type); + if (ret < 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } - ssm2602_reset(codec); + ret = ssm2602_reset(codec); + if (ret < 0) { + dev_err(codec->dev, "Failed to issue reset: %d\n", ret); + return ret; + } /*power on device*/ - ssm2602_write(codec, SSM2602_ACTIVE, 0); + snd_soc_write(codec, SSM2602_ACTIVE, 0); /* set the update bits */ - reg = ssm2602_read_reg_cache(codec, SSM2602_LINVOL); - ssm2602_write(codec, SSM2602_LINVOL, reg | LINVOL_LRIN_BOTH); - reg = ssm2602_read_reg_cache(codec, SSM2602_RINVOL); - ssm2602_write(codec, SSM2602_RINVOL, reg | RINVOL_RLIN_BOTH); - reg = ssm2602_read_reg_cache(codec, SSM2602_LOUT1V); - ssm2602_write(codec, SSM2602_LOUT1V, reg | LOUT1V_LRHP_BOTH); - reg = ssm2602_read_reg_cache(codec, SSM2602_ROUT1V); - ssm2602_write(codec, SSM2602_ROUT1V, reg | ROUT1V_RLHP_BOTH); + reg = snd_soc_read(codec, SSM2602_LINVOL); + snd_soc_write(codec, SSM2602_LINVOL, reg | LINVOL_LRIN_BOTH); + reg = snd_soc_read(codec, SSM2602_RINVOL); + snd_soc_write(codec, SSM2602_RINVOL, reg | RINVOL_RLIN_BOTH); + reg = snd_soc_read(codec, SSM2602_LOUT1V); + snd_soc_write(codec, SSM2602_LOUT1V, reg | LOUT1V_LRHP_BOTH); + reg = snd_soc_read(codec, SSM2602_ROUT1V); + snd_soc_write(codec, SSM2602_ROUT1V, reg | ROUT1V_RLHP_BOTH); /*select Line in as default input*/ - ssm2602_write(codec, SSM2602_APANA, APANA_SELECT_DAC | + snd_soc_write(codec, SSM2602_APANA, APANA_SELECT_DAC | APANA_ENABLE_MIC_BOOST); - ssm2602_write(codec, SSM2602_PWR, 0); + snd_soc_write(codec, SSM2602_PWR, 0); snd_soc_add_controls(codec, ssm2602_snd_controls, ARRAY_SIZE(ssm2602_snd_controls)); ssm2602_add_widgets(codec); - return ret; + return 0; } /* remove everything here */ @@ -599,14 +551,49 @@ static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = { .remove = ssm2602_remove, .suspend = ssm2602_suspend, .resume = ssm2602_resume, - .read = ssm2602_read_reg_cache, - .write = ssm2602_write, .set_bias_level = ssm2602_set_bias_level, .reg_cache_size = sizeof(ssm2602_reg), .reg_word_size = sizeof(u16), .reg_cache_default = ssm2602_reg, }; +#if defined(CONFIG_SPI_MASTER) +static int __devinit ssm2602_spi_probe(struct spi_device *spi) +{ + struct ssm2602_priv *ssm2602; + int ret; + + ssm2602 = kzalloc(sizeof(struct ssm2602_priv), GFP_KERNEL); + if (ssm2602 == NULL) + return -ENOMEM; + + spi_set_drvdata(spi, ssm2602); + ssm2602->control_type = SND_SOC_SPI; + + ret = snd_soc_register_codec(&spi->dev, + &soc_codec_dev_ssm2602, &ssm2602_dai, 1); + if (ret < 0) + kfree(ssm2602); + return ret; +} + +static int __devexit ssm2602_spi_remove(struct spi_device *spi) +{ + snd_soc_unregister_codec(&spi->dev); + kfree(spi_get_drvdata(spi)); + return 0; +} + +static struct spi_driver ssm2602_spi_driver = { + .driver = { + .name = "ssm2602", + .owner = THIS_MODULE, + }, + .probe = ssm2602_spi_probe, + .remove = __devexit_p(ssm2602_spi_remove), +}; +#endif + #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) /* * ssm2602 2 wire address is determined by GPIO5 @@ -625,7 +612,6 @@ static int ssm2602_i2c_probe(struct i2c_client *i2c, return -ENOMEM; i2c_set_clientdata(i2c, ssm2602); - ssm2602->control_data = i2c; ssm2602->control_type = SND_SOC_I2C; ret = snd_soc_register_codec(&i2c->dev, @@ -651,7 +637,7 @@ MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id); /* corgi i2c codec control layer */ static struct i2c_driver ssm2602_i2c_driver = { .driver = { - .name = "ssm2602-codec", + .name = "ssm2602", .owner = THIS_MODULE, }, .probe = ssm2602_i2c_probe, @@ -664,19 +650,29 @@ static struct i2c_driver ssm2602_i2c_driver = { static int __init ssm2602_modinit(void) { int ret = 0; + +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&ssm2602_spi_driver); + if (ret) + return ret; +#endif + #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) ret = i2c_add_driver(&ssm2602_i2c_driver); - if (ret != 0) { - printk(KERN_ERR "Failed to register SSM2602 I2C driver: %d\n", - ret); - } + if (ret) + return ret; #endif + return ret; } module_init(ssm2602_modinit); static void __exit ssm2602_exit(void) { +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&ssm2602_spi_driver); +#endif + #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) i2c_del_driver(&ssm2602_i2c_driver); #endif diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c index 54a30ef..33bb52f 100644 --- a/sound/soc/codecs/tlv320aic23.c +++ b/sound/soc/codecs/tlv320aic23.c @@ -212,7 +212,7 @@ static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = { SND_SOC_DAPM_INPUT("MICIN"), }; -static const struct snd_soc_dapm_route intercon[] = { +static const struct snd_soc_dapm_route tlv320aic23_intercon[] = { /* Output Mixer */ {"Output Mixer", "Line Bypass Switch", "Line Input"}, {"Output Mixer", "Playback Switch", "DAC"}, @@ -388,18 +388,6 @@ static int set_sample_rate_control(struct snd_soc_codec *codec, int mclk, return 0; } -static int tlv320aic23_add_widgets(struct snd_soc_codec *codec) -{ - struct snd_soc_dapm_context *dapm = &codec->dapm; - - snd_soc_dapm_new_controls(dapm, tlv320aic23_dapm_widgets, - ARRAY_SIZE(tlv320aic23_dapm_widgets)); - /* set up audio path interconnects */ - snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); - - return 0; -} - static int tlv320aic23_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -676,7 +664,6 @@ static int tlv320aic23_probe(struct snd_soc_codec *codec) snd_soc_add_controls(codec, tlv320aic23_snd_controls, ARRAY_SIZE(tlv320aic23_snd_controls)); - tlv320aic23_add_widgets(codec); return 0; } @@ -698,6 +685,10 @@ static struct snd_soc_codec_driver soc_codec_dev_tlv320aic23 = { .read = tlv320aic23_read_reg_cache, .write = tlv320aic23_write, .set_bias_level = tlv320aic23_set_bias_level, + .dapm_widgets = tlv320aic23_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets), + .dapm_routes = tlv320aic23_intercon, + .num_dapm_routes = ARRAY_SIZE(tlv320aic23_intercon), }; #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 00b6d87..eb1a0b4 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -324,6 +324,10 @@ static void dac33_init_chip(struct snd_soc_codec *codec) dac33_write(codec, DAC33_OUT_AMP_CTRL, dac33_read_reg_cache(codec, DAC33_OUT_AMP_CTRL)); + dac33_write(codec, DAC33_LDAC_PWR_CTRL, + dac33_read_reg_cache(codec, DAC33_LDAC_PWR_CTRL)); + dac33_write(codec, DAC33_RDAC_PWR_CTRL, + dac33_read_reg_cache(codec, DAC33_RDAC_PWR_CTRL)); } static inline int dac33_read_id(struct snd_soc_codec *codec) @@ -670,6 +674,7 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) { struct snd_soc_codec *codec = dac33->codec; unsigned int delay; + unsigned long flags; switch (dac33->fifo_mode) { case DAC33_FIFO_MODE1: @@ -677,10 +682,10 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) DAC33_THRREG(dac33->nsample)); /* Take the timestamps */ - spin_lock_irq(&dac33->lock); + spin_lock_irqsave(&dac33->lock, flags); dac33->t_stamp2 = ktime_to_us(ktime_get()); dac33->t_stamp1 = dac33->t_stamp2; - spin_unlock_irq(&dac33->lock); + spin_unlock_irqrestore(&dac33->lock, flags); dac33_write16(codec, DAC33_PREFILL_MSB, DAC33_THRREG(dac33->alarm_threshold)); @@ -692,11 +697,11 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) break; case DAC33_FIFO_MODE7: /* Take the timestamp */ - spin_lock_irq(&dac33->lock); + spin_lock_irqsave(&dac33->lock, flags); dac33->t_stamp1 = ktime_to_us(ktime_get()); /* Move back the timestamp with drain time */ dac33->t_stamp1 -= dac33->mode7_us_to_lthr; - spin_unlock_irq(&dac33->lock); + spin_unlock_irqrestore(&dac33->lock, flags); dac33_write16(codec, DAC33_PREFILL_MSB, DAC33_THRREG(DAC33_MODE7_MARGIN)); @@ -714,13 +719,14 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) static inline void dac33_playback_handler(struct tlv320dac33_priv *dac33) { struct snd_soc_codec *codec = dac33->codec; + unsigned long flags; switch (dac33->fifo_mode) { case DAC33_FIFO_MODE1: /* Take the timestamp */ - spin_lock_irq(&dac33->lock); + spin_lock_irqsave(&dac33->lock, flags); dac33->t_stamp2 = ktime_to_us(ktime_get()); - spin_unlock_irq(&dac33->lock); + spin_unlock_irqrestore(&dac33->lock, flags); dac33_write16(codec, DAC33_NSAMPLE_MSB, DAC33_THRREG(dac33->nsample)); @@ -773,10 +779,11 @@ static irqreturn_t dac33_interrupt_handler(int irq, void *dev) { struct snd_soc_codec *codec = dev; struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); + unsigned long flags; - spin_lock(&dac33->lock); + spin_lock_irqsave(&dac33->lock, flags); dac33->t_stamp1 = ktime_to_us(ktime_get()); - spin_unlock(&dac33->lock); + spin_unlock_irqrestore(&dac33->lock, flags); /* Do not schedule the workqueue in Mode7 */ if (dac33->fifo_mode != DAC33_FIFO_MODE7) @@ -1173,15 +1180,16 @@ static snd_pcm_sframes_t dac33_dai_delay( unsigned int time_delta, uthr; int samples_out, samples_in, samples; snd_pcm_sframes_t delay = 0; + unsigned long flags; switch (dac33->fifo_mode) { case DAC33_FIFO_BYPASS: break; case DAC33_FIFO_MODE1: - spin_lock(&dac33->lock); + spin_lock_irqsave(&dac33->lock, flags); t0 = dac33->t_stamp1; t1 = dac33->t_stamp2; - spin_unlock(&dac33->lock); + spin_unlock_irqrestore(&dac33->lock, flags); t_now = ktime_to_us(ktime_get()); /* We have not started to fill the FIFO yet, delay is 0 */ @@ -1246,10 +1254,10 @@ static snd_pcm_sframes_t dac33_dai_delay( } break; case DAC33_FIFO_MODE7: - spin_lock(&dac33->lock); + spin_lock_irqsave(&dac33->lock, flags); t0 = dac33->t_stamp1; uthr = dac33->uthr; - spin_unlock(&dac33->lock); + spin_unlock_irqrestore(&dac33->lock, flags); t_now = ktime_to_us(ktime_get()); /* We have not started to fill the FIFO yet, delay is 0 */ diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 482fcdb..255901c 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -1629,8 +1629,10 @@ static int twl6040_probe(struct snd_soc_codec *codec) priv->naudint = naudint; priv->workqueue = create_singlethread_workqueue("twl6040-codec"); - if (!priv->workqueue) + if (!priv->workqueue) { + ret = -ENOMEM; goto work_err; + } INIT_DELAYED_WORK(&priv->delayed_work, twl6040_accessory_work); diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index 97c3038..a537e4a 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -77,7 +77,7 @@ SND_SOC_DAPM_OUTPUT("ROUT"), SND_SOC_DAPM_OUTPUT("RHPOUT"), }; -static const struct snd_soc_dapm_route intercon[] = { +static const struct snd_soc_dapm_route wm8711_intercon[] = { /* output mixer */ {"Output Mixer", "Line Bypass Switch", "Line Input"}, {"Output Mixer", "HiFi Playback Switch", "DAC"}, @@ -89,17 +89,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"LOUT", NULL, "Output Mixer"}, }; -static int wm8711_add_widgets(struct snd_soc_codec *codec) -{ - struct snd_soc_dapm_context *dapm = &codec->dapm; - - snd_soc_dapm_new_controls(dapm, wm8711_dapm_widgets, - ARRAY_SIZE(wm8711_dapm_widgets)); - snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); - - return 0; -} - struct _coeff_div { u32 mclk; u32 rate; @@ -398,7 +387,6 @@ static int wm8711_probe(struct snd_soc_codec *codec) snd_soc_add_controls(codec, wm8711_snd_controls, ARRAY_SIZE(wm8711_snd_controls)); - wm8711_add_widgets(codec); return ret; @@ -420,6 +408,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8711 = { .reg_cache_size = ARRAY_SIZE(wm8711_reg), .reg_word_size = sizeof(u16), .reg_cache_default = wm8711_reg, + .dapm_widgets = wm8711_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8711_dapm_widgets), + .dapm_routes = wm8711_intercon, + .num_dapm_routes = ARRAY_SIZE(wm8711_intercon), }; #if defined(CONFIG_SPI_MASTER) diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c index 736b035..86d4718 100644 --- a/sound/soc/codecs/wm8728.c +++ b/sound/soc/codecs/wm8728.c @@ -65,22 +65,11 @@ SND_SOC_DAPM_OUTPUT("VOUTL"), SND_SOC_DAPM_OUTPUT("VOUTR"), }; -static const struct snd_soc_dapm_route intercon[] = { +static const struct snd_soc_dapm_route wm8728_intercon[] = { {"VOUTL", NULL, "DAC"}, {"VOUTR", NULL, "DAC"}, }; -static int wm8728_add_widgets(struct snd_soc_codec *codec) -{ - struct snd_soc_dapm_context *dapm = &codec->dapm; - - snd_soc_dapm_new_controls(dapm, wm8728_dapm_widgets, - ARRAY_SIZE(wm8728_dapm_widgets)); - snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); - - return 0; -} - static int wm8728_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; @@ -255,7 +244,6 @@ static int wm8728_probe(struct snd_soc_codec *codec) snd_soc_add_controls(codec, wm8728_snd_controls, ARRAY_SIZE(wm8728_snd_controls)); - wm8728_add_widgets(codec); return ret; } @@ -275,6 +263,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8728 = { .reg_cache_size = ARRAY_SIZE(wm8728_reg_defaults), .reg_word_size = sizeof(u16), .reg_cache_default = wm8728_reg_defaults, + .dapm_widgets = wm8728_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8728_dapm_widgets), + .dapm_routes = wm8728_intercon, + .num_dapm_routes = ARRAY_SIZE(wm8728_intercon), }; #if defined(CONFIG_SPI_MASTER) diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 0a67c31..6dec7ce 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -201,7 +201,7 @@ static int wm8731_check_osc(struct snd_soc_dapm_widget *source, return wm8731->sysclk_type == WM8731_SYSCLK_MCLK; } -static const struct snd_soc_dapm_route intercon[] = { +static const struct snd_soc_dapm_route wm8731_intercon[] = { {"DAC", NULL, "OSC", wm8731_check_osc}, {"ADC", NULL, "OSC", wm8731_check_osc}, @@ -227,17 +227,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"Mic Bias", NULL, "MICIN"}, }; -static int wm8731_add_widgets(struct snd_soc_codec *codec) -{ - struct snd_soc_dapm_context *dapm = &codec->dapm; - - snd_soc_dapm_new_controls(dapm, wm8731_dapm_widgets, - ARRAY_SIZE(wm8731_dapm_widgets)); - snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); - - return 0; -} - struct _coeff_div { u32 mclk; u32 rate; @@ -599,7 +588,6 @@ static int wm8731_probe(struct snd_soc_codec *codec) snd_soc_add_controls(codec, wm8731_snd_controls, ARRAY_SIZE(wm8731_snd_controls)); - wm8731_add_widgets(codec); /* Regulators will have been enabled by bias management */ regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); @@ -636,6 +624,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8731 = { .reg_cache_size = ARRAY_SIZE(wm8731_reg), .reg_word_size = sizeof(u16), .reg_cache_default = wm8731_reg, + .dapm_widgets = wm8731_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets), + .dapm_routes = wm8731_intercon, + .num_dapm_routes = ARRAY_SIZE(wm8731_intercon), }; #if defined(CONFIG_SPI_MASTER) @@ -667,7 +659,7 @@ static int __devexit wm8731_spi_remove(struct spi_device *spi) static struct spi_driver wm8731_spi_driver = { .driver = { - .name = "wm8731-codec", + .name = "wm8731", .owner = THIS_MODULE, }, .probe = wm8731_spi_probe, @@ -711,7 +703,7 @@ MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id); static struct i2c_driver wm8731_i2c_driver = { .driver = { - .name = "wm8731-codec", + .name = "wm8731", .owner = THIS_MODULE, }, .probe = wm8731_i2c_probe, diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index f52b623..d53f206 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -930,7 +930,7 @@ SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8903_CLOCK_RATES_2, 1, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("CLK_SYS", WM8903_CLOCK_RATES_2, 2, 0, NULL, 0), }; -static const struct snd_soc_dapm_route intercon[] = { +static const struct snd_soc_dapm_route wm8903_intercon[] = { { "CLK_DSP", NULL, "CLK_SYS" }, { "Mic Bias", NULL, "CLK_SYS" }, @@ -1087,17 +1087,6 @@ static const struct snd_soc_dapm_route intercon[] = { { "Right Line Output PGA", NULL, "Charge Pump" }, }; -static int wm8903_add_widgets(struct snd_soc_codec *codec) -{ - struct snd_soc_dapm_context *dapm = &codec->dapm; - - snd_soc_dapm_new_controls(dapm, wm8903_dapm_widgets, - ARRAY_SIZE(wm8903_dapm_widgets)); - snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); - - return 0; -} - static int wm8903_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { @@ -2028,7 +2017,6 @@ static int wm8903_probe(struct snd_soc_codec *codec) snd_soc_add_controls(codec, wm8903_snd_controls, ARRAY_SIZE(wm8903_snd_controls)); - wm8903_add_widgets(codec); wm8903_init_gpio(codec); @@ -2054,6 +2042,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8903 = { .reg_cache_default = wm8903_reg_defaults, .volatile_register = wm8903_volatile_register, .seq_notifier = wm8903_seq_notifier, + .dapm_widgets = wm8903_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8903_dapm_widgets), + .dapm_routes = wm8903_intercon, + .num_dapm_routes = ARRAY_SIZE(wm8903_intercon), }; #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c new file mode 100644 index 0000000..74983ee --- /dev/null +++ b/sound/soc/codecs/wm8958-dsp2.c @@ -0,0 +1,1028 @@ +/* + * wm8958-dsp2.c -- WM8958 DSP2 support + * + * Copyright 2011 Wolfson Microelectronics plc + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <sound/soc.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <trace/events/asoc.h> + +#include <linux/mfd/wm8994/core.h> +#include <linux/mfd/wm8994/registers.h> +#include <linux/mfd/wm8994/pdata.h> +#include <linux/mfd/wm8994/gpio.h> + +#include "wm8994.h" + +#define WM_FW_BLOCK_INFO 0xff +#define WM_FW_BLOCK_PM 0x00 +#define WM_FW_BLOCK_X 0x01 +#define WM_FW_BLOCK_Y 0x02 +#define WM_FW_BLOCK_Z 0x03 +#define WM_FW_BLOCK_I 0x06 +#define WM_FW_BLOCK_A 0x08 +#define WM_FW_BLOCK_C 0x0c + +static int wm8958_dsp2_fw(struct snd_soc_codec *codec, const char *name, + const struct firmware *fw, bool check) +{ + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + u64 data64; + u32 data32; + const u8 *data; + char *str; + size_t block_len, len; + int ret = 0; + + /* Suppress unneeded downloads */ + if (wm8994->cur_fw == fw) + return 0; + + if (fw->size < 32) { + dev_err(codec->dev, "%s: firmware too short\n", name); + goto err; + } + + if (memcmp(fw->data, "WMFW", 4) != 0) { + dev_err(codec->dev, "%s: firmware has bad file magic %08x\n", + name, data32); + goto err; + } + + memcpy(&data32, fw->data + 4, sizeof(data32)); + len = be32_to_cpu(data32); + + memcpy(&data32, fw->data + 8, sizeof(data32)); + data32 = be32_to_cpu(data32); + if ((data32 >> 24) & 0xff) { + dev_err(codec->dev, "%s: unsupported firmware version %d\n", + name, (data32 >> 24) & 0xff); + goto err; + } + if ((data32 & 0xffff) != 8958) { + dev_err(codec->dev, "%s: unsupported target device %d\n", + name, data32 & 0xffff); + goto err; + } + if (((data32 >> 16) & 0xff) != 0xc) { + dev_err(codec->dev, "%s: unsupported target core %d\n", + name, (data32 >> 16) & 0xff); + goto err; + } + + if (check) { + memcpy(&data64, fw->data + 24, sizeof(u64)); + dev_info(codec->dev, "%s timestamp %llx\n", + name, be64_to_cpu(data64)); + } else { + snd_soc_write(codec, 0x102, 0x2); + snd_soc_write(codec, 0x900, 0x2); + } + + data = fw->data + len; + len = fw->size - len; + while (len) { + if (len < 12) { + dev_err(codec->dev, "%s short data block of %d\n", + name, len); + goto err; + } + + memcpy(&data32, data + 4, sizeof(data32)); + block_len = be32_to_cpu(data32); + if (block_len + 8 > len) { + dev_err(codec->dev, "%d byte block longer than file\n", + block_len); + goto err; + } + if (block_len == 0) { + dev_err(codec->dev, "Zero length block\n"); + goto err; + } + + memcpy(&data32, data, sizeof(data32)); + data32 = be32_to_cpu(data32); + + switch ((data32 >> 24) & 0xff) { + case WM_FW_BLOCK_INFO: + /* Informational text */ + if (!check) + break; + + str = kzalloc(block_len + 1, GFP_KERNEL); + if (str) { + memcpy(str, data + 8, block_len); + dev_info(codec->dev, "%s: %s\n", name, str); + kfree(str); + } else { + dev_err(codec->dev, "Out of memory\n"); + } + break; + case WM_FW_BLOCK_PM: + case WM_FW_BLOCK_X: + case WM_FW_BLOCK_Y: + case WM_FW_BLOCK_Z: + case WM_FW_BLOCK_I: + case WM_FW_BLOCK_A: + case WM_FW_BLOCK_C: + dev_dbg(codec->dev, "%s: %d bytes of %x@%x\n", name, + block_len, (data32 >> 24) & 0xff, + data32 & 0xffffff); + + if (check) + break; + + data32 &= 0xffffff; + + wm8994_bulk_write(codec->control_data, + data32 & 0xffffff, + block_len / 2, + (void *)(data + 8)); + + break; + default: + dev_warn(codec->dev, "%s: unknown block type %d\n", + name, (data32 >> 24) & 0xff); + break; + } + + /* Round up to the next 32 bit word */ + block_len += block_len % 4; + + data += block_len + 8; + len -= block_len + 8; + } + + if (!check) { + dev_dbg(codec->dev, "%s: download done\n", name); + wm8994->cur_fw = fw; + } else { + dev_info(codec->dev, "%s: got firmware\n", name); + } + + goto ok; + +err: + ret = -EINVAL; +ok: + if (!check) { + snd_soc_write(codec, 0x900, 0x0); + snd_soc_write(codec, 0x102, 0x0); + } + + return ret; +} + +static void wm8958_dsp_start_mbc(struct snd_soc_codec *codec, int path) +{ + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + struct wm8994_pdata *pdata = wm8994->pdata; + int i; + + /* If the DSP is already running then noop */ + if (snd_soc_read(codec, WM8958_DSP2_PROGRAM) & WM8958_DSP2_ENA) + return; + + /* If we have MBC firmware download it */ + if (wm8994->mbc) + wm8958_dsp2_fw(codec, "MBC", wm8994->mbc, false); + + snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM, + WM8958_DSP2_ENA, WM8958_DSP2_ENA); + + /* If we've got user supplied MBC settings use them */ + if (pdata && pdata->num_mbc_cfgs) { + struct wm8958_mbc_cfg *cfg + = &pdata->mbc_cfgs[wm8994->mbc_cfg]; + + for (i = 0; i < ARRAY_SIZE(cfg->coeff_regs); i++) + snd_soc_write(codec, i + WM8958_MBC_BAND_1_K_1, + cfg->coeff_regs[i]); + + for (i = 0; i < ARRAY_SIZE(cfg->cutoff_regs); i++) + snd_soc_write(codec, + i + WM8958_MBC_BAND_2_LOWER_CUTOFF_C1_1, + cfg->cutoff_regs[i]); + } + + /* Run the DSP */ + snd_soc_write(codec, WM8958_DSP2_EXECCONTROL, + WM8958_DSP2_RUNR); + + /* And we're off! */ + snd_soc_update_bits(codec, WM8958_DSP2_CONFIG, + WM8958_MBC_ENA | + WM8958_MBC_SEL_MASK, + path << WM8958_MBC_SEL_SHIFT | + WM8958_MBC_ENA); +} + +static void wm8958_dsp_start_vss(struct snd_soc_codec *codec, int path) +{ + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + struct wm8994_pdata *pdata = wm8994->pdata; + int i, ena; + + if (wm8994->mbc_vss) + wm8958_dsp2_fw(codec, "MBC+VSS", wm8994->mbc_vss, false); + + snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM, + WM8958_DSP2_ENA, WM8958_DSP2_ENA); + + /* If we've got user supplied settings use them */ + if (pdata && pdata->num_mbc_cfgs) { + struct wm8958_mbc_cfg *cfg + = &pdata->mbc_cfgs[wm8994->mbc_cfg]; + + for (i = 0; i < ARRAY_SIZE(cfg->combined_regs); i++) + snd_soc_write(codec, i + 0x2800, + cfg->combined_regs[i]); + } + + if (pdata && pdata->num_vss_cfgs) { + struct wm8958_vss_cfg *cfg + = &pdata->vss_cfgs[wm8994->vss_cfg]; + + for (i = 0; i < ARRAY_SIZE(cfg->regs); i++) + snd_soc_write(codec, i + 0x2600, cfg->regs[i]); + } + + if (pdata && pdata->num_vss_hpf_cfgs) { + struct wm8958_vss_hpf_cfg *cfg + = &pdata->vss_hpf_cfgs[wm8994->vss_hpf_cfg]; + + for (i = 0; i < ARRAY_SIZE(cfg->regs); i++) + snd_soc_write(codec, i + 0x2400, cfg->regs[i]); + } + + /* Run the DSP */ + snd_soc_write(codec, WM8958_DSP2_EXECCONTROL, + WM8958_DSP2_RUNR); + + /* Enable the algorithms we've selected */ + ena = 0; + if (wm8994->mbc_ena[path]) + ena |= 0x8; + if (wm8994->hpf2_ena[path]) + ena |= 0x4; + if (wm8994->hpf1_ena[path]) + ena |= 0x2; + if (wm8994->vss_ena[path]) + ena |= 0x1; + + snd_soc_write(codec, 0x2201, ena); + + /* Switch the DSP into the data path */ + snd_soc_update_bits(codec, WM8958_DSP2_CONFIG, + WM8958_MBC_SEL_MASK | WM8958_MBC_ENA, + path << WM8958_MBC_SEL_SHIFT | WM8958_MBC_ENA); +} + +static void wm8958_dsp_start_enh_eq(struct snd_soc_codec *codec, int path) +{ + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + struct wm8994_pdata *pdata = wm8994->pdata; + int i; + + wm8958_dsp2_fw(codec, "ENH_EQ", wm8994->enh_eq, false); + + snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM, + WM8958_DSP2_ENA, WM8958_DSP2_ENA); + + /* If we've got user supplied settings use them */ + if (pdata && pdata->num_enh_eq_cfgs) { + struct wm8958_enh_eq_cfg *cfg + = &pdata->enh_eq_cfgs[wm8994->enh_eq_cfg]; + + for (i = 0; i < ARRAY_SIZE(cfg->regs); i++) + snd_soc_write(codec, i + 0x2200, + cfg->regs[i]); + } + + /* Run the DSP */ + snd_soc_write(codec, WM8958_DSP2_EXECCONTROL, + WM8958_DSP2_RUNR); + + /* Switch the DSP into the data path */ + snd_soc_update_bits(codec, WM8958_DSP2_CONFIG, + WM8958_MBC_SEL_MASK | WM8958_MBC_ENA, + path << WM8958_MBC_SEL_SHIFT | WM8958_MBC_ENA); +} + +static void wm8958_dsp_apply(struct snd_soc_codec *codec, int path, int start) +{ + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + int pwr_reg = snd_soc_read(codec, WM8994_POWER_MANAGEMENT_5); + int ena, reg, aif; + + switch (path) { + case 0: + pwr_reg &= (WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA); + aif = 0; + break; + case 1: + pwr_reg &= (WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA); + aif = 0; + break; + case 2: + pwr_reg &= (WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA); + aif = 1; + break; + default: + BUG(); + return; + } + + /* Do we have both an active AIF and an active algorithm? */ + ena = wm8994->mbc_ena[path] || wm8994->vss_ena[path] || + wm8994->hpf1_ena[path] || wm8994->hpf2_ena[path] || + wm8994->enh_eq_ena[path]; + if (!pwr_reg) + ena = 0; + + reg = snd_soc_read(codec, WM8958_DSP2_PROGRAM); + + dev_dbg(codec->dev, "DSP path %d %d startup: %d, power: %x, DSP: %x\n", + path, wm8994->dsp_active, start, pwr_reg, reg); + + if (start && ena) { + /* If either AIFnCLK is not yet enabled postpone */ + if (!(snd_soc_read(codec, WM8994_AIF1_CLOCKING_1) + & WM8994_AIF1CLK_ENA_MASK) && + !(snd_soc_read(codec, WM8994_AIF2_CLOCKING_1) + & WM8994_AIF2CLK_ENA_MASK)) + return; + + /* Switch the clock over to the appropriate AIF */ + snd_soc_update_bits(codec, WM8994_CLOCKING_1, + WM8958_DSP2CLK_SRC | WM8958_DSP2CLK_ENA, + aif << WM8958_DSP2CLK_SRC_SHIFT | + WM8958_DSP2CLK_ENA); + + if (wm8994->enh_eq_ena[path]) + wm8958_dsp_start_enh_eq(codec, path); + else if (wm8994->vss_ena[path] || wm8994->hpf1_ena[path] || + wm8994->hpf2_ena[path]) + wm8958_dsp_start_vss(codec, path); + else if (wm8994->mbc_ena[path]) + wm8958_dsp_start_mbc(codec, path); + + wm8994->dsp_active = path; + + dev_dbg(codec->dev, "DSP running in path %d\n", path); + } + + if (!start && wm8994->dsp_active == path) { + /* If the DSP is already stopped then noop */ + if (!(reg & WM8958_DSP2_ENA)) + return; + + snd_soc_update_bits(codec, WM8958_DSP2_CONFIG, + WM8958_MBC_ENA, 0); + snd_soc_write(codec, WM8958_DSP2_EXECCONTROL, + WM8958_DSP2_STOP); + snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM, + WM8958_DSP2_ENA, 0); + snd_soc_update_bits(codec, WM8994_CLOCKING_1, + WM8958_DSP2CLK_ENA, 0); + + wm8994->dsp_active = -1; + + dev_dbg(codec->dev, "DSP stopped\n"); + } +} + +int wm8958_aif_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + int i; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + case SND_SOC_DAPM_PRE_PMU: + for (i = 0; i < 3; i++) + wm8958_dsp_apply(codec, i, 1); + break; + case SND_SOC_DAPM_POST_PMD: + case SND_SOC_DAPM_PRE_PMD: + for (i = 0; i < 3; i++) + wm8958_dsp_apply(codec, i, 0); + break; + } + + return 0; +} + +/* Check if DSP2 is in use on another AIF */ +static int wm8958_dsp2_busy(struct wm8994_priv *wm8994, int aif) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(wm8994->mbc_ena); i++) { + if (i == aif) + continue; + if (wm8994->mbc_ena[i] || wm8994->vss_ena[i] || + wm8994->hpf1_ena[i] || wm8994->hpf2_ena[i]) + return 1; + } + + return 0; +} + +static int wm8958_put_mbc_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + struct wm8994_pdata *pdata = wm8994->pdata; + int value = ucontrol->value.integer.value[0]; + int reg; + + /* Don't allow on the fly reconfiguration */ + reg = snd_soc_read(codec, WM8994_CLOCKING_1); + if (reg < 0 || reg & WM8958_DSP2CLK_ENA) + return -EBUSY; + + if (value >= pdata->num_mbc_cfgs) + return -EINVAL; + + wm8994->mbc_cfg = value; + + return 0; +} + +static int wm8958_get_mbc_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = wm8994->mbc_cfg; + + return 0; +} + +static int wm8958_mbc_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int wm8958_mbc_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int mbc = kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = wm8994->mbc_ena[mbc]; + + return 0; +} + +static int wm8958_mbc_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int mbc = kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + if (ucontrol->value.integer.value[0] > 1) + return -EINVAL; + + if (wm8958_dsp2_busy(wm8994, mbc)) { + dev_dbg(codec->dev, "DSP2 active on %d already\n", mbc); + return -EBUSY; + } + + if (wm8994->enh_eq_ena[mbc]) + return -EBUSY; + + wm8994->mbc_ena[mbc] = ucontrol->value.integer.value[0]; + + wm8958_dsp_apply(codec, mbc, wm8994->mbc_ena[mbc]); + + return 0; +} + +#define WM8958_MBC_SWITCH(xname, xval) {\ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .info = wm8958_mbc_info, \ + .get = wm8958_mbc_get, .put = wm8958_mbc_put, \ + .private_value = xval } + +static int wm8958_put_vss_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + struct wm8994_pdata *pdata = wm8994->pdata; + int value = ucontrol->value.integer.value[0]; + int reg; + + /* Don't allow on the fly reconfiguration */ + reg = snd_soc_read(codec, WM8994_CLOCKING_1); + if (reg < 0 || reg & WM8958_DSP2CLK_ENA) + return -EBUSY; + + if (value >= pdata->num_vss_cfgs) + return -EINVAL; + + wm8994->vss_cfg = value; + + return 0; +} + +static int wm8958_get_vss_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = wm8994->vss_cfg; + + return 0; +} + +static int wm8958_put_vss_hpf_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + struct wm8994_pdata *pdata = wm8994->pdata; + int value = ucontrol->value.integer.value[0]; + int reg; + + /* Don't allow on the fly reconfiguration */ + reg = snd_soc_read(codec, WM8994_CLOCKING_1); + if (reg < 0 || reg & WM8958_DSP2CLK_ENA) + return -EBUSY; + + if (value >= pdata->num_vss_hpf_cfgs) + return -EINVAL; + + wm8994->vss_hpf_cfg = value; + + return 0; +} + +static int wm8958_get_vss_hpf_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = wm8994->vss_hpf_cfg; + + return 0; +} + +static int wm8958_vss_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int wm8958_vss_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int vss = kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = wm8994->vss_ena[vss]; + + return 0; +} + +static int wm8958_vss_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int vss = kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + if (ucontrol->value.integer.value[0] > 1) + return -EINVAL; + + if (!wm8994->mbc_vss) + return -ENODEV; + + if (wm8958_dsp2_busy(wm8994, vss)) { + dev_dbg(codec->dev, "DSP2 active on %d already\n", vss); + return -EBUSY; + } + + if (wm8994->enh_eq_ena[vss]) + return -EBUSY; + + wm8994->vss_ena[vss] = ucontrol->value.integer.value[0]; + + wm8958_dsp_apply(codec, vss, wm8994->vss_ena[vss]); + + return 0; +} + + +#define WM8958_VSS_SWITCH(xname, xval) {\ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .info = wm8958_vss_info, \ + .get = wm8958_vss_get, .put = wm8958_vss_put, \ + .private_value = xval } + +static int wm8958_hpf_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int wm8958_hpf_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int hpf = kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + if (hpf < 3) + ucontrol->value.integer.value[0] = wm8994->hpf1_ena[hpf % 3]; + else + ucontrol->value.integer.value[0] = wm8994->hpf2_ena[hpf % 3]; + + return 0; +} + +static int wm8958_hpf_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int hpf = kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + if (ucontrol->value.integer.value[0] > 1) + return -EINVAL; + + if (!wm8994->mbc_vss) + return -ENODEV; + + if (wm8958_dsp2_busy(wm8994, hpf % 3)) { + dev_dbg(codec->dev, "DSP2 active on %d already\n", hpf); + return -EBUSY; + } + + if (wm8994->enh_eq_ena[hpf % 3]) + return -EBUSY; + + if (hpf < 3) + wm8994->hpf1_ena[hpf % 3] = ucontrol->value.integer.value[0]; + else + wm8994->hpf2_ena[hpf % 3] = ucontrol->value.integer.value[0]; + + wm8958_dsp_apply(codec, hpf % 3, ucontrol->value.integer.value[0]); + + return 0; +} + +#define WM8958_HPF_SWITCH(xname, xval) {\ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .info = wm8958_hpf_info, \ + .get = wm8958_hpf_get, .put = wm8958_hpf_put, \ + .private_value = xval } + +static int wm8958_put_enh_eq_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + struct wm8994_pdata *pdata = wm8994->pdata; + int value = ucontrol->value.integer.value[0]; + int reg; + + /* Don't allow on the fly reconfiguration */ + reg = snd_soc_read(codec, WM8994_CLOCKING_1); + if (reg < 0 || reg & WM8958_DSP2CLK_ENA) + return -EBUSY; + + if (value >= pdata->num_enh_eq_cfgs) + return -EINVAL; + + wm8994->enh_eq_cfg = value; + + return 0; +} + +static int wm8958_get_enh_eq_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = wm8994->enh_eq_cfg; + + return 0; +} + +static int wm8958_enh_eq_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int wm8958_enh_eq_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int eq = kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = wm8994->enh_eq_ena[eq]; + + return 0; +} + +static int wm8958_enh_eq_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int eq = kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + if (ucontrol->value.integer.value[0] > 1) + return -EINVAL; + + if (!wm8994->enh_eq) + return -ENODEV; + + if (wm8958_dsp2_busy(wm8994, eq)) { + dev_dbg(codec->dev, "DSP2 active on %d already\n", eq); + return -EBUSY; + } + + if (wm8994->mbc_ena[eq] || wm8994->vss_ena[eq] || + wm8994->hpf1_ena[eq] || wm8994->hpf2_ena[eq]) + return -EBUSY; + + wm8994->enh_eq_ena[eq] = ucontrol->value.integer.value[0]; + + wm8958_dsp_apply(codec, eq, ucontrol->value.integer.value[0]); + + return 0; +} + +#define WM8958_ENH_EQ_SWITCH(xname, xval) {\ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .info = wm8958_enh_eq_info, \ + .get = wm8958_enh_eq_get, .put = wm8958_enh_eq_put, \ + .private_value = xval } + +static const struct snd_kcontrol_new wm8958_mbc_snd_controls[] = { +WM8958_MBC_SWITCH("AIF1DAC1 MBC Switch", 0), +WM8958_MBC_SWITCH("AIF1DAC2 MBC Switch", 1), +WM8958_MBC_SWITCH("AIF2DAC MBC Switch", 2), +}; + +static const struct snd_kcontrol_new wm8958_vss_snd_controls[] = { +WM8958_VSS_SWITCH("AIF1DAC1 VSS Switch", 0), +WM8958_VSS_SWITCH("AIF1DAC2 VSS Switch", 1), +WM8958_VSS_SWITCH("AIF2DAC VSS Switch", 2), +WM8958_HPF_SWITCH("AIF1DAC1 HPF1 Switch", 0), +WM8958_HPF_SWITCH("AIF1DAC2 HPF1 Switch", 1), +WM8958_HPF_SWITCH("AIF2DAC HPF1 Switch", 2), +WM8958_HPF_SWITCH("AIF1DAC1 HPF2 Switch", 3), +WM8958_HPF_SWITCH("AIF1DAC2 HPF2 Switch", 4), +WM8958_HPF_SWITCH("AIF2DAC HPF2 Switch", 5), +}; + +static const struct snd_kcontrol_new wm8958_enh_eq_snd_controls[] = { +WM8958_ENH_EQ_SWITCH("AIF1DAC1 Enhanced EQ Switch", 0), +WM8958_ENH_EQ_SWITCH("AIF1DAC2 Enhanced EQ Switch", 1), +WM8958_ENH_EQ_SWITCH("AIF2DAC Enhanced EQ Switch", 2), +}; + +static void wm8958_enh_eq_loaded(const struct firmware *fw, void *context) +{ + struct snd_soc_codec *codec = context; + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + if (fw && (wm8958_dsp2_fw(codec, "ENH_EQ", fw, true) == 0)) { + mutex_lock(&codec->mutex); + wm8994->enh_eq = fw; + mutex_unlock(&codec->mutex); + } +} + +static void wm8958_mbc_vss_loaded(const struct firmware *fw, void *context) +{ + struct snd_soc_codec *codec = context; + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + if (fw && (wm8958_dsp2_fw(codec, "MBC+VSS", fw, true) == 0)) { + mutex_lock(&codec->mutex); + wm8994->mbc_vss = fw; + mutex_unlock(&codec->mutex); + } + + /* We can't have more than one request outstanding at once so + * we daisy chain. + */ + request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + "wm8958_enh_eq.wfw", codec->dev, GFP_KERNEL, + codec, wm8958_enh_eq_loaded); +} + +static void wm8958_mbc_loaded(const struct firmware *fw, void *context) +{ + struct snd_soc_codec *codec = context; + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + if (wm8958_dsp2_fw(codec, "MBC", fw, true) != 0) + return; + + mutex_lock(&codec->mutex); + wm8994->mbc = fw; + mutex_unlock(&codec->mutex); + + /* We can't have more than one request outstanding at once so + * we daisy chain. + */ + request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + "wm8958_mbc_vss.wfw", codec->dev, GFP_KERNEL, + codec, wm8958_mbc_vss_loaded); +} + +void wm8958_dsp2_init(struct snd_soc_codec *codec) +{ + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + struct wm8994_pdata *pdata = wm8994->pdata; + int ret, i; + + wm8994->dsp_active = -1; + + snd_soc_add_controls(codec, wm8958_mbc_snd_controls, + ARRAY_SIZE(wm8958_mbc_snd_controls)); + snd_soc_add_controls(codec, wm8958_vss_snd_controls, + ARRAY_SIZE(wm8958_vss_snd_controls)); + snd_soc_add_controls(codec, wm8958_enh_eq_snd_controls, + ARRAY_SIZE(wm8958_enh_eq_snd_controls)); + + + /* We don't *require* firmware and don't want to delay boot */ + request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + "wm8958_mbc.wfw", codec->dev, GFP_KERNEL, + codec, wm8958_mbc_loaded); + + if (!pdata) + return; + + if (pdata->num_mbc_cfgs) { + struct snd_kcontrol_new control[] = { + SOC_ENUM_EXT("MBC Mode", wm8994->mbc_enum, + wm8958_get_mbc_enum, wm8958_put_mbc_enum), + }; + + /* We need an array of texts for the enum API */ + wm8994->mbc_texts = kmalloc(sizeof(char *) + * pdata->num_mbc_cfgs, GFP_KERNEL); + if (!wm8994->mbc_texts) { + dev_err(wm8994->codec->dev, + "Failed to allocate %d MBC config texts\n", + pdata->num_mbc_cfgs); + return; + } + + 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.texts = wm8994->mbc_texts; + + ret = snd_soc_add_controls(wm8994->codec, control, 1); + if (ret != 0) + dev_err(wm8994->codec->dev, + "Failed to add MBC mode controls: %d\n", ret); + } + + if (pdata->num_vss_cfgs) { + struct snd_kcontrol_new control[] = { + SOC_ENUM_EXT("VSS Mode", wm8994->vss_enum, + wm8958_get_vss_enum, wm8958_put_vss_enum), + }; + + /* We need an array of texts for the enum API */ + wm8994->vss_texts = kmalloc(sizeof(char *) + * pdata->num_vss_cfgs, GFP_KERNEL); + if (!wm8994->vss_texts) { + dev_err(wm8994->codec->dev, + "Failed to allocate %d VSS config texts\n", + pdata->num_vss_cfgs); + return; + } + + 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.texts = wm8994->vss_texts; + + ret = snd_soc_add_controls(wm8994->codec, control, 1); + if (ret != 0) + dev_err(wm8994->codec->dev, + "Failed to add VSS mode controls: %d\n", ret); + } + + if (pdata->num_vss_hpf_cfgs) { + struct snd_kcontrol_new control[] = { + SOC_ENUM_EXT("VSS HPF Mode", wm8994->vss_hpf_enum, + wm8958_get_vss_hpf_enum, + wm8958_put_vss_hpf_enum), + }; + + /* We need an array of texts for the enum API */ + wm8994->vss_hpf_texts = kmalloc(sizeof(char *) + * pdata->num_vss_hpf_cfgs, GFP_KERNEL); + if (!wm8994->vss_hpf_texts) { + dev_err(wm8994->codec->dev, + "Failed to allocate %d VSS HPF config texts\n", + pdata->num_vss_hpf_cfgs); + return; + } + + 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.texts = wm8994->vss_hpf_texts; + + ret = snd_soc_add_controls(wm8994->codec, control, 1); + if (ret != 0) + dev_err(wm8994->codec->dev, + "Failed to add VSS HPFmode controls: %d\n", + ret); + } + + if (pdata->num_enh_eq_cfgs) { + struct snd_kcontrol_new control[] = { + SOC_ENUM_EXT("Enhanced EQ Mode", wm8994->enh_eq_enum, + wm8958_get_enh_eq_enum, + wm8958_put_enh_eq_enum), + }; + + /* We need an array of texts for the enum API */ + wm8994->enh_eq_texts = kmalloc(sizeof(char *) + * pdata->num_enh_eq_cfgs, GFP_KERNEL); + if (!wm8994->enh_eq_texts) { + dev_err(wm8994->codec->dev, + "Failed to allocate %d enhanced EQ config texts\n", + pdata->num_enh_eq_cfgs); + return; + } + + 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.texts = wm8994->enh_eq_texts; + + ret = snd_soc_add_controls(wm8994->codec, control, 1); + if (ret != 0) + dev_err(wm8994->codec->dev, + "Failed to add enhanced EQ controls: %d\n", + ret); + } +} diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 24857d5..b6d47e7 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -38,12 +38,6 @@ #include "wm8994.h" #include "wm_hubs.h" -struct fll_config { - int src; - int in; - int out; -}; - #define WM8994_NUM_DRC 3 #define WM8994_NUM_EQ 3 @@ -59,63 +53,11 @@ static int wm8994_retune_mobile_base[] = { WM8994_AIF2_EQ_GAINS_1, }; -struct wm8994_micdet { - struct snd_soc_jack *jack; - int det; - int shrt; -}; - -/* codec private data */ -struct wm8994_priv { - struct wm_hubs_data hubs; - enum snd_soc_control_type control_type; - void *control_data; - struct snd_soc_codec *codec; - int sysclk[2]; - int sysclk_rate[2]; - int mclk[2]; - int aifclk[2]; - struct fll_config fll[2], fll_suspend[2]; - - int dac_rates[2]; - int lrclk_shared[2]; - - int mbc_ena[3]; - - /* Platform dependant DRC configuration */ - const char **drc_texts; - int drc_cfg[WM8994_NUM_DRC]; - struct soc_enum drc_enum; - - /* Platform dependant ReTune mobile configuration */ - int num_retune_mobile_texts; - const char **retune_mobile_texts; - int retune_mobile_cfg[WM8994_NUM_EQ]; - struct soc_enum retune_mobile_enum; - - /* Platform dependant MBC configuration */ - int mbc_cfg; - const char **mbc_texts; - struct soc_enum mbc_enum; - - struct wm8994_micdet micdet[2]; - - wm8958_micdet_cb jack_cb; - void *jack_cb_data; - int micdet_irq; - - int revision; - struct wm8994_pdata *pdata; - - unsigned int aif1clk_enable:1; - unsigned int aif2clk_enable:1; - - unsigned int aif1clk_disable:1; - unsigned int aif2clk_disable:1; -}; - static int wm8994_readable(struct snd_soc_codec *codec, unsigned int reg) { + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + struct wm8994 *control = wm8994->control_data; + switch (reg) { case WM8994_GPIO_1: case WM8994_GPIO_2: @@ -132,6 +74,15 @@ static int wm8994_readable(struct snd_soc_codec *codec, unsigned int reg) case WM8994_INTERRUPT_STATUS_2: case WM8994_INTERRUPT_RAW_STATUS_2: return 1; + + case WM8958_DSP2_PROGRAM: + case WM8958_DSP2_CONFIG: + case WM8958_DSP2_EXECCONTROL: + if (control->type == WM8958) + return 1; + else + return 0; + default: break; } @@ -574,215 +525,6 @@ static const struct soc_enum dac_osr = static const struct soc_enum adc_osr = SOC_ENUM_SINGLE(WM8994_OVERSAMPLING, 1, 2, osr_text); -static void wm8958_mbc_apply(struct snd_soc_codec *codec, int mbc, int start) -{ - struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); - struct wm8994_pdata *pdata = wm8994->pdata; - int pwr_reg = snd_soc_read(codec, WM8994_POWER_MANAGEMENT_5); - int ena, reg, aif, i; - - switch (mbc) { - case 0: - pwr_reg &= (WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA); - aif = 0; - break; - case 1: - pwr_reg &= (WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA); - aif = 0; - break; - case 2: - pwr_reg &= (WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA); - aif = 1; - break; - default: - BUG(); - return; - } - - /* We can only enable the MBC if the AIF is enabled and we - * want it to be enabled. */ - ena = pwr_reg && wm8994->mbc_ena[mbc]; - - reg = snd_soc_read(codec, WM8958_DSP2_PROGRAM); - - dev_dbg(codec->dev, "MBC %d startup: %d, power: %x, DSP: %x\n", - mbc, start, pwr_reg, reg); - - if (start && ena) { - /* If the DSP is already running then noop */ - if (reg & WM8958_DSP2_ENA) - return; - - /* Switch the clock over to the appropriate AIF */ - snd_soc_update_bits(codec, WM8994_CLOCKING_1, - WM8958_DSP2CLK_SRC | WM8958_DSP2CLK_ENA, - aif << WM8958_DSP2CLK_SRC_SHIFT | - WM8958_DSP2CLK_ENA); - - snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM, - WM8958_DSP2_ENA, WM8958_DSP2_ENA); - - /* If we've got user supplied MBC settings use them */ - if (pdata && pdata->num_mbc_cfgs) { - struct wm8958_mbc_cfg *cfg - = &pdata->mbc_cfgs[wm8994->mbc_cfg]; - - for (i = 0; i < ARRAY_SIZE(cfg->coeff_regs); i++) - snd_soc_write(codec, i + WM8958_MBC_BAND_1_K_1, - cfg->coeff_regs[i]); - - for (i = 0; i < ARRAY_SIZE(cfg->cutoff_regs); i++) - snd_soc_write(codec, - i + WM8958_MBC_BAND_2_LOWER_CUTOFF_C1_1, - cfg->cutoff_regs[i]); - } - - /* Run the DSP */ - snd_soc_write(codec, WM8958_DSP2_EXECCONTROL, - WM8958_DSP2_RUNR); - - /* And we're off! */ - snd_soc_update_bits(codec, WM8958_DSP2_CONFIG, - WM8958_MBC_ENA | WM8958_MBC_SEL_MASK, - mbc << WM8958_MBC_SEL_SHIFT | - WM8958_MBC_ENA); - } else { - /* If the DSP is already stopped then noop */ - if (!(reg & WM8958_DSP2_ENA)) - return; - - snd_soc_update_bits(codec, WM8958_DSP2_CONFIG, - WM8958_MBC_ENA, 0); - snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM, - WM8958_DSP2_ENA, 0); - snd_soc_update_bits(codec, WM8994_CLOCKING_1, - WM8958_DSP2CLK_ENA, 0); - } -} - -static int wm8958_aif_ev(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct snd_soc_codec *codec = w->codec; - int mbc; - - switch (w->shift) { - case 13: - case 12: - mbc = 2; - break; - case 11: - case 10: - mbc = 1; - break; - case 9: - case 8: - mbc = 0; - break; - default: - BUG(); - return -EINVAL; - } - - switch (event) { - case SND_SOC_DAPM_POST_PMU: - wm8958_mbc_apply(codec, mbc, 1); - break; - case SND_SOC_DAPM_POST_PMD: - wm8958_mbc_apply(codec, mbc, 0); - break; - } - - return 0; -} - -static int wm8958_put_mbc_enum(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); - struct wm8994_pdata *pdata = wm8994->pdata; - int value = ucontrol->value.integer.value[0]; - int reg; - - /* Don't allow on the fly reconfiguration */ - reg = snd_soc_read(codec, WM8994_CLOCKING_1); - if (reg < 0 || reg & WM8958_DSP2CLK_ENA) - return -EBUSY; - - if (value >= pdata->num_mbc_cfgs) - return -EINVAL; - - wm8994->mbc_cfg = value; - - return 0; -} - -static int wm8958_get_mbc_enum(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); - - ucontrol->value.enumerated.item[0] = wm8994->mbc_cfg; - - return 0; -} - -static int wm8958_mbc_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} - -static int wm8958_mbc_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - int mbc = kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); - - ucontrol->value.integer.value[0] = wm8994->mbc_ena[mbc]; - - return 0; -} - -static int wm8958_mbc_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - int mbc = kcontrol->private_value; - int i; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); - - if (ucontrol->value.integer.value[0] > 1) - return -EINVAL; - - for (i = 0; i < ARRAY_SIZE(wm8994->mbc_ena); i++) { - if (mbc != i && wm8994->mbc_ena[i]) { - dev_dbg(codec->dev, "MBC %d active already\n", mbc); - return -EBUSY; - } - } - - wm8994->mbc_ena[mbc] = ucontrol->value.integer.value[0]; - - wm8958_mbc_apply(codec, mbc, wm8994->mbc_ena[mbc]); - - return 0; -} - -#define WM8958_MBC_SWITCH(xname, xval) {\ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\ - .info = wm8958_mbc_info, \ - .get = wm8958_mbc_get, .put = wm8958_mbc_put, \ - .private_value = xval } - static const struct snd_kcontrol_new wm8994_snd_controls[] = { SOC_DOUBLE_R_TLV("AIF1ADC1 Volume", WM8994_AIF1_ADC1_LEFT_VOLUME, WM8994_AIF1_ADC1_RIGHT_VOLUME, @@ -924,9 +666,6 @@ SOC_SINGLE_TLV("AIF2 EQ5 Volume", WM8994_AIF2_EQ_GAINS_2, 6, 31, 0, static const struct snd_kcontrol_new wm8958_snd_controls[] = { SOC_SINGLE_TLV("AIF3 Boost Volume", WM8958_AIF3_CONTROL_2, 10, 3, 0, aif_tlv), -WM8958_MBC_SWITCH("AIF1DAC1 MBC Switch", 0), -WM8958_MBC_SWITCH("AIF1DAC2 MBC Switch", 1), -WM8958_MBC_SWITCH("AIF2DAC MBC Switch", 2), }; static int clk_sys_event(struct snd_soc_dapm_widget *w, @@ -1032,6 +771,9 @@ static int late_enable_ev(struct snd_soc_dapm_widget *w, break; } + /* We may also have postponed startup of DSP, handle that. */ + wm8958_aif_ev(w, kcontrol, event); + return 0; } @@ -2180,6 +1922,8 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec, WM8994_VMID_BUF_ENA | WM8994_VMID_RAMP_MASK, 0); + wm8994->cur_fw = NULL; + pm_runtime_put(codec->dev); } break; @@ -2676,7 +2420,7 @@ static int wm8994_suspend(struct snd_soc_codec *codec, pm_message_t state) for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) { memcpy(&wm8994->fll_suspend[i], &wm8994->fll[i], - sizeof(struct fll_config)); + sizeof(struct wm8994_fll_config)); ret = _wm8994_set_fll(codec, i + 1, 0, 0, 0); if (ret < 0) dev_warn(codec->dev, "Failed to stop FLL%d: %d\n", @@ -2862,34 +2606,6 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994) dev_dbg(codec->dev, "%d ReTune Mobile configurations\n", pdata->num_retune_mobile_cfgs); - if (pdata->num_mbc_cfgs) { - struct snd_kcontrol_new control[] = { - SOC_ENUM_EXT("MBC Mode", wm8994->mbc_enum, - wm8958_get_mbc_enum, wm8958_put_mbc_enum), - }; - - /* We need an array of texts for the enum API */ - wm8994->mbc_texts = kmalloc(sizeof(char *) - * pdata->num_mbc_cfgs, GFP_KERNEL); - if (!wm8994->mbc_texts) { - dev_err(wm8994->codec->dev, - "Failed to allocate %d MBC config texts\n", - pdata->num_mbc_cfgs); - return; - } - - 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.texts = wm8994->mbc_texts; - - ret = snd_soc_add_controls(wm8994->codec, control, 1); - if (ret != 0) - dev_err(wm8994->codec->dev, - "Failed to add MBC mode controls: %d\n", ret); - } - if (pdata->num_retune_mobile_cfgs) wm8994_handle_retune_mobile_pdata(wm8994); else @@ -3343,14 +3059,23 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) case WM8958: snd_soc_add_controls(codec, wm8958_snd_controls, ARRAY_SIZE(wm8958_snd_controls)); - snd_soc_dapm_new_controls(dapm, wm8994_lateclk_widgets, - ARRAY_SIZE(wm8994_lateclk_widgets)); - snd_soc_dapm_new_controls(dapm, wm8994_adc_widgets, - ARRAY_SIZE(wm8994_adc_widgets)); - snd_soc_dapm_new_controls(dapm, wm8994_dac_widgets, - ARRAY_SIZE(wm8994_dac_widgets)); snd_soc_dapm_new_controls(dapm, wm8958_dapm_widgets, ARRAY_SIZE(wm8958_dapm_widgets)); + if (wm8994->revision < 1) { + snd_soc_dapm_new_controls(dapm, wm8994_lateclk_revd_widgets, + ARRAY_SIZE(wm8994_lateclk_revd_widgets)); + snd_soc_dapm_new_controls(dapm, wm8994_adc_revd_widgets, + ARRAY_SIZE(wm8994_adc_revd_widgets)); + snd_soc_dapm_new_controls(dapm, wm8994_dac_revd_widgets, + ARRAY_SIZE(wm8994_dac_revd_widgets)); + } else { + snd_soc_dapm_new_controls(dapm, wm8994_lateclk_widgets, + ARRAY_SIZE(wm8994_lateclk_widgets)); + snd_soc_dapm_new_controls(dapm, wm8994_adc_widgets, + ARRAY_SIZE(wm8994_adc_widgets)); + snd_soc_dapm_new_controls(dapm, wm8994_dac_widgets, + ARRAY_SIZE(wm8994_dac_widgets)); + } break; } @@ -3374,10 +3099,19 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) } break; case WM8958: - snd_soc_dapm_add_routes(dapm, wm8994_lateclk_intercon, - ARRAY_SIZE(wm8994_lateclk_intercon)); - snd_soc_dapm_add_routes(dapm, wm8958_intercon, - ARRAY_SIZE(wm8958_intercon)); + if (wm8994->revision < 1) { + snd_soc_dapm_add_routes(dapm, wm8994_revd_intercon, + ARRAY_SIZE(wm8994_revd_intercon)); + snd_soc_dapm_add_routes(dapm, wm8994_lateclk_revd_intercon, + ARRAY_SIZE(wm8994_lateclk_revd_intercon)); + } else { + snd_soc_dapm_add_routes(dapm, wm8994_lateclk_intercon, + ARRAY_SIZE(wm8994_lateclk_intercon)); + snd_soc_dapm_add_routes(dapm, wm8958_intercon, + ARRAY_SIZE(wm8958_intercon)); + } + + wm8958_dsp2_init(codec); break; } @@ -3420,6 +3154,12 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec) free_irq(wm8994->micdet_irq, wm8994); break; } + if (wm8994->mbc) + release_firmware(wm8994->mbc); + if (wm8994->mbc_vss) + release_firmware(wm8994->mbc_vss); + if (wm8994->enh_eq) + release_firmware(wm8994->enh_eq); kfree(wm8994->retune_mobile_texts); kfree(wm8994->drc_texts); kfree(wm8994); diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h index 999b885..0a1db04 100644 --- a/sound/soc/codecs/wm8994.h +++ b/sound/soc/codecs/wm8994.h @@ -10,6 +10,9 @@ #define _WM8994_H #include <sound/soc.h> +#include <linux/firmware.h> + +#include "wm_hubs.h" /* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */ #define WM8994_SYSCLK_MCLK1 1 @@ -45,4 +48,98 @@ struct wm8994_access_mask { extern const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE]; extern const u16 wm8994_reg_defaults[WM8994_CACHE_SIZE]; +int wm8958_aif_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); + +void wm8958_dsp2_init(struct snd_soc_codec *codec); + +struct wm8994_micdet { + struct snd_soc_jack *jack; + int det; + int shrt; +}; + +/* codec private data */ +struct wm8994_fll_config { + int src; + int in; + int out; +}; + +#define WM8994_NUM_DRC 3 +#define WM8994_NUM_EQ 3 + +struct wm8994_priv { + struct wm_hubs_data hubs; + enum snd_soc_control_type control_type; + void *control_data; + struct snd_soc_codec *codec; + int sysclk[2]; + int sysclk_rate[2]; + int mclk[2]; + int aifclk[2]; + struct wm8994_fll_config fll[2], fll_suspend[2]; + + int dac_rates[2]; + int lrclk_shared[2]; + + int mbc_ena[3]; + int hpf1_ena[3]; + int hpf2_ena[3]; + int vss_ena[3]; + int enh_eq_ena[3]; + + /* Platform dependant DRC configuration */ + const char **drc_texts; + int drc_cfg[WM8994_NUM_DRC]; + struct soc_enum drc_enum; + + /* Platform dependant ReTune mobile configuration */ + int num_retune_mobile_texts; + const char **retune_mobile_texts; + int retune_mobile_cfg[WM8994_NUM_EQ]; + struct soc_enum retune_mobile_enum; + + /* Platform dependant MBC configuration */ + int mbc_cfg; + const char **mbc_texts; + struct soc_enum mbc_enum; + + /* Platform dependant VSS configuration */ + int vss_cfg; + const char **vss_texts; + struct soc_enum vss_enum; + + /* Platform dependant VSS HPF configuration */ + int vss_hpf_cfg; + const char **vss_hpf_texts; + struct soc_enum vss_hpf_enum; + + /* Platform dependant enhanced EQ configuration */ + int enh_eq_cfg; + const char **enh_eq_texts; + struct soc_enum enh_eq_enum; + + struct wm8994_micdet micdet[2]; + + wm8958_micdet_cb jack_cb; + void *jack_cb_data; + int micdet_irq; + + int revision; + struct wm8994_pdata *pdata; + + unsigned int aif1clk_enable:1; + unsigned int aif2clk_enable:1; + + unsigned int aif1clk_disable:1; + unsigned int aif2clk_disable:1; + + int dsp_active; + const struct firmware *cur_fw; + const struct firmware *mbc; + const struct firmware *mbc_vss; + const struct firmware *enh_eq; +}; + #endif diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c index 47b357a..646b58d 100644 --- a/sound/soc/codecs/wm9705.c +++ b/sound/soc/codecs/wm9705.c @@ -142,7 +142,7 @@ static const struct snd_soc_dapm_widget wm9705_dapm_widgets[] = { * constantly enabled, we use the mutes on those inputs to simulate such * controls. */ -static const struct snd_soc_dapm_route audio_map[] = { +static const struct snd_soc_dapm_route wm9705_audio_map[] = { /* HP mixer */ {"HP Mixer", "PCBeep Playback Switch", "PCBEEP PGA"}, {"HP Mixer", "CD Playback Switch", "CD PGA"}, @@ -200,17 +200,6 @@ static const struct snd_soc_dapm_route audio_map[] = { {"Right ADC", NULL, "ADC PGA"}, }; -static int wm9705_add_widgets(struct snd_soc_codec *codec) -{ - struct snd_soc_dapm_context *dapm = &codec->dapm; - - snd_soc_dapm_new_controls(dapm, wm9705_dapm_widgets, - ARRAY_SIZE(wm9705_dapm_widgets)); - snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - - return 0; -} - /* We use a register cache to enhance read performance. */ static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg) { @@ -364,7 +353,6 @@ static int wm9705_soc_probe(struct snd_soc_codec *codec) snd_soc_add_controls(codec, wm9705_snd_ac97_controls, ARRAY_SIZE(wm9705_snd_ac97_controls)); - wm9705_add_widgets(codec); return 0; @@ -390,6 +378,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9705 = { .reg_word_size = sizeof(u16), .reg_cache_step = 2, .reg_cache_default = wm9705_reg, + .dapm_widgets = wm9705_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm9705_dapm_widgets), + .dapm_routes = wm9705_audio_map, + .num_dapm_routes = ARRAY_SIZE(wm9705_audio_map), }; static __devinit int wm9705_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index bf5d4ef..90117f8 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -332,7 +332,7 @@ SND_SOC_DAPM_INPUT("MIC1"), SND_SOC_DAPM_INPUT("MIC2"), }; -static const struct snd_soc_dapm_route audio_map[] = { +static const struct snd_soc_dapm_route wm9712_audio_map[] = { /* virtual mixer - mixes left & right channels for spk and mono */ {"AC97 Mixer", NULL, "Left DAC"}, {"AC97 Mixer", NULL, "Right DAC"}, @@ -429,17 +429,6 @@ static const struct snd_soc_dapm_route audio_map[] = { {"ROUT2", NULL, "Speaker PGA"}, }; -static int wm9712_add_widgets(struct snd_soc_codec *codec) -{ - struct snd_soc_dapm_context *dapm = &codec->dapm; - - snd_soc_dapm_new_controls(dapm, wm9712_dapm_widgets, - ARRAY_SIZE(wm9712_dapm_widgets)); - snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - - return 0; -} - static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg) { @@ -651,7 +640,6 @@ static int wm9712_soc_probe(struct snd_soc_codec *codec) wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY); snd_soc_add_controls(codec, wm9712_snd_ac97_controls, ARRAY_SIZE(wm9712_snd_ac97_controls)); - wm9712_add_widgets(codec); return 0; @@ -678,6 +666,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9712 = { .reg_word_size = sizeof(u16), .reg_cache_step = 2, .reg_cache_default = wm9712_reg, + .dapm_widgets = wm9712_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm9712_dapm_widgets), + .dapm_routes = wm9712_audio_map, + .num_dapm_routes = ARRAY_SIZE(wm9712_audio_map), }; static __devinit int wm9712_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 38ed985..7167cb6 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -487,7 +487,7 @@ SND_SOC_DAPM_INPUT("MIC2B"), SND_SOC_DAPM_VMID("VMID"), }; -static const struct snd_soc_dapm_route audio_map[] = { +static const struct snd_soc_dapm_route wm9713_audio_map[] = { /* left HP mixer */ {"Left HP Mixer", "Beep Playback Switch", "PCBEEP"}, {"Left HP Mixer", "Voice Playback Switch", "Voice DAC"}, @@ -644,18 +644,6 @@ static const struct snd_soc_dapm_route audio_map[] = { {"Capture Mono Mux", "Right", "Right Capture Source"}, }; -static int wm9713_add_widgets(struct snd_soc_codec *codec) -{ - struct snd_soc_dapm_context *dapm = &codec->dapm; - - snd_soc_dapm_new_controls(dapm, wm9713_dapm_widgets, - ARRAY_SIZE(wm9713_dapm_widgets)); - - snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - - return 0; -} - static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg) { @@ -1231,7 +1219,6 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec) snd_soc_add_controls(codec, wm9713_snd_ac97_controls, ARRAY_SIZE(wm9713_snd_ac97_controls)); - wm9713_add_widgets(codec); return 0; @@ -1262,6 +1249,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = { .reg_word_size = sizeof(u16), .reg_cache_step = 2, .reg_cache_default = wm9713_reg, + .dapm_widgets = wm9713_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm9713_dapm_widgets), + .dapm_routes = wm9713_audio_map, + .num_dapm_routes = ARRAY_SIZE(wm9713_audio_map), }; static __devinit int wm9713_probe(struct platform_device *pdev) diff --git a/sound/soc/imx/imx-ssi.c b/sound/soc/imx/imx-ssi.c index bc92ec6..c331d65 100644 --- a/sound/soc/imx/imx-ssi.c +++ b/sound/soc/imx/imx-ssi.c @@ -667,12 +667,6 @@ static int imx_ssi_probe(struct platform_device *pdev) if (res) ssi->dma_params_rx.dma = res->start; - if ((cpu_is_mx27() || cpu_is_mx21()) && - !(ssi->flags & IMX_SSI_USE_AC97) && - (ssi->flags & IMX_SSI_DMA)) { - ssi->flags |= IMX_SSI_DMA; - } - platform_set_drvdata(pdev, ssi); ret = snd_soc_register_dai(&pdev->dev, dai); diff --git a/sound/soc/mid-x86/sst_platform.c b/sound/soc/mid-x86/sst_platform.c index 9ebe027..d827edb 100644 --- a/sound/soc/mid-x86/sst_platform.c +++ b/sound/soc/mid-x86/sst_platform.c @@ -249,10 +249,13 @@ static int sst_platform_open(struct snd_pcm_substream *substream) return -ENOMEM; } stream->sstdrv_ops->vendor_id = MSIC_VENDOR_ID; + stream->sstdrv_ops->module_name = SST_CARD_NAMES; /* registering with SST driver to get access to SST APIs to use */ ret_val = register_sst_card(stream->sstdrv_ops); if (ret_val) { pr_err("sst: sst card registration failed\n"); + kfree(stream->sstdrv_ops); + kfree(stream); return ret_val; } runtime->private_data = stream; @@ -270,6 +273,7 @@ static int sst_platform_close(struct snd_pcm_substream *substream) str_id = stream->stream_info.str_id; if (str_id) ret_val = stream->sstdrv_ops->pcm_control->close(str_id); + unregister_sst_card(stream->sstdrv_ops); kfree(stream->sstdrv_ops); kfree(stream); return ret_val; diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c index 9027da4..28757fb 100644 --- a/sound/soc/pxa/corgi.c +++ b/sound/soc/pxa/corgi.c @@ -310,7 +310,7 @@ static struct snd_soc_dai_link corgi_dai = { .cpu_dai_name = "pxa2xx-i2s", .codec_dai_name = "wm8731-hifi", .platform_name = "pxa-pcm-audio", - .codec_name = "wm8731-codec.0-001b", + .codec_name = "wm8731.0-001b", .init = corgi_wm8731_init, .ops = &corgi_ops, }; diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c index a7d4999..da3ae43 100644 --- a/sound/soc/pxa/poodle.c +++ b/sound/soc/pxa/poodle.c @@ -276,7 +276,7 @@ static struct snd_soc_dai_link poodle_dai = { .cpu_dai_name = "pxa2xx-i2s", .codec_dai_name = "wm8731-hifi", .platform_name = "pxa-pcm-audio", - .codec_name = "wm8731-codec.0-001b", + .codec_name = "wm8731.0-001b", .init = poodle_wm8731_init, .ops = &poodle_ops, }; diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c index 8e15713..b253d86 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c @@ -42,6 +42,7 @@ static int spitz_jack_func; static int spitz_spk_func; +static int spitz_mic_gpio; static void spitz_ext_control(struct snd_soc_codec *codec) { @@ -217,14 +218,7 @@ static int spitz_set_spk(struct snd_kcontrol *kcontrol, static int spitz_mic_bias(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { - if (machine_is_borzoi() || machine_is_spitz()) - gpio_set_value(SPITZ_GPIO_MIC_BIAS, - SND_SOC_DAPM_EVENT_ON(event)); - - if (machine_is_akita()) - gpio_set_value(AKITA_GPIO_MIC_BIAS, - SND_SOC_DAPM_EVENT_ON(event)); - + gpio_set_value_cansleep(spitz_mic_gpio, SND_SOC_DAPM_EVENT_ON(event)); return 0; } @@ -339,22 +333,45 @@ static int __init spitz_init(void) if (!(machine_is_spitz() || machine_is_borzoi() || machine_is_akita())) return -ENODEV; + if (machine_is_borzoi() || machine_is_spitz()) + spitz_mic_gpio = SPITZ_GPIO_MIC_BIAS; + else + spitz_mic_gpio = AKITA_GPIO_MIC_BIAS; + + ret = gpio_request(spitz_mic_gpio, "MIC GPIO"); + if (ret) + goto err1; + + ret = gpio_direction_output(spitz_mic_gpio, 0); + if (ret) + goto err2; + spitz_snd_device = platform_device_alloc("soc-audio", -1); - if (!spitz_snd_device) - return -ENOMEM; + if (!spitz_snd_device) { + ret = -ENOMEM; + goto err2; + } platform_set_drvdata(spitz_snd_device, &snd_soc_spitz); - ret = platform_device_add(spitz_snd_device); + ret = platform_device_add(spitz_snd_device); if (ret) - platform_device_put(spitz_snd_device); + goto err3; + + return 0; +err3: + platform_device_put(spitz_snd_device); +err2: + gpio_free(spitz_mic_gpio); +err1: return ret; } static void __exit spitz_exit(void) { platform_device_unregister(spitz_snd_device); + gpio_free(spitz_mic_gpio); } module_init(spitz_init); diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index 5d76da4..f46a198 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -20,40 +20,38 @@ #include <trace/events/asoc.h> -static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec, - unsigned int reg) +#if defined(CONFIG_SPI_MASTER) +static int do_spi_write(void *control_data, const void *msg, + int len) { - int ret; - unsigned int val; + struct spi_device *spi = control_data; + struct spi_transfer t; + struct spi_message m; - if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg) || - codec->cache_bypass) { - if (codec->cache_only) - return -1; + if (len <= 0) + return 0; - BUG_ON(!codec->hw_read); - return codec->hw_read(codec, reg); - } + spi_message_init(&m); + memset(&t, 0, sizeof t); - ret = snd_soc_cache_read(codec, reg, &val); - if (ret < 0) - return -1; - return val; + t.tx_buf = msg; + t.len = len; + + spi_message_add_tail(&t, &m); + spi_sync(spi, &m); + + return len; } +#endif -static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) +static int do_hw_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value, const void *data, int len) { - u8 data[2]; int ret; - data[0] = (reg << 4) | ((value >> 8) & 0x000f); - data[1] = value & 0x00ff; - if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size && - !codec->cache_bypass) { + reg < codec->driver->reg_cache_size && + !codec->cache_bypass) { ret = snd_soc_cache_write(codec, reg, value); if (ret < 0) return -1; @@ -64,8 +62,8 @@ static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg, return 0; } - ret = codec->hw_write(codec->control_data, data, 2); - if (ret == 2) + ret = codec->hw_write(codec->control_data, data, len); + if (ret == len) return 0; if (ret < 0) return ret; @@ -73,31 +71,54 @@ static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg, return -EIO; } +static unsigned int do_hw_read(struct snd_soc_codec *codec, unsigned int reg) +{ + int ret; + unsigned int val; + + if (reg >= codec->driver->reg_cache_size || + snd_soc_codec_volatile_register(codec, reg) || + codec->cache_bypass) { + if (codec->cache_only) + return -1; + + BUG_ON(!codec->hw_read); + return codec->hw_read(codec, reg); + } + + ret = snd_soc_cache_read(codec, reg, &val); + if (ret < 0) + return -1; + return val; +} + +static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + return do_hw_read(codec, reg); +} + +static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u8 data[2]; + + data[0] = (reg << 4) | ((value >> 8) & 0x000f); + data[1] = value & 0x00ff; + + return do_hw_write(codec, reg, value, data, 2); +} + #if defined(CONFIG_SPI_MASTER) static int snd_soc_4_12_spi_write(void *control_data, const char *data, - int len) + int len) { - struct spi_device *spi = control_data; - struct spi_transfer t; - struct spi_message m; u8 msg[2]; - if (len <= 0) - return 0; - msg[0] = data[1]; msg[1] = data[0]; - spi_message_init(&m); - memset(&t, 0, sizeof t); - - t.tx_buf = &msg[0]; - t.len = len; - - spi_message_add_tail(&t, &m); - spi_sync(spi, &m); - - return len; + return do_spi_write(control_data, msg, len); } #else #define snd_soc_4_12_spi_write NULL @@ -106,81 +127,30 @@ static int snd_soc_4_12_spi_write(void *control_data, const char *data, static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec, unsigned int reg) { - int ret; - unsigned int val; - - if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg) || - codec->cache_bypass) { - if (codec->cache_only) - return -1; - - BUG_ON(!codec->hw_read); - return codec->hw_read(codec, reg); - } - - ret = snd_soc_cache_read(codec, reg, &val); - if (ret < 0) - return -1; - return val; + return do_hw_read(codec, reg); } static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { u8 data[2]; - int ret; data[0] = (reg << 1) | ((value >> 8) & 0x0001); data[1] = value & 0x00ff; - if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size && - !codec->cache_bypass) { - ret = snd_soc_cache_write(codec, reg, value); - if (ret < 0) - return -1; - } - - if (codec->cache_only) { - codec->cache_sync = 1; - return 0; - } - - ret = codec->hw_write(codec->control_data, data, 2); - if (ret == 2) - return 0; - if (ret < 0) - return ret; - else - return -EIO; + return do_hw_write(codec, reg, value, data, 2); } #if defined(CONFIG_SPI_MASTER) static int snd_soc_7_9_spi_write(void *control_data, const char *data, int len) { - struct spi_device *spi = control_data; - struct spi_transfer t; - struct spi_message m; u8 msg[2]; - if (len <= 0) - return 0; - msg[0] = data[0]; msg[1] = data[1]; - spi_message_init(&m); - memset(&t, 0, sizeof t); - - t.tx_buf = &msg[0]; - t.len = len; - - spi_message_add_tail(&t, &m); - spi_sync(spi, &m); - - return len; + return do_spi_write(control_data, msg, len); } #else #define snd_soc_7_9_spi_write NULL @@ -190,79 +160,30 @@ static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { u8 data[2]; - int ret; reg &= 0xff; data[0] = reg; data[1] = value & 0xff; - if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size && - !codec->cache_bypass) { - ret = snd_soc_cache_write(codec, reg, value); - if (ret < 0) - return -1; - } - - if (codec->cache_only) { - codec->cache_sync = 1; - return 0; - } - - if (codec->hw_write(codec->control_data, data, 2) == 2) - return 0; - else - return -EIO; + return do_hw_write(codec, reg, value, data, 2); } static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec, unsigned int reg) { - int ret; - unsigned int val; - - reg &= 0xff; - if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg) || - codec->cache_bypass) { - if (codec->cache_only) - return -1; - - BUG_ON(!codec->hw_read); - return codec->hw_read(codec, reg); - } - - ret = snd_soc_cache_read(codec, reg, &val); - if (ret < 0) - return -1; - return val; + return do_hw_read(codec, reg); } #if defined(CONFIG_SPI_MASTER) static int snd_soc_8_8_spi_write(void *control_data, const char *data, int len) { - struct spi_device *spi = control_data; - struct spi_transfer t; - struct spi_message m; u8 msg[2]; - if (len <= 0) - return 0; - msg[0] = data[0]; msg[1] = data[1]; - spi_message_init(&m); - memset(&t, 0, sizeof t); - - t.tx_buf = &msg[0]; - t.len = len; - - spi_message_add_tail(&t, &m); - spi_sync(spi, &m); - - return len; + return do_spi_write(control_data, msg, len); } #else #define snd_soc_8_8_spi_write NULL @@ -272,112 +193,78 @@ static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { u8 data[3]; - int ret; data[0] = reg; data[1] = (value >> 8) & 0xff; data[2] = value & 0xff; - if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size && - !codec->cache_bypass) { - ret = snd_soc_cache_write(codec, reg, value); - if (ret < 0) - return -1; - } - - if (codec->cache_only) { - codec->cache_sync = 1; - return 0; - } - - if (codec->hw_write(codec->control_data, data, 3) == 3) - return 0; - else - return -EIO; + return do_hw_write(codec, reg, value, data, 3); } static unsigned int snd_soc_8_16_read(struct snd_soc_codec *codec, unsigned int reg) { - int ret; - unsigned int val; - - if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg) || - codec->cache_bypass) { - if (codec->cache_only) - return -1; - - BUG_ON(!codec->hw_read); - return codec->hw_read(codec, reg); - } - - ret = snd_soc_cache_read(codec, reg, &val); - if (ret < 0) - return -1; - return val; + return do_hw_read(codec, reg); } #if defined(CONFIG_SPI_MASTER) static int snd_soc_8_16_spi_write(void *control_data, const char *data, - int len) + int len) { - struct spi_device *spi = control_data; - struct spi_transfer t; - struct spi_message m; u8 msg[3]; - if (len <= 0) - return 0; - msg[0] = data[0]; msg[1] = data[1]; msg[2] = data[2]; - spi_message_init(&m); - memset(&t, 0, sizeof t); - - t.tx_buf = &msg[0]; - t.len = len; - - spi_message_add_tail(&t, &m); - spi_sync(spi, &m); - - return len; + return do_spi_write(control_data, msg, len); } #else #define snd_soc_8_16_spi_write NULL #endif #if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) -static unsigned int snd_soc_8_8_read_i2c(struct snd_soc_codec *codec, - unsigned int r) +static unsigned int do_i2c_read(struct snd_soc_codec *codec, + void *reg, int reglen, + void *data, int datalen) { struct i2c_msg xfer[2]; - u8 reg = r; - u8 data; int ret; struct i2c_client *client = codec->control_data; /* Write register */ xfer[0].addr = client->addr; xfer[0].flags = 0; - xfer[0].len = 1; - xfer[0].buf = ® + xfer[0].len = reglen; + xfer[0].buf = reg; /* Read data */ xfer[1].addr = client->addr; xfer[1].flags = I2C_M_RD; - xfer[1].len = 1; - xfer[1].buf = &data; + xfer[1].len = datalen; + xfer[1].buf = data; ret = i2c_transfer(client->adapter, xfer, 2); - if (ret != 2) { - dev_err(&client->dev, "i2c_transfer() returned %d\n", ret); + if (ret == 2) return 0; - } + else if (ret < 0) + return ret; + else + return -EIO; +} +#endif + +#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) +static unsigned int snd_soc_8_8_read_i2c(struct snd_soc_codec *codec, + unsigned int r) +{ + u8 reg = r; + u8 data; + int ret; + ret = do_i2c_read(codec, ®, 1, &data, 1); + if (ret < 0) + return 0; return data; } #else @@ -388,30 +275,13 @@ static unsigned int snd_soc_8_8_read_i2c(struct snd_soc_codec *codec, static unsigned int snd_soc_8_16_read_i2c(struct snd_soc_codec *codec, unsigned int r) { - struct i2c_msg xfer[2]; u8 reg = r; u16 data; int ret; - struct i2c_client *client = codec->control_data; - /* Write register */ - xfer[0].addr = client->addr; - xfer[0].flags = 0; - xfer[0].len = 1; - xfer[0].buf = ® - - /* Read data */ - xfer[1].addr = client->addr; - xfer[1].flags = I2C_M_RD; - xfer[1].len = 2; - xfer[1].buf = (u8 *)&data; - - ret = i2c_transfer(client->adapter, xfer, 2); - if (ret != 2) { - dev_err(&client->dev, "i2c_transfer() returned %d\n", ret); + ret = do_i2c_read(codec, ®, 1, &data, 2); + if (ret < 0) return 0; - } - return (data >> 8) | ((data & 0xff) << 8); } #else @@ -422,30 +292,13 @@ static unsigned int snd_soc_8_16_read_i2c(struct snd_soc_codec *codec, static unsigned int snd_soc_16_8_read_i2c(struct snd_soc_codec *codec, unsigned int r) { - struct i2c_msg xfer[2]; u16 reg = r; u8 data; int ret; - struct i2c_client *client = codec->control_data; - - /* Write register */ - xfer[0].addr = client->addr; - xfer[0].flags = 0; - xfer[0].len = 2; - xfer[0].buf = (u8 *)® - - /* Read data */ - xfer[1].addr = client->addr; - xfer[1].flags = I2C_M_RD; - xfer[1].len = 1; - xfer[1].buf = &data; - ret = i2c_transfer(client->adapter, xfer, 2); - if (ret != 2) { - dev_err(&client->dev, "i2c_transfer() returned %d\n", ret); + ret = do_i2c_read(codec, ®, 2, &data, 1); + if (ret < 0) return 0; - } - return data; } #else @@ -453,87 +306,35 @@ static unsigned int snd_soc_16_8_read_i2c(struct snd_soc_codec *codec, #endif static unsigned int snd_soc_16_8_read(struct snd_soc_codec *codec, - unsigned int reg) + unsigned int reg) { - int ret; - unsigned int val; - - reg &= 0xff; - if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg) || - codec->cache_bypass) { - if (codec->cache_only) - return -1; - - BUG_ON(!codec->hw_read); - return codec->hw_read(codec, reg); - } - - ret = snd_soc_cache_read(codec, reg, &val); - if (ret < 0) - return -1; - return val; + return do_hw_read(codec, reg); } static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) + unsigned int value) { u8 data[3]; - int ret; data[0] = (reg >> 8) & 0xff; data[1] = reg & 0xff; data[2] = value; - reg &= 0xff; - if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size && - !codec->cache_bypass) { - ret = snd_soc_cache_write(codec, reg, value); - if (ret < 0) - return -1; - } - if (codec->cache_only) { - codec->cache_sync = 1; - return 0; - } - - ret = codec->hw_write(codec->control_data, data, 3); - if (ret == 3) - return 0; - if (ret < 0) - return ret; - else - return -EIO; + return do_hw_write(codec, reg, value, data, 3); } #if defined(CONFIG_SPI_MASTER) static int snd_soc_16_8_spi_write(void *control_data, const char *data, - int len) + int len) { - struct spi_device *spi = control_data; - struct spi_transfer t; - struct spi_message m; u8 msg[3]; - if (len <= 0) - return 0; - msg[0] = data[0]; msg[1] = data[1]; msg[2] = data[2]; - spi_message_init(&m); - memset(&t, 0, sizeof t); - - t.tx_buf = &msg[0]; - t.len = len; - - spi_message_add_tail(&t, &m); - spi_sync(spi, &m); - - return len; + return do_spi_write(control_data, msg, len); } #else #define snd_soc_16_8_spi_write NULL @@ -543,30 +344,13 @@ static int snd_soc_16_8_spi_write(void *control_data, const char *data, static unsigned int snd_soc_16_16_read_i2c(struct snd_soc_codec *codec, unsigned int r) { - struct i2c_msg xfer[2]; u16 reg = cpu_to_be16(r); u16 data; int ret; - struct i2c_client *client = codec->control_data; - - /* Write register */ - xfer[0].addr = client->addr; - xfer[0].flags = 0; - xfer[0].len = 2; - xfer[0].buf = (u8 *)® - /* Read data */ - xfer[1].addr = client->addr; - xfer[1].flags = I2C_M_RD; - xfer[1].len = 2; - xfer[1].buf = (u8 *)&data; - - ret = i2c_transfer(client->adapter, xfer, 2); - if (ret != 2) { - dev_err(&client->dev, "i2c_transfer() returned %d\n", ret); + ret = do_i2c_read(codec, ®, 2, &data, 2); + if (ret < 0) return 0; - } - return be16_to_cpu(data); } #else @@ -576,91 +360,82 @@ static unsigned int snd_soc_16_16_read_i2c(struct snd_soc_codec *codec, static unsigned int snd_soc_16_16_read(struct snd_soc_codec *codec, unsigned int reg) { - int ret; - unsigned int val; - - if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg) || - codec->cache_bypass) { - if (codec->cache_only) - return -1; - - BUG_ON(!codec->hw_read); - return codec->hw_read(codec, reg); - } - - ret = snd_soc_cache_read(codec, reg, &val); - if (ret < 0) - return -1; - - return val; + return do_hw_read(codec, reg); } static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { u8 data[4]; - int ret; data[0] = (reg >> 8) & 0xff; data[1] = reg & 0xff; data[2] = (value >> 8) & 0xff; data[3] = value & 0xff; - if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size && - !codec->cache_bypass) { - ret = snd_soc_cache_write(codec, reg, value); - if (ret < 0) - return -1; - } - - if (codec->cache_only) { - codec->cache_sync = 1; - return 0; - } - - ret = codec->hw_write(codec->control_data, data, 4); - if (ret == 4) - return 0; - if (ret < 0) - return ret; - else - return -EIO; + return do_hw_write(codec, reg, value, data, 4); } #if defined(CONFIG_SPI_MASTER) static int snd_soc_16_16_spi_write(void *control_data, const char *data, - int len) + int len) { - struct spi_device *spi = control_data; - struct spi_transfer t; - struct spi_message m; u8 msg[4]; - if (len <= 0) - return 0; - msg[0] = data[0]; msg[1] = data[1]; msg[2] = data[2]; msg[3] = data[3]; - spi_message_init(&m); - memset(&t, 0, sizeof t); - - t.tx_buf = &msg[0]; - t.len = len; - - spi_message_add_tail(&t, &m); - spi_sync(spi, &m); - - return len; + return do_spi_write(control_data, msg, len); } #else #define snd_soc_16_16_spi_write NULL #endif +/* Primitive bulk write support for soc-cache. The data pointed to by + * `data' needs to already be in the form the hardware expects + * including any leading register specific data. Any data written + * through this function will not go through the cache as it only + * handles writing to volatile or out of bounds registers. + */ +static int snd_soc_hw_bulk_write_raw(struct snd_soc_codec *codec, unsigned int reg, + const void *data, size_t len) +{ + int ret; + + /* Ensure that the base register is volatile. Subsequently + * any other register that is touched by this routine should be + * volatile as well to ensure that we don't get out of sync with + * the cache. + */ + if (!snd_soc_codec_volatile_register(codec, reg) + && reg < codec->driver->reg_cache_size) + return -EINVAL; + + switch (codec->control_type) { +#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) + case SND_SOC_I2C: + ret = i2c_master_send(codec->control_data, data, len); + break; +#endif +#if defined(CONFIG_SPI_MASTER) + case SND_SOC_SPI: + ret = do_spi_write(codec->control_data, data, len); + break; +#endif + default: + BUG(); + } + + if (ret == len) + return 0; + if (ret < 0) + return ret; + else + return -EIO; +} + static struct { int addr_bits; int data_bits; @@ -744,6 +519,7 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, codec->write = io_types[i].write; codec->read = io_types[i].read; + codec->bulk_write_raw = snd_soc_hw_bulk_write_raw; switch (control) { case SND_SOC_CUSTOM: @@ -889,6 +665,8 @@ static int snd_soc_rbtree_cache_sync(struct snd_soc_codec *codec) rbnode = rb_entry(node, struct snd_soc_rbtree_node, node); if (rbnode->value == rbnode->defval) continue; + WARN_ON(codec->writable_register && + codec->writable_register(codec, rbnode->reg)); ret = snd_soc_cache_read(codec, rbnode->reg, &val); if (ret) return ret; @@ -1149,6 +927,8 @@ static int snd_soc_lzo_cache_sync(struct snd_soc_codec *codec) lzo_blocks = codec->reg_cache; for_each_set_bit(i, lzo_blocks[0]->sync_bmp, lzo_blocks[0]->sync_bmp_nbits) { + WARN_ON(codec->writable_register && + codec->writable_register(codec, i)); ret = snd_soc_cache_read(codec, i, &val); if (ret) return ret; @@ -1407,6 +1187,8 @@ static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec) codec_drv = codec->driver; for (i = 0; i < codec_drv->reg_cache_size; ++i) { + WARN_ON(codec->writable_register && + codec->writable_register(codec, i)); ret = snd_soc_cache_read(codec, i, &val); if (ret) return ret; @@ -1523,7 +1305,7 @@ int snd_soc_cache_init(struct snd_soc_codec *codec) codec->cache_ops->name, codec->name); return codec->cache_ops->init(codec); } - return -EINVAL; + return -ENOSYS; } /* @@ -1538,7 +1320,7 @@ int snd_soc_cache_exit(struct snd_soc_codec *codec) codec->cache_ops->name, codec->name); return codec->cache_ops->exit(codec); } - return -EINVAL; + return -ENOSYS; } /** @@ -1562,7 +1344,7 @@ int snd_soc_cache_read(struct snd_soc_codec *codec, } mutex_unlock(&codec->cache_rw_mutex); - return -EINVAL; + return -ENOSYS; } EXPORT_SYMBOL_GPL(snd_soc_cache_read); @@ -1587,7 +1369,7 @@ int snd_soc_cache_write(struct snd_soc_codec *codec, } mutex_unlock(&codec->cache_rw_mutex); - return -EINVAL; + return -ENOSYS; } EXPORT_SYMBOL_GPL(snd_soc_cache_write); @@ -1610,7 +1392,7 @@ int snd_soc_cache_sync(struct snd_soc_codec *codec) } if (!codec->cache_ops || !codec->cache_ops->sync) - return -EINVAL; + return -ENOSYS; if (codec->cache_ops->name) name = codec->cache_ops->name; @@ -1677,3 +1459,17 @@ int snd_soc_default_readable_register(struct snd_soc_codec *codec, return codec->driver->reg_access_default[index].read; } EXPORT_SYMBOL_GPL(snd_soc_default_readable_register); + +int snd_soc_default_writable_register(struct snd_soc_codec *codec, + unsigned int reg) +{ + int index; + + if (reg >= codec->driver->reg_cache_size) + return 1; + index = snd_soc_get_reg_access_index(codec, reg); + if (index < 0) + return 0; + return codec->driver->reg_access_default[index].write; +} +EXPORT_SYMBOL_GPL(snd_soc_default_writable_register); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index d8562ce..f75f139 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1493,6 +1493,9 @@ static int soc_probe_codec(struct snd_soc_card *card, } } + if (driver->controls) + snd_soc_add_controls(codec, driver->controls, + driver->num_controls); if (driver->dapm_widgets) snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets, driver->num_dapm_widgets); @@ -1890,6 +1893,14 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) } } + /* We should have a non-codec control add function but we don't */ + if (card->controls) + snd_soc_add_controls(list_first_entry(&card->codec_dev_list, + struct snd_soc_codec, + card_list), + card->controls, + card->num_controls); + if (card->dapm_widgets) snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets, card->num_dapm_widgets); @@ -2150,6 +2161,42 @@ int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, EXPORT_SYMBOL_GPL(snd_soc_codec_volatile_register); /** + * snd_soc_codec_readable_register: Report if a register is readable. + * + * @codec: CODEC to query. + * @reg: Register to query. + * + * Boolean function indicating if a CODEC register is readable. + */ +int snd_soc_codec_readable_register(struct snd_soc_codec *codec, + unsigned int reg) +{ + if (codec->readable_register) + return codec->readable_register(codec, reg); + else + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_codec_readable_register); + +/** + * snd_soc_codec_writable_register: Report if a register is writable. + * + * @codec: CODEC to query. + * @reg: Register to query. + * + * Boolean function indicating if a CODEC register is writable. + */ +int snd_soc_codec_writable_register(struct snd_soc_codec *codec, + unsigned int reg) +{ + if (codec->writable_register) + return codec->writable_register(codec, reg); + else + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_codec_writable_register); + +/** * snd_soc_new_ac97_codec - initailise AC97 device * @codec: audio codec * @ops: AC97 bus operations @@ -2231,6 +2278,13 @@ unsigned int snd_soc_write(struct snd_soc_codec *codec, } EXPORT_SYMBOL_GPL(snd_soc_write); +unsigned int snd_soc_bulk_write_raw(struct snd_soc_codec *codec, + unsigned int reg, const void *data, size_t len) +{ + return codec->bulk_write_raw(codec, reg, data, len); +} +EXPORT_SYMBOL_GPL(snd_soc_bulk_write_raw); + /** * snd_soc_update_bits - update codec register bits * @codec: audio codec @@ -3669,6 +3723,7 @@ int snd_soc_register_codec(struct device *dev, codec->read = codec_drv->read; codec->volatile_register = codec_drv->volatile_register; codec->readable_register = codec_drv->readable_register; + codec->writable_register = codec_drv->writable_register; codec->dapm.bias_level = SND_SOC_BIAS_OFF; codec->dapm.dev = dev; codec->dapm.codec = codec; @@ -3703,6 +3758,8 @@ int snd_soc_register_codec(struct device *dev, codec->volatile_register = snd_soc_default_volatile_register; if (!codec->readable_register) codec->readable_register = snd_soc_default_readable_register; + if (!codec->writable_register) + codec->writable_register = snd_soc_default_writable_register; } for (i = 0; i < num_dai; i++) { diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 81c4052..2ee738c 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -322,45 +322,6 @@ static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm, return -ENODEV; } -/* update dapm codec register bits */ -static int dapm_update_bits(struct snd_soc_dapm_widget *widget) -{ - int change, power; - unsigned int old, new; - struct snd_soc_codec *codec = widget->codec; - struct snd_soc_dapm_context *dapm = widget->dapm; - struct snd_soc_card *card = dapm->card; - - /* check for valid widgets */ - if (widget->reg < 0 || widget->id == snd_soc_dapm_input || - widget->id == snd_soc_dapm_output || - widget->id == snd_soc_dapm_hp || - widget->id == snd_soc_dapm_mic || - widget->id == snd_soc_dapm_line || - widget->id == snd_soc_dapm_spk) - return 0; - - power = widget->power; - if (widget->invert) - power = (power ? 0:1); - - old = snd_soc_read(codec, widget->reg); - new = (old & ~(0x1 << widget->shift)) | (power << widget->shift); - - change = old != new; - if (change) { - pop_dbg(dapm->dev, card->pop_time, - "pop test %s : %s in %d ms\n", - widget->name, widget->power ? "on" : "off", - card->pop_time); - pop_wait(card->pop_time); - snd_soc_write(codec, widget->reg, new); - } - dev_dbg(dapm->dev, "reg %x old %x new %x change %d\n", widget->reg, - old, new, change); - return change; -} - /* create new dapm mixer control */ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_widget *w) @@ -644,57 +605,6 @@ int dapm_reg_event(struct snd_soc_dapm_widget *w, } EXPORT_SYMBOL_GPL(dapm_reg_event); -/* Standard power change method, used to apply power changes to most - * widgets. - */ -static int dapm_generic_apply_power(struct snd_soc_dapm_widget *w) -{ - int ret; - - /* call any power change event handlers */ - if (w->event) - dev_dbg(w->dapm->dev, "power %s event for %s flags %x\n", - w->power ? "on" : "off", - w->name, w->event_flags); - - /* power up pre event */ - if (w->power && w->event && - (w->event_flags & SND_SOC_DAPM_PRE_PMU)) { - ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU); - if (ret < 0) - return ret; - } - - /* power down pre event */ - if (!w->power && w->event && - (w->event_flags & SND_SOC_DAPM_PRE_PMD)) { - ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD); - if (ret < 0) - return ret; - } - - dapm_update_bits(w); - - /* power up post event */ - if (w->power && w->event && - (w->event_flags & SND_SOC_DAPM_POST_PMU)) { - ret = w->event(w, - NULL, SND_SOC_DAPM_POST_PMU); - if (ret < 0) - return ret; - } - - /* power down post event */ - if (!w->power && w->event && - (w->event_flags & SND_SOC_DAPM_POST_PMD)) { - ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD); - if (ret < 0) - return ret; - } - - return 0; -} - /* Generic check to see if a widget should be powered. */ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w) @@ -981,16 +891,6 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, NULL, SND_SOC_DAPM_POST_PMD); break; - case snd_soc_dapm_input: - case snd_soc_dapm_output: - case snd_soc_dapm_hp: - case snd_soc_dapm_mic: - case snd_soc_dapm_line: - case snd_soc_dapm_spk: - /* No register support currently */ - ret = dapm_generic_apply_power(w); - break; - default: /* Queue it up for application */ cur_sort = sort[w->id]; @@ -1201,6 +1101,15 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) } } + /* Force all contexts in the card to the same bias state */ + power = 0; + list_for_each_entry(d, &card->dapm_list, list) + if (d->dev_power) + power = 1; + list_for_each_entry(d, &card->dapm_list, list) + d->dev_power = power; + + /* Run all the bias changes in parallel */ list_for_each_entry(d, &dapm->card->dapm_list, list) async_schedule_domain(dapm_pre_sequence_async, d, @@ -1304,6 +1213,47 @@ static const struct file_operations dapm_widget_power_fops = { .llseek = default_llseek, }; +static int dapm_bias_open_file(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t dapm_bias_read_file(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct snd_soc_dapm_context *dapm = file->private_data; + char *level; + + switch (dapm->bias_level) { + case SND_SOC_BIAS_ON: + level = "On\n"; + break; + case SND_SOC_BIAS_PREPARE: + level = "Prepare\n"; + break; + case SND_SOC_BIAS_STANDBY: + level = "Standby\n"; + break; + case SND_SOC_BIAS_OFF: + level = "Off\n"; + break; + default: + BUG(); + level = "Unknown\n"; + break; + } + + return simple_read_from_buffer(user_buf, count, ppos, level, + strlen(level)); +} + +static const struct file_operations dapm_bias_fops = { + .open = dapm_bias_open_file, + .read = dapm_bias_read_file, + .llseek = default_llseek, +}; + void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm) { struct snd_soc_dapm_widget *w; @@ -1312,6 +1262,13 @@ void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm) if (!dapm->debugfs_dapm) return; + d = debugfs_create_file("bias_level", 0444, + dapm->debugfs_dapm, dapm, + &dapm_bias_fops); + if (!d) + dev_warn(dapm->dev, + "ASoC: Failed to create bias level debugfs file\n"); + list_for_each_entry(w, &dapm->card->widgets, list) { if (!w->name || w->dapm != dapm) continue; @@ -1520,6 +1477,19 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, } } + /* Try again in other contexts */ + list_for_each_entry(w, &dapm->card->widgets, list) { + if (!strcmp(w->name, pin)) { + dev_dbg(w->dapm->dev, "dapm: pin %s = %d\n", + pin, status); + w->connected = status; + /* Allow disabling of forced pins */ + if (status == 0) + w->force = 0; + return 0; + } + } + dev_err(dapm->dev, "dapm: unknown pin %s\n", pin); return -EINVAL; } @@ -2360,6 +2330,17 @@ int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, } } + /* Try again with other contexts */ + list_for_each_entry(w, &dapm->card->widgets, list) { + if (!strcmp(w->name, pin)) { + dev_dbg(w->dapm->dev, + "dapm: force enable pin %s\n", pin); + w->connected = 1; + w->force = 1; + return 0; + } + } + dev_err(dapm->dev, "dapm: unknown pin %s\n", pin); return -EINVAL; } diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index fcab80b..6203a72 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -325,7 +325,7 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, gpio_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - jack->codec->dev->driver->name, + gpios[i].name, &gpios[i]); if (ret) goto err; |