From: Mark Brown Date: Wed, 6 Apr 2011 14:15:15 +0000 (+0900) Subject: Merge branch 'for-2.6.39' into for-2.6.40 X-Git-Tag: v3.0-rc1~347^2~1^2~106 X-Git-Url: https://git.openpandora.org/cgi-bin/gitweb.cgi?p=pandora-kernel.git;a=commitdiff_plain;h=fa880004682cf0d10e7a7c71dc8d56bbd67ac3d5;hp=deb2607e6c3d75c7185bb8aba658d9cd57e6e54a Merge branch 'for-2.6.39' into for-2.6.40 --- diff --git a/include/linux/mfd/wm8994/pdata.h b/include/linux/mfd/wm8994/pdata.h index 466b1c777aff..d12f8d635a81 100644 --- a/include/linux/mfd/wm8994/pdata.h +++ b/include/linux/mfd/wm8994/pdata.h @@ -32,6 +32,10 @@ struct wm8994_ldo_pdata { #define WM8994_EQ_REGS 20 #define WM8958_MBC_CUTOFF_REGS 20 #define WM8958_MBC_COEFF_REGS 48 +#define WM8958_MBC_COMBINED_REGS 56 +#define WM8958_VSS_HPF_REGS 2 +#define WM8958_VSS_REGS 148 +#define WM8958_ENH_EQ_REGS 32 /** * DRC configurations are specified with a label and a set of register @@ -71,6 +75,42 @@ struct wm8958_mbc_cfg { const char *name; u16 cutoff_regs[WM8958_MBC_CUTOFF_REGS]; u16 coeff_regs[WM8958_MBC_COEFF_REGS]; + + /* Coefficient layout when using MBC+VSS firmware */ + u16 combined_regs[WM8958_MBC_COMBINED_REGS]; +}; + +/** + * VSS HPF configurations are specified with a label and two values to + * write. Configurations are expected to be generated using the + * multiband compressor configuration panel in WISCE - see + * http://www.wolfsonmicro.com/wisce/ + */ +struct wm8958_vss_hpf_cfg { + const char *name; + u16 regs[WM8958_VSS_HPF_REGS]; +}; + +/** + * VSS configurations are specified with a label and array of values + * to write. Configurations are expected to be generated using the + * multiband compressor configuration panel in WISCE - see + * http://www.wolfsonmicro.com/wisce/ + */ +struct wm8958_vss_cfg { + const char *name; + u16 regs[WM8958_VSS_REGS]; +}; + +/** + * Enhanced EQ configurations are specified with a label and array of + * values to write. Configurations are expected to be generated using + * the multiband compressor configuration panel in WISCE - see + * http://www.wolfsonmicro.com/wisce/ + */ +struct wm8958_enh_eq_cfg { + const char *name; + u16 regs[WM8958_ENH_EQ_REGS]; }; struct wm8994_pdata { @@ -95,6 +135,15 @@ struct wm8994_pdata { int num_mbc_cfgs; struct wm8958_mbc_cfg *mbc_cfgs; + int num_vss_cfgs; + struct wm8958_vss_cfg *vss_cfgs; + + int num_vss_hpf_cfgs; + struct wm8958_vss_hpf_cfg *vss_hpf_cfgs; + + int num_enh_eq_cfgs; + struct wm8958_enh_eq_cfg *enh_eq_cfgs; + /* LINEOUT can be differential or single ended */ unsigned int lineout1_diff:1; unsigned int lineout2_diff:1; diff --git a/include/sound/soc.h b/include/sound/soc.h index bfa4836ea107..2720a9f3780b 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -248,7 +248,7 @@ typedef int (*hw_write_t)(void *,const char* ,int); extern struct snd_ac97_bus_ops soc_ac97_ops; enum snd_soc_control_type { - SND_SOC_CUSTOM, + SND_SOC_CUSTOM = 1, SND_SOC_I2C, SND_SOC_SPI, }; @@ -278,6 +278,10 @@ int snd_soc_register_codec(struct device *dev, void snd_soc_unregister_codec(struct device *dev); int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, unsigned int reg); +int snd_soc_codec_readable_register(struct snd_soc_codec *codec, + unsigned int reg); +int snd_soc_codec_writable_register(struct snd_soc_codec *codec, + unsigned int reg); int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, int addr_bits, int data_bits, enum snd_soc_control_type control); @@ -292,6 +296,8 @@ int snd_soc_default_volatile_register(struct snd_soc_codec *codec, unsigned int reg); int snd_soc_default_readable_register(struct snd_soc_codec *codec, unsigned int reg); +int snd_soc_default_writable_register(struct snd_soc_codec *codec, + unsigned int reg); /* Utility functions to get clock rates from various things */ int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots); @@ -523,6 +529,7 @@ struct snd_soc_codec { size_t reg_size; /* reg_cache_size * reg_word_size */ int (*volatile_register)(struct snd_soc_codec *, unsigned int); int (*readable_register)(struct snd_soc_codec *, unsigned int); + int (*writable_register)(struct snd_soc_codec *, unsigned int); /* runtime */ struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */ @@ -539,10 +546,12 @@ struct snd_soc_codec { /* codec IO */ void *control_data; /* codec control (i2c/3wire) data */ + enum snd_soc_control_type control_type; hw_write_t hw_write; unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int); unsigned int (*read)(struct snd_soc_codec *, unsigned int); int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); + int (*bulk_write_raw)(struct snd_soc_codec *, unsigned int, const void *, size_t); void *reg_cache; const void *reg_def_copy; const struct snd_soc_cache_ops *cache_ops; @@ -587,6 +596,7 @@ struct snd_soc_codec_driver { size_t, unsigned int); int (*volatile_register)(struct snd_soc_codec *, unsigned int); int (*readable_register)(struct snd_soc_codec *, unsigned int); + int (*writable_register)(struct snd_soc_codec *, unsigned int); short reg_cache_size; short reg_cache_step; short reg_word_size; @@ -814,6 +824,8 @@ struct soc_enum { unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg); unsigned int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val); +unsigned int snd_soc_bulk_write_raw(struct snd_soc_codec *codec, + unsigned int reg, const void *data, size_t len); /* device driver data */ diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 0ef0035fe99f..12c6f4508c54 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 af3c73053ee4..28afbbf69ce0 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 cb99f04abe88..1d3e258c9ea8 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 5a2fd8abaefa..98b44b316e78 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 ffbac26b9bce..6d2162590889 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 83012da9dfc2..ea4951cf5526 100644 --- a/sound/soc/blackfin/bf5xx-ad1836.c +++ b/sound/soc/blackfin/bf5xx-ad1836.c @@ -29,22 +29,12 @@ #include #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 d3ccb926b5e4..d6651c033cb7 100644 --- a/sound/soc/blackfin/bf5xx-ad193x.c +++ b/sound/soc/blackfin/bf5xx-ad193x.c @@ -38,30 +38,28 @@ #include #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 d57c9c9c9883..06a84b211b52 100644 --- a/sound/soc/blackfin/bf5xx-ad1980.c +++ b/sound/soc/blackfin/bf5xx-ad1980.c @@ -47,39 +47,34 @@ #include #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 732fb8bad076..732a247f2527 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 890a0dccf902..b5101efd1c87 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 d453b1e9d607..00cc3e00b2fe 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 99051ff0954e..a2d40349fcc4 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 a86e8cc0b2d3..5ab60bd613ea 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 * @@ -33,15 +33,18 @@ #include #include #include +#include #include #include #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 ad28663f5bbd..767e772a815d 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 74cf759b78a6..07cfc7a9e49a 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 5515ac9e05c7..a822d1ee1380 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 6943e24a74a1..b814ed0a1636 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 diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 379bc55f0723..49121ad0e172 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -69,7 +69,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 diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c index da46479bfcfa..2374ca5ffe68 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 34cb51ef2156..923b364a3e41 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 de799cd1ba72..8d793e993e9a 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 8b38739c88f8..e1a214ee757f 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 2ec75abfa3e9..88b29f8c748b 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 0bb424af956f..d68ea532cc7f 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 bd0517cb7980..bb58bdb49853 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/sn95031.c b/sound/soc/codecs/sn95031.c index 2a30eae1881c..569555f4a92f 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 2727befd158e..8a2b52fa4a7e 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -48,7 +48,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 +64,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 +229,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 +244,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 +261,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 +310,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 +324,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 +335,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 +416,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 +489,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 +503,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,8 +550,6 @@ 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), @@ -625,7 +574,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 +599,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, diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c index 54a30ef0ec8b..33bb52f3f683 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 00b6d87e7bdb..eb1a0b4e09b6 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 482fcdb59bfa..255901c4460d 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 97c30382d3ff..a537e4af6ae7 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 736b0352d0a7..86d4718d3a76 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 0a67c31b2663..6dec7cee2cb4 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 ae1cadfae84c..a739e09a8309 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -926,7 +926,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" }, @@ -1079,17 +1079,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) { @@ -2020,7 +2009,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); @@ -2046,6 +2034,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 000000000000..74983ee2b87f --- /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 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 3dc64c8b6a5c..03ef62462bab 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 @@ -3327,14 +3043,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; } @@ -3358,10 +3083,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; } @@ -3404,6 +3138,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 999b8851226b..0a1db04b73bd 100644 --- a/sound/soc/codecs/wm8994.h +++ b/sound/soc/codecs/wm8994.h @@ -10,6 +10,9 @@ #define _WM8994_H #include +#include + +#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 47b357adabdd..646b58dda849 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 bf5d4ef1a2a6..90117f8156e8 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 38ed98558718..7167cb6787db 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 bc92ec620004..c331d65587d8 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/pxa/corgi.c b/sound/soc/pxa/corgi.c index 9027da466cae..28757fb9df31 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 a7d4999f9b24..da3ae4316cf2 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 8e1571350630..b253d864868a 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 5d76da43b14c..f46a198a48c0 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -20,40 +20,38 @@ #include -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 960718b3beb8..1c7e82a4c808 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2147,6 +2147,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 @@ -2229,6 +2265,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 @@ -3667,6 +3710,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; @@ -3701,6 +3745,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 81c4052c127c..05da8a8f0aef 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1304,6 +1304,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 +1353,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; diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index fcab80b36a37..6203a72d57af 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;