diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/pci/Kconfig | 4 | ||||
-rw-r--r-- | sound/pci/hda/patch_realtek.c | 2 | ||||
-rw-r--r-- | sound/pci/hda/patch_sigmatel.c | 54 | ||||
-rw-r--r-- | sound/pci/oxygen/virtuoso.c | 3 | ||||
-rw-r--r-- | sound/soc/atmel/atmel-pcm.c | 2 | ||||
-rw-r--r-- | sound/soc/fsl/mpc8610_hpcd.c | 18 | ||||
-rw-r--r-- | sound/soc/s3c24xx/Kconfig | 19 | ||||
-rw-r--r-- | sound/soc/s3c24xx/Makefile | 6 | ||||
-rw-r--r-- | sound/soc/s3c24xx/jive_wm8750.c | 216 | ||||
-rw-r--r-- | sound/soc/s3c24xx/neo1973_wm8753.c | 6 | ||||
-rw-r--r-- | sound/soc/s3c24xx/s3c-i2s-v2.c | 626 | ||||
-rw-r--r-- | sound/soc/s3c24xx/s3c-i2s-v2.h | 90 | ||||
-rw-r--r-- | sound/soc/s3c24xx/s3c2412-i2s.c | 604 | ||||
-rw-r--r-- | sound/soc/s3c24xx/s3c2412-i2s.h | 17 | ||||
-rw-r--r-- | sound/soc/s3c24xx/s3c2443-ac97.c | 2 | ||||
-rw-r--r-- | sound/soc/s3c24xx/s3c24xx-i2s.c | 6 | ||||
-rw-r--r-- | sound/soc/s3c24xx/s3c24xx-pcm.c | 25 | ||||
-rw-r--r-- | sound/soc/s3c24xx/s3c24xx_uda134x.c | 2 | ||||
-rw-r--r-- | sound/soc/s3c24xx/s3c64xx-i2s.c | 185 | ||||
-rw-r--r-- | sound/soc/s3c24xx/s3c64xx-i2s.h | 28 |
20 files changed, 1258 insertions, 657 deletions
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 6e3a1848447..82b9bddcdcd 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -744,8 +744,8 @@ config SND_VIRTUOSO select SND_OXYGEN_LIB help Say Y here to include support for sound cards based on the - Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X and - HDAV1.3 (Deluxe). + Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2 and D2X. + Support for the HDAV1.3 (Deluxe) is very experimental. To compile this driver as a module, choose M here: the module will be called snd-virtuoso. diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 82dd0843197..5d249a547fb 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -8478,6 +8478,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_ASUS_EEE1601), SND_PCI_QUIRK(0x105b, 0x0ce8, "Foxconn P35AX-S", ALC883_6ST_DIG), SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1071, 0x8227, "Mitac 82801H", ALC883_MITAC), SND_PCI_QUIRK(0x1071, 0x8253, "Mitac 8252d", ALC883_MITAC), SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD), SND_PCI_QUIRK(0x10f1, 0x2350, "TYAN-S2350", ALC888_6ST_DELL), @@ -8526,6 +8527,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { SND_PCI_QUIRK(0x1991, 0x5625, "Haier W66", ALC883_HAIER_W66), SND_PCI_QUIRK(0x8086, 0x0001, "DG33BUC", ALC883_3ST_6ch_INTEL), SND_PCI_QUIRK(0x8086, 0x0002, "DG33FBC", ALC883_3ST_6ch_INTEL), + SND_PCI_QUIRK(0x8086, 0x2503, "82801H", ALC883_MITAC), SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC883_3ST_6ch_INTEL), SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch), {} diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index c39deebb588..3dd4eee70b7 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -81,6 +81,7 @@ enum { enum { STAC_92HD83XXX_REF, + STAC_92HD83XXX_PWR_REF, STAC_92HD83XXX_MODELS }; @@ -334,7 +335,7 @@ static hda_nid_t stac92hd83xxx_slave_dig_outs[2] = { }; static unsigned int stac92hd83xxx_pwr_mapping[4] = { - 0x03, 0x0c, 0x10, 0x40, + 0x03, 0x0c, 0x20, 0x40, }; static hda_nid_t stac92hd83xxx_amp_nids[1] = { @@ -841,10 +842,6 @@ static struct hda_verb stac92hd73xx_10ch_core_init[] = { }; static struct hda_verb stac92hd83xxx_core_init[] = { - /* start of config #1 */ - { 0xe, AC_VERB_SET_CONNECT_SEL, 0x3}, - - /* start of config #2 */ { 0xa, AC_VERB_SET_CONNECT_SEL, 0x0}, { 0xb, AC_VERB_SET_CONNECT_SEL, 0x0}, { 0xd, AC_VERB_SET_CONNECT_SEL, 0x1}, @@ -885,8 +882,8 @@ static struct hda_verb stac92hd71bxx_analog_core_init[] = { static struct hda_verb stac925x_core_init[] = { /* set dac0mux for dac converter */ { 0x06, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* unmute and set max the selector */ - { 0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f }, + /* mute the master volume */ + { 0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, {} }; @@ -1138,6 +1135,8 @@ static struct snd_kcontrol_new stac92hd71bxx_mixer[] = { }; static struct snd_kcontrol_new stac925x_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x0e, 0, HDA_OUTPUT), + HDA_CODEC_MUTE("Master Playback Switch", 0x0e, 0, HDA_OUTPUT), STAC_INPUT_SOURCE(1), HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_OUTPUT), HDA_CODEC_MUTE("Capture Switch", 0x14, 0, HDA_OUTPUT), @@ -1736,10 +1735,12 @@ static unsigned int ref92hd83xxx_pin_configs[14] = { static unsigned int *stac92hd83xxx_brd_tbl[STAC_92HD83XXX_MODELS] = { [STAC_92HD83XXX_REF] = ref92hd83xxx_pin_configs, + [STAC_92HD83XXX_PWR_REF] = ref92hd83xxx_pin_configs, }; static const char *stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = { [STAC_92HD83XXX_REF] = "ref", + [STAC_92HD83XXX_PWR_REF] = "mic-ref", }; static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = { @@ -1799,6 +1800,8 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = { "HP dv5", STAC_HP_M4), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30f4, "HP dv7", STAC_HP_M4), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30f7, + "HP dv4", STAC_HP_DV5), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30fc, "HP dv7", STAC_HP_M4), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3603, @@ -3573,13 +3576,12 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out err = stac92xx_auto_fill_dac_nids(codec); if (err < 0) return err; + err = stac92xx_auto_create_multi_out_ctls(codec, + &spec->autocfg); + if (err < 0) + return err; } - err = stac92xx_auto_create_multi_out_ctls(codec, &spec->autocfg); - - if (err < 0) - return err; - /* setup analog beep controls */ if (spec->anabeep_nid > 0) { err = stac92xx_auto_create_beep_ctls(codec, @@ -4753,7 +4755,9 @@ static struct hda_input_mux stac92hd83xxx_dmux = { static int patch_stac92hd83xxx(struct hda_codec *codec) { struct sigmatel_spec *spec; + hda_nid_t conn[STAC92HD83_DAC_COUNT + 1]; int err; + int num_dacs; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) @@ -4772,15 +4776,16 @@ static int patch_stac92hd83xxx(struct hda_codec *codec) spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids); spec->multiout.dac_nids = spec->dac_nids; - spec->init = stac92hd83xxx_core_init; - switch (codec->vendor_id) { - case 0x111d7605: - break; - default: - spec->num_pwrs--; - spec->init++; /* switch to config #2 */ - } + /* set port 0xe to select the last DAC + */ + num_dacs = snd_hda_get_connections(codec, 0x0e, + conn, STAC92HD83_DAC_COUNT + 1) - 1; + + snd_hda_codec_write_cache(codec, 0xe, 0, + AC_VERB_SET_CONNECT_SEL, num_dacs); + + spec->init = stac92hd83xxx_core_init; spec->mixer = stac92hd83xxx_mixer; spec->num_pins = ARRAY_SIZE(stac92hd83xxx_pin_nids); spec->num_dmuxes = ARRAY_SIZE(stac92hd83xxx_dmux_nids); @@ -4806,6 +4811,15 @@ again: return err; } + switch (codec->vendor_id) { + case 0x111d7604: + case 0x111d7605: + if (spec->board_config == STAC_92HD83XXX_PWR_REF) + break; + spec->num_pwrs = 0; + break; + } + err = stac92xx_parse_auto_config(codec, 0x1d, 0); if (!err) { if (spec->board_config < 0) { diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c index e9e829e83d7..18c7c91786b 100644 --- a/sound/pci/oxygen/virtuoso.c +++ b/sound/pci/oxygen/virtuoso.c @@ -683,7 +683,7 @@ static void xonar_hdav_uart_input(struct oxygen *chip) if (chip->uart_input_count >= 2 && chip->uart_input[chip->uart_input_count - 2] == 'O' && chip->uart_input[chip->uart_input_count - 1] == 'K') { - printk(KERN_DEBUG "message from Xonar HDAV HDMI chip received:"); + printk(KERN_DEBUG "message from Xonar HDAV HDMI chip received:\n"); print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, chip->uart_input, chip->uart_input_count); chip->uart_input_count = 0; @@ -908,6 +908,7 @@ static const struct oxygen_model model_xonar_hdav = { .dac_channels = 8, .dac_volume_min = 0x0f, .dac_volume_max = 0xff, + .misc_flags = OXYGEN_MISC_MIDI, .function_flags = OXYGEN_FUNCTION_2WIRE, .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, diff --git a/sound/soc/atmel/atmel-pcm.c b/sound/soc/atmel/atmel-pcm.c index 1fac5efd285..3dcdc4e3cfa 100644 --- a/sound/soc/atmel/atmel-pcm.c +++ b/sound/soc/atmel/atmel-pcm.c @@ -44,8 +44,6 @@ #include <sound/pcm_params.h> #include <sound/soc.h> -#include <mach/hardware.h> - #include "atmel-pcm.h" diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c index bcec3f60bad..acf39a646b2 100644 --- a/sound/soc/fsl/mpc8610_hpcd.c +++ b/sound/soc/fsl/mpc8610_hpcd.c @@ -183,16 +183,6 @@ static struct snd_soc_ops mpc8610_hpcd_ops = { }; /** - * mpc8610_hpcd_machine: ASoC machine data - */ -static struct snd_soc_card mpc8610_hpcd_machine = { - .probe = mpc8610_hpcd_machine_probe, - .remove = mpc8610_hpcd_machine_remove, - .name = "MPC8610 HPCD", - .num_links = 1, -}; - -/** * mpc8610_hpcd_probe: OF probe function for the fabric driver * * This function gets called when an SSI node is found in the device tree. @@ -455,7 +445,11 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev, machine_data->dai.codec_dai = &cs4270_dai; /* The codec_dai we want */ machine_data->dai.ops = &mpc8610_hpcd_ops; - mpc8610_hpcd_machine.dai_link = &machine_data->dai; + machine_data->machine.probe = mpc8610_hpcd_machine_probe; + machine_data->machine.remove = mpc8610_hpcd_machine_remove; + machine_data->machine.name = "MPC8610 HPCD"; + machine_data->machine.num_links = 1; + machine_data->machine.dai_link = &machine_data->dai; /* Allocate a new audio platform device structure */ sound_device = platform_device_alloc("soc-audio", -1); @@ -465,7 +459,7 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev, goto error; } - machine_data->sound_devdata.card = &mpc8610_hpcd_machine; + machine_data->sound_devdata.card = &machine_data->machine; machine_data->sound_devdata.codec_dev = &soc_codec_device_cs4270; machine_data->machine.platform = &fsl_soc_platform; diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig index caf93fae762..b845699dedd 100644 --- a/sound/soc/s3c24xx/Kconfig +++ b/sound/soc/s3c24xx/Kconfig @@ -1,6 +1,6 @@ config SND_S3C24XX_SOC tristate "SoC Audio for the Samsung S3C24XX chips" - depends on ARCH_S3C2410 + depends on ARCH_S3C2410 || ARCH_S3C64XX help Say Y or M if you want to add support for codecs attached to the S3C24XX AC97, I2S or SSP interface. You will also need @@ -9,8 +9,16 @@ config SND_S3C24XX_SOC config SND_S3C24XX_SOC_I2S tristate +config SND_S3C_I2SV2_SOC + tristate + config SND_S3C2412_SOC_I2S tristate + select SND_S3C_I2SV2_SOC + +config SND_S3C64XX_SOC_I2S + tristate + select SND_S3C_I2SV2_SOC config SND_S3C2443_SOC_AC97 tristate @@ -26,6 +34,15 @@ config SND_S3C24XX_SOC_NEO1973_WM8753 Say Y if you want to add support for SoC audio on smdk2440 with the WM8753. +config SND_S3C24XX_SOC_JIVE_WM8750 + tristate "SoC I2S Audio support for Jive" + depends on SND_S3C24XX_SOC && MACH_JIVE + select SND_SOC_WM8750 + select SND_SOC_WM8750_SPI + select SND_S3C2412_SOC_I2S + help + Sat Y if you want to add support for SoC audio on the Jive. + config SND_S3C24XX_SOC_NEO1973_GTA02_WM8753 tristate "SoC I2S Audio support for NEO1973 GTA02 - WM8753" depends on SND_S3C24XX_SOC && MACH_NEO1973_GTA02 diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile index 74533ec7476..de985410444 100644 --- a/sound/soc/s3c24xx/Makefile +++ b/sound/soc/s3c24xx/Makefile @@ -2,20 +2,26 @@ snd-soc-s3c24xx-objs := s3c24xx-pcm.o snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o +snd-soc-s3c64xx-i2s-objs := s3c64xx-i2s.o snd-soc-s3c2443-ac97-objs := s3c2443-ac97.o +snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.o obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o +obj-$(CONFIG_SND_S3C64XX_SOC_I2S) += snd-soc-s3c64xx-i2s.o +obj-$(CONFIG_SND_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o # S3C24XX Machine Support +snd-soc-jive-wm8750-objs := jive_wm8750.o snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o snd-soc-neo1973-gta02-wm8753-objs := neo1973_gta02_wm8753.o +obj-$(CONFIG_SND_S3C24XX_SOC_JIVE_WM8750) += snd-soc-jive-wm8750.o obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o obj-$(CONFIG_SND_S3C24XX_SOC_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o diff --git a/sound/soc/s3c24xx/jive_wm8750.c b/sound/soc/s3c24xx/jive_wm8750.c new file mode 100644 index 00000000000..7dfe26ea8f4 --- /dev/null +++ b/sound/soc/s3c24xx/jive_wm8750.c @@ -0,0 +1,216 @@ +/* sound/soc/s3c24xx/jive_wm8750.c + * + * Copyright 2007,2008 Simtec Electronics + * + * Based on sound/soc/pxa/spitz.c + * Copyright 2005 Wolfson Microelectronics PLC. + * Copyright 2005 Openedhand Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/clk.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> + +#include <asm/mach-types.h> + +#include "s3c24xx-pcm.h" +#include "s3c2412-i2s.h" + +#include "../codecs/wm8750.h" + +static const struct snd_soc_dapm_route audio_map[] = { + { "Headphone Jack", NULL, "LOUT1" }, + { "Headphone Jack", NULL, "ROUT1" }, + { "Internal Speaker", NULL, "LOUT2" }, + { "Internal Speaker", NULL, "ROUT2" }, + { "LINPUT1", NULL, "Line Input" }, + { "RINPUT1", NULL, "Line Input" }, +}; + +static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_SPK("Internal Speaker", NULL), + SND_SOC_DAPM_LINE("Line In", NULL), +}; + +static int jive_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->socdev->codec; + + snd_soc_dapm_enable_pin(codec, "Headphone Jack"); + snd_soc_dapm_enable_pin(codec, "Internal Speaker"); + snd_soc_dapm_enable_pin(codec, "Line In"); + + snd_soc_dapm_sync(codec); + + return 0; +} + +static int jive_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 *codec_dai = rtd->dai->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + struct s3c_i2sv2_rate_calc div; + unsigned int clk = 0; + int ret = 0; + + switch (params_rate(params)) { + case 8000: + case 16000: + case 48000: + case 96000: + clk = 12288000; + break; + case 11025: + case 22050: + case 44100: + clk = 11289600; + break; + } + + s3c_i2sv2_calc_rate(&div, NULL, params_rate(params), + s3c2412_get_iisclk()); + + /* set codec DAI configuration */ + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* set cpu DAI configuration */ + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* set the codec system clock for DAC and ADC */ + ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk, + SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C2412_DIV_RCLK, div.fs_div); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C2412_DIV_PRESCALER, + div.clk_div - 1); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops jive_ops = { + .startup = jive_startup, + .hw_params = jive_hw_params, +}; + +static int jive_wm8750_init(struct snd_soc_codec *codec) +{ + int err; + + /* These endpoints are not being used. */ + snd_soc_dapm_disable_pin(codec, "LINPUT2"); + snd_soc_dapm_disable_pin(codec, "RINPUT2"); + snd_soc_dapm_disable_pin(codec, "LINPUT3"); + snd_soc_dapm_disable_pin(codec, "RINPUT3"); + snd_soc_dapm_disable_pin(codec, "OUT3"); + snd_soc_dapm_disable_pin(codec, "MONO"); + + /* Add jive specific widgets */ + err = snd_soc_dapm_new_controls(codec, wm8750_dapm_widgets, + ARRAY_SIZE(wm8750_dapm_widgets)); + if (err) { + printk(KERN_ERR "%s: failed to add widgets (%d)\n", + __func__, err); + return err; + } + + snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_sync(codec); + + return 0; +} + +static struct snd_soc_dai_link jive_dai = { + .name = "wm8750", + .stream_name = "WM8750", + .cpu_dai = &s3c2412_i2s_dai, + .codec_dai = &wm8750_dai, + .init = jive_wm8750_init, + .ops = &jive_ops, +}; + +/* jive audio machine driver */ +static struct snd_soc_machine snd_soc_machine_jive = { + .name = "Jive", + .dai_link = &jive_dai, + .num_links = 1, +}; + +/* jive audio private data */ +static struct wm8750_setup_data jive_wm8750_setup = { +}; + +/* jive audio subsystem */ +static struct snd_soc_device jive_snd_devdata = { + .machine = &snd_soc_machine_jive, + .platform = &s3c24xx_soc_platform, + .codec_dev = &soc_codec_dev_wm8750_spi, + .codec_data = &jive_wm8750_setup, +}; + +static struct platform_device *jive_snd_device; + +static int __init jive_init(void) +{ + int ret; + + if (!machine_is_jive()) + return 0; + + printk("JIVE WM8750 Audio support\n"); + + jive_snd_device = platform_device_alloc("soc-audio", -1); + if (!jive_snd_device) + return -ENOMEM; + + platform_set_drvdata(jive_snd_device, &jive_snd_devdata); + jive_snd_devdata.dev = &jive_snd_device->dev; + ret = platform_device_add(jive_snd_device); + + if (ret) + platform_device_put(jive_snd_device); + + return ret; +} + +static void __exit jive_exit(void) +{ + platform_device_unregister(jive_snd_device); +} + +module_init(jive_init); +module_exit(jive_exit); + +MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); +MODULE_DESCRIPTION("ALSA SoC Jive Audio support"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c index 81d2940e710..21249967450 100644 --- a/sound/soc/s3c24xx/neo1973_wm8753.c +++ b/sound/soc/s3c24xx/neo1973_wm8753.c @@ -29,11 +29,11 @@ #include <mach/regs-clock.h> #include <mach/regs-gpio.h> #include <mach/hardware.h> -#include <mach/audio.h> +#include <plat/audio.h> #include <linux/io.h> #include <mach/spi-gpio.h> -#include <asm/mach-types.h> -#include <asm/plat-s3c24xx/regs-iis.h> + +#include <plat/regs-iis.h> #include "../codecs/wm8753.h" #include "lm4857.h" diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.c b/sound/soc/s3c24xx/s3c-i2s-v2.c new file mode 100644 index 00000000000..1dda7c85622 --- /dev/null +++ b/sound/soc/s3c24xx/s3c-i2s-v2.c @@ -0,0 +1,626 @@ +/* sound/soc/s3c24xx/s3c-i2c-v2.c + * + * ALSA Soc Audio Layer - I2S core for newer Samsung SoCs. + * + * Copyright (c) 2006 Wolfson Microelectronics PLC. + * Graeme Gregory graeme.gregory@wolfsonmicro.com + * linux@wolfsonmicro.com + * + * Copyright (c) 2008, 2007, 2004-2005 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/kernel.h> +#include <linux/io.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/soc.h> + +#include <plat/regs-s3c2412-iis.h> + +#include <plat/audio.h> +#include <mach/dma.h> + +#include "s3c-i2s-v2.h" + +#define S3C2412_I2S_DEBUG_CON 0 +#define S3C2412_I2S_DEBUG 0 + +#if S3C2412_I2S_DEBUG +#define DBG(x...) printk(KERN_INFO x) +#else +#define DBG(x...) do { } while (0) +#endif + +static inline struct s3c_i2sv2_info *to_info(struct snd_soc_dai *cpu_dai) +{ + return cpu_dai->private_data; +} + +#define bit_set(v, b) (((v) & (b)) ? 1 : 0) + +#if S3C2412_I2S_DEBUG_CON +static void dbg_showcon(const char *fn, u32 con) +{ + printk(KERN_DEBUG "%s: LRI=%d, TXFEMPT=%d, RXFEMPT=%d, TXFFULL=%d, RXFFULL=%d\n", fn, + bit_set(con, S3C2412_IISCON_LRINDEX), + bit_set(con, S3C2412_IISCON_TXFIFO_EMPTY), + bit_set(con, S3C2412_IISCON_RXFIFO_EMPTY), + bit_set(con, S3C2412_IISCON_TXFIFO_FULL), + bit_set(con, S3C2412_IISCON_RXFIFO_FULL)); + + printk(KERN_DEBUG "%s: PAUSE: TXDMA=%d, RXDMA=%d, TXCH=%d, RXCH=%d\n", + fn, + bit_set(con, S3C2412_IISCON_TXDMA_PAUSE), + bit_set(con, S3C2412_IISCON_RXDMA_PAUSE), + bit_set(con, S3C2412_IISCON_TXCH_PAUSE), + bit_set(con, S3C2412_IISCON_RXCH_PAUSE)); + printk(KERN_DEBUG "%s: ACTIVE: TXDMA=%d, RXDMA=%d, IIS=%d\n", fn, + bit_set(con, S3C2412_IISCON_TXDMA_ACTIVE), + bit_set(con, S3C2412_IISCON_RXDMA_ACTIVE), + bit_set(con, S3C2412_IISCON_IIS_ACTIVE)); +} +#else +static inline void dbg_showcon(const char *fn, u32 con) +{ +} +#endif + + +/* Turn on or off the transmission path. */ +void s3c2412_snd_txctrl(struct s3c_i2sv2_info *i2s, int on) +{ + void __iomem *regs = i2s->regs; + u32 fic, con, mod; + + DBG("%s(%d)\n", __func__, on); + + fic = readl(regs + S3C2412_IISFIC); + con = readl(regs + S3C2412_IISCON); + mod = readl(regs + S3C2412_IISMOD); + + DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); + + if (on) { + con |= S3C2412_IISCON_TXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE; + con &= ~S3C2412_IISCON_TXDMA_PAUSE; + con &= ~S3C2412_IISCON_TXCH_PAUSE; + + switch (mod & S3C2412_IISMOD_MODE_MASK) { + case S3C2412_IISMOD_MODE_TXONLY: + case S3C2412_IISMOD_MODE_TXRX: + /* do nothing, we are in the right mode */ + break; + + case S3C2412_IISMOD_MODE_RXONLY: + mod &= ~S3C2412_IISMOD_MODE_MASK; + mod |= S3C2412_IISMOD_MODE_TXRX; + break; + + default: + dev_err(i2s->dev, "TXEN: Invalid MODE in IISMOD\n"); + } + + writel(con, regs + S3C2412_IISCON); + writel(mod, regs + S3C2412_IISMOD); + } else { + /* Note, we do not have any indication that the FIFO problems + * tha the S3C2410/2440 had apply here, so we should be able + * to disable the DMA and TX without resetting the FIFOS. + */ + + con |= S3C2412_IISCON_TXDMA_PAUSE; + con |= S3C2412_IISCON_TXCH_PAUSE; + con &= ~S3C2412_IISCON_TXDMA_ACTIVE; + + switch (mod & S3C2412_IISMOD_MODE_MASK) { + case S3C2412_IISMOD_MODE_TXRX: + mod &= ~S3C2412_IISMOD_MODE_MASK; + mod |= S3C2412_IISMOD_MODE_RXONLY; + break; + + case S3C2412_IISMOD_MODE_TXONLY: + mod &= ~S3C2412_IISMOD_MODE_MASK; + con &= ~S3C2412_IISCON_IIS_ACTIVE; + break; + + default: + dev_err(i2s->dev, "TXDIS: Invalid MODE in IISMOD\n"); + } + + writel(mod, regs + S3C2412_IISMOD); + writel(con, regs + S3C2412_IISCON); + } + + fic = readl(regs + S3C2412_IISFIC); + dbg_showcon(__func__, con); + DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); +} +EXPORT_SYMBOL_GPL(s3c2412_snd_txctrl); + +void s3c2412_snd_rxctrl(struct s3c_i2sv2_info *i2s, int on) +{ + void __iomem *regs = i2s->regs; + u32 fic, con, mod; + + DBG("%s(%d)\n", __func__, on); + + fic = readl(regs + S3C2412_IISFIC); + con = readl(regs + S3C2412_IISCON); + mod = readl(regs + S3C2412_IISMOD); + + DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); + + if (on) { + con |= S3C2412_IISCON_RXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE; + con &= ~S3C2412_IISCON_RXDMA_PAUSE; + con &= ~S3C2412_IISCON_RXCH_PAUSE; + + switch (mod & S3C2412_IISMOD_MODE_MASK) { + case S3C2412_IISMOD_MODE_TXRX: + case S3C2412_IISMOD_MODE_RXONLY: + /* do nothing, we are in the right mode */ + break; + + case S3C2412_IISMOD_MODE_TXONLY: + mod &= ~S3C2412_IISMOD_MODE_MASK; + mod |= S3C2412_IISMOD_MODE_TXRX; + break; + + default: + dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n"); + } + + writel(mod, regs + S3C2412_IISMOD); + writel(con, regs + S3C2412_IISCON); + } else { + /* See txctrl notes on FIFOs. */ + + con &= ~S3C2412_IISCON_RXDMA_ACTIVE; + con |= S3C2412_IISCON_RXDMA_PAUSE; + con |= S3C2412_IISCON_RXCH_PAUSE; + + switch (mod & S3C2412_IISMOD_MODE_MASK) { + case S3C2412_IISMOD_MODE_RXONLY: + con &= ~S3C2412_IISCON_IIS_ACTIVE; + mod &= ~S3C2412_IISMOD_MODE_MASK; + break; + + case S3C2412_IISMOD_MODE_TXRX: + mod &= ~S3C2412_IISMOD_MODE_MASK; + mod |= S3C2412_IISMOD_MODE_TXONLY; + break; + + default: + dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n"); + } + + writel(con, regs + S3C2412_IISCON); + writel(mod, regs + S3C2412_IISMOD); + } + + fic = readl(regs + S3C2412_IISFIC); + DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); +} +EXPORT_SYMBOL_GPL(s3c2412_snd_rxctrl); + +/* + * Wait for the LR signal to allow synchronisation to the L/R clock + * from the codec. May only be needed for slave mode. + */ +static int s3c2412_snd_lrsync(struct s3c_i2sv2_info *i2s) +{ + u32 iiscon; + unsigned long timeout = jiffies + msecs_to_jiffies(5); + + DBG("Entered %s\n", __func__); + + while (1) { + iiscon = readl(i2s->regs + S3C2412_IISCON); + if (iiscon & S3C2412_IISCON_LRINDEX) + break; + + if (timeout < jiffies) { + printk(KERN_ERR "%s: timeout\n", __func__); + return -ETIMEDOUT; + } + } + + return 0; +} + +/* + * Set S3C2412 I2S DAI format + */ +static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai, + unsigned int fmt) +{ + struct s3c_i2sv2_info *i2s = to_info(cpu_dai); + u32 iismod; + + DBG("Entered %s\n", __func__); + + iismod = readl(i2s->regs + S3C2412_IISMOD); + DBG("hw_params r: IISMOD: %x \n", iismod); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + i2s->master = 0; + iismod &= ~S3C2412_IISMOD_MASTER_MASK; + iismod |= S3C2412_IISMOD_SLAVE; + break; + case SND_SOC_DAIFMT_CBS_CFS: + i2s->master = 1; + iismod &= ~S3C2412_IISMOD_MASTER_MASK; + iismod |= S3C2412_IISMOD_MASTER_INTERNAL; + break; + default: + DBG("unknwon master/slave format\n"); + return -EINVAL; + } + + iismod &= ~S3C2412_IISMOD_SDF_MASK; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + iismod |= S3C2412_IISMOD_SDF_MSB; + break; + case SND_SOC_DAIFMT_LEFT_J: + iismod |= S3C2412_IISMOD_SDF_LSB; + break; + case SND_SOC_DAIFMT_I2S: + iismod |= S3C2412_IISMOD_SDF_IIS; + break; + default: + DBG("Unknown data format\n"); + return -EINVAL; + } + + writel(iismod, i2s->regs + S3C2412_IISMOD); + DBG("hw_params w: IISMOD: %x \n", iismod); + return 0; +} + +static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *socdai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai_link *dai = rtd->dai; + struct s3c_i2sv2_info *i2s = to_info(dai->cpu_dai); + u32 iismod; + + DBG("Entered %s\n", __func__); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dai->cpu_dai->dma_data = i2s->dma_playback; + else + dai->cpu_dai->dma_data = i2s->dma_capture; + + /* Working copies of register */ + iismod = readl(i2s->regs + S3C2412_IISMOD); + DBG("%s: r: IISMOD: %x\n", __func__, iismod); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + iismod |= S3C2412_IISMOD_8BIT; + break; + case SNDRV_PCM_FORMAT_S16_LE: + iismod &= ~S3C2412_IISMOD_8BIT; + break; + } + + writel(iismod, i2s->regs + S3C2412_IISMOD); + DBG("%s: w: IISMOD: %x\n", __func__, iismod); + return 0; +} + +static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct s3c_i2sv2_info *i2s = to_info(rtd->dai->cpu_dai); + int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); + unsigned long irqs; + int ret = 0; + + DBG("Entered %s\n", __func__); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + /* On start, ensure that the FIFOs are cleared and reset. */ + + writel(capture ? S3C2412_IISFIC_RXFLUSH : S3C2412_IISFIC_TXFLUSH, + i2s->regs + S3C2412_IISFIC); + + /* clear again, just in case */ + writel(0x0, i2s->regs + S3C2412_IISFIC); + + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (!i2s->master) { + ret = s3c2412_snd_lrsync(i2s); + if (ret) + goto exit_err; + } + + local_irq_save(irqs); + + if (capture) + s3c2412_snd_rxctrl(i2s, 1); + else + s3c2412_snd_txctrl(i2s, 1); + + local_irq_restore(irqs); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + local_irq_save(irqs); + + if (capture) + s3c2412_snd_rxctrl(i2s, 0); + else + s3c2412_snd_txctrl(i2s, 0); + + local_irq_restore(irqs); + break; + default: + ret = -EINVAL; + break; + } + +exit_err: + return ret; +} + +/* + * Set S3C2412 Clock dividers + */ +static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai, + int div_id, int div) +{ + struct s3c_i2sv2_info *i2s = to_info(cpu_dai); + u32 reg; + + DBG("%s(%p, %d, %d)\n", __func__, cpu_dai, div_id, div); + + switch (div_id) { + case S3C_I2SV2_DIV_BCLK: + reg = readl(i2s->regs + S3C2412_IISMOD); + reg &= ~S3C2412_IISMOD_BCLK_MASK; + writel(reg | div, i2s->regs + S3C2412_IISMOD); + + DBG("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD)); + break; + + case S3C_I2SV2_DIV_RCLK: + if (div > 3) { + /* convert value to bit field */ + + switch (div) { + case 256: + div = S3C2412_IISMOD_RCLK_256FS; + break; + + case 384: + div = S3C2412_IISMOD_RCLK_384FS; + break; + + case 512: + div = S3C2412_IISMOD_RCLK_512FS; + break; + + case 768: + div = S3C2412_IISMOD_RCLK_768FS; + break; + + default: + return -EINVAL; + } + } + + reg = readl(i2s->regs + S3C2412_IISMOD); + reg &= ~S3C2412_IISMOD_RCLK_MASK; + writel(reg | div, i2s->regs + S3C2412_IISMOD); + DBG("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD)); + break; + + case S3C_I2SV2_DIV_PRESCALER: + if (div >= 0) { + writel((div << 8) | S3C2412_IISPSR_PSREN, + i2s->regs + S3C2412_IISPSR); + } else { + writel(0x0, i2s->regs + S3C2412_IISPSR); + } + DBG("%s: PSR=%08x\n", __func__, readl(i2s->regs + S3C2412_IISPSR)); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* default table of all avaialable root fs divisors */ +static unsigned int iis_fs_tab[] = { 256, 512, 384, 768 }; + +int s3c2412_iis_calc_rate(struct s3c_i2sv2_rate_calc *info, + unsigned int *fstab, + unsigned int rate, struct clk *clk) +{ + unsigned long clkrate = clk_get_rate(clk); + unsigned int div; + unsigned int fsclk; + unsigned int actual; + unsigned int fs; + unsigned int fsdiv; + signed int deviation = 0; + unsigned int best_fs = 0; + unsigned int best_div = 0; + unsigned int best_rate = 0; + unsigned int best_deviation = INT_MAX; + + if (fstab == NULL) + fstab = iis_fs_tab; + + for (fs = 0; fs < ARRAY_SIZE(iis_fs_tab); fs++) { + fsdiv = iis_fs_tab[fs]; + + fsclk = clkrate / fsdiv; + div = fsclk / rate; + + if ((fsclk % rate) > (rate / 2)) + div++; + + if (div <= 1) + continue; + + actual = clkrate / (fsdiv * div); + deviation = actual - rate; + + printk(KERN_DEBUG "%dfs: div %d => result %d, deviation %d\n", + fsdiv, div, actual, deviation); + + deviation = abs(deviation); + + if (deviation < best_deviation) { + best_fs = fsdiv; + best_div = div; + best_rate = actual; + best_deviation = deviation; + } + + if (deviation == 0) + break; + } + + printk(KERN_DEBUG "best: fs=%d, div=%d, rate=%d\n", + best_fs, best_div, best_rate); + + info->fs_div = best_fs; + info->clk_div = best_div; + + return 0; +} +EXPORT_SYMBOL_GPL(s3c2412_iis_calc_rate); + +int s3c_i2sv2_probe(struct platform_device *pdev, + struct snd_soc_dai *dai, + struct s3c_i2sv2_info *i2s, + unsigned long base) +{ + struct device *dev = &pdev->dev; + + i2s->dev = dev; + + /* record our i2s structure for later use in the callbacks */ + dai->private_data = i2s; + + i2s->regs = ioremap(base, 0x100); + if (i2s->regs == NULL) { + dev_err(dev, "cannot ioremap registers\n"); + return -ENXIO; + } + + i2s->iis_pclk = clk_get(dev, "iis"); + if (i2s->iis_pclk == NULL) { + DBG("failed to get iis_clock\n"); + iounmap(i2s->regs); + return -ENOENT; + } + + clk_enable(i2s->iis_pclk); + + s3c2412_snd_txctrl(i2s, 0); + s3c2412_snd_rxctrl(i2s, 0); + + return 0; +} + +EXPORT_SYMBOL_GPL(s3c_i2sv2_probe); + +#ifdef CONFIG_PM +static int s3c2412_i2s_suspend(struct snd_soc_dai *dai) +{ + struct s3c_i2sv2_info *i2s = to_info(dai); + u32 iismod; + + if (dai->active) { + i2s->suspend_iismod = readl(i2s->regs + S3C2412_IISMOD); + i2s->suspend_iiscon = readl(i2s->regs + S3C2412_IISCON); + i2s->suspend_iispsr = readl(i2s->regs + S3C2412_IISPSR); + + /* some basic suspend checks */ + + iismod = readl(i2s->regs + S3C2412_IISMOD); + + if (iismod & S3C2412_IISCON_RXDMA_ACTIVE) + pr_warning("%s: RXDMA active?\n", __func__); + + if (iismod & S3C2412_IISCON_TXDMA_ACTIVE) + pr_warning("%s: TXDMA active?\n", __func__); + + if (iismod & S3C2412_IISCON_IIS_ACTIVE) + pr_warning("%s: IIS active\n", __func__); + } + + return 0; +} + +static int s3c2412_i2s_resume(struct snd_soc_dai *dai) +{ + struct s3c_i2sv2_info *i2s = to_info(dai); + + pr_info("dai_active %d, IISMOD %08x, IISCON %08x\n", + dai->active, i2s->suspend_iismod, i2s->suspend_iiscon); + + if (dai->active) { + writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON); + writel(i2s->suspend_iismod, i2s->regs + S3C2412_IISMOD); + writel(i2s->suspend_iispsr, i2s->regs + S3C2412_IISPSR); + + writel(S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH, + i2s->regs + S3C2412_IISFIC); + + ndelay(250); + writel(0x0, i2s->regs + S3C2412_IISFIC); + } + + return 0; +} +#else +#define s3c2412_i2s_suspend NULL +#define s3c2412_i2s_resume NULL +#endif + +int s3c_i2sv2_register_dai(struct snd_soc_dai *dai) +{ + dai->ops.trigger = s3c2412_i2s_trigger; + dai->ops.hw_params = s3c2412_i2s_hw_params; + dai->ops.set_fmt = s3c2412_i2s_set_fmt; + dai->ops.set_clkdiv = s3c2412_i2s_set_clkdiv; + + dai->suspend = s3c2412_i2s_suspend; + dai->resume = s3c2412_i2s_resume; + + return snd_soc_register_dai(dai); +} + +EXPORT_SYMBOL_GPL(s3c_i2sv2_register_dai); diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.h b/sound/soc/s3c24xx/s3c-i2s-v2.h new file mode 100644 index 00000000000..f66854a77fb --- /dev/null +++ b/sound/soc/s3c24xx/s3c-i2s-v2.h @@ -0,0 +1,90 @@ +/* sound/soc/s3c24xx/s3c-i2s-v2.h + * + * ALSA Soc Audio Layer - S3C_I2SV2 I2S driver + * + * Copyright (c) 2007 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. +*/ + +/* This code is the core support for the I2S block found in a number of + * Samsung SoC devices which is unofficially named I2S-V2. Currently the + * S3C2412 and the S3C64XX series use this block to provide 1 or 2 I2S + * channels via configurable GPIO. + */ + +#ifndef __SND_SOC_S3C24XX_S3C_I2SV2_I2S_H +#define __SND_SOC_S3C24XX_S3C_I2SV2_I2S_H __FILE__ + +#define S3C_I2SV2_DIV_BCLK (1) +#define S3C_I2SV2_DIV_RCLK (2) +#define S3C_I2SV2_DIV_PRESCALER (3) + +/** + * struct s3c_i2sv2_info - S3C I2S-V2 information + * @dev: The parent device passed to use from the probe. + * @regs: The pointer to the device registe block. + * @master: True if the I2S core is the I2S bit clock master. + * @dma_playback: DMA information for playback channel. + * @dma_capture: DMA information for capture channel. + * @suspend_iismod: PM save for the IISMOD register. + * @suspend_iiscon: PM save for the IISCON register. + * @suspend_iispsr: PM save for the IISPSR register. + * + * This is the private codec state for the hardware associated with an + * I2S channel such as the register mappings and clock sources. + */ +struct s3c_i2sv2_info { + struct device *dev; + void __iomem *regs; + + struct clk *iis_pclk; + struct clk *iis_cclk; + struct clk *iis_clk; + + unsigned char master; + + struct s3c24xx_pcm_dma_params *dma_playback; + struct s3c24xx_pcm_dma_params *dma_capture; + + u32 suspend_iismod; + u32 suspend_iiscon; + u32 suspend_iispsr; +}; + +struct s3c_i2sv2_rate_calc { + unsigned int clk_div; /* for prescaler */ + unsigned int fs_div; /* for root frame clock */ +}; + +extern int s3c_i2sv2_iis_calc_rate(struct s3c_i2sv2_rate_calc *info, + unsigned int *fstab, + unsigned int rate, struct clk *clk); + +/** + * s3c_i2sv2_probe - probe for i2s device helper + * @pdev: The platform device supplied to the original probe. + * @dai: The ASoC DAI structure supplied to the original probe. + * @i2s: Our local i2s structure to fill in. + * @base: The base address for the registers. + */ +extern int s3c_i2sv2_probe(struct platform_device *pdev, + struct snd_soc_dai *dai, + struct s3c_i2sv2_info *i2s, + unsigned long base); + +/** + * s3c_i2sv2_register_dai - register dai with soc core + * @dai: The snd_soc_dai structure to register + * + * Fill in any missing fields and then register the given dai with the + * soc core. + */ +extern int s3c_i2sv2_register_dai(struct snd_soc_dai *dai); + +#endif /* __SND_SOC_S3C24XX_S3C_I2SV2_I2S_H */ diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c index f3fc0aba0aa..5099d939667 100644 --- a/sound/soc/s3c24xx/s3c2412-i2s.c +++ b/sound/soc/s3c24xx/s3c2412-i2s.c @@ -22,6 +22,7 @@ #include <linux/delay.h> #include <linux/clk.h> #include <linux/kernel.h> +#include <linux/io.h> #include <sound/core.h> #include <sound/pcm.h> @@ -30,20 +31,16 @@ #include <sound/soc.h> #include <mach/hardware.h> -#include <linux/io.h> -#include <asm/dma.h> - -#include <asm/plat-s3c24xx/regs-s3c2412-iis.h> +#include <plat/regs-s3c2412-iis.h> -#include <mach/regs-gpio.h> -#include <mach/audio.h> +#include <plat/regs-gpio.h> +#include <plat/audio.h> #include <mach/dma.h> #include "s3c24xx-pcm.h" #include "s3c2412-i2s.h" #define S3C2412_I2S_DEBUG 0 -#define S3C2412_I2S_DEBUG_CON 0 #if S3C2412_I2S_DEBUG #define DBG(x...) printk(KERN_INFO x) @@ -73,431 +70,7 @@ static struct s3c24xx_pcm_dma_params s3c2412_i2s_pcm_stereo_in = { .dma_size = 4, }; -struct s3c2412_i2s_info { - struct device *dev; - void __iomem *regs; - struct clk *iis_clk; - struct clk *iis_pclk; - struct clk *iis_cclk; - - u32 suspend_iismod; - u32 suspend_iiscon; - u32 suspend_iispsr; -}; - -static struct s3c2412_i2s_info s3c2412_i2s; - -#define bit_set(v, b) (((v) & (b)) ? 1 : 0) - -#if S3C2412_I2S_DEBUG_CON -static void dbg_showcon(const char *fn, u32 con) -{ - printk(KERN_DEBUG "%s: LRI=%d, TXFEMPT=%d, RXFEMPT=%d, TXFFULL=%d, RXFFULL=%d\n", fn, - bit_set(con, S3C2412_IISCON_LRINDEX), - bit_set(con, S3C2412_IISCON_TXFIFO_EMPTY), - bit_set(con, S3C2412_IISCON_RXFIFO_EMPTY), - bit_set(con, S3C2412_IISCON_TXFIFO_FULL), - bit_set(con, S3C2412_IISCON_RXFIFO_FULL)); - - printk(KERN_DEBUG "%s: PAUSE: TXDMA=%d, RXDMA=%d, TXCH=%d, RXCH=%d\n", - fn, - bit_set(con, S3C2412_IISCON_TXDMA_PAUSE), - bit_set(con, S3C2412_IISCON_RXDMA_PAUSE), - bit_set(con, S3C2412_IISCON_TXCH_PAUSE), - bit_set(con, S3C2412_IISCON_RXCH_PAUSE)); - printk(KERN_DEBUG "%s: ACTIVE: TXDMA=%d, RXDMA=%d, IIS=%d\n", fn, - bit_set(con, S3C2412_IISCON_TXDMA_ACTIVE), - bit_set(con, S3C2412_IISCON_RXDMA_ACTIVE), - bit_set(con, S3C2412_IISCON_IIS_ACTIVE)); -} -#else -static inline void dbg_showcon(const char *fn, u32 con) -{ -} -#endif - -/* Turn on or off the transmission path. */ -static void s3c2412_snd_txctrl(int on) -{ - struct s3c2412_i2s_info *i2s = &s3c2412_i2s; - void __iomem *regs = i2s->regs; - u32 fic, con, mod; - - DBG("%s(%d)\n", __func__, on); - - fic = readl(regs + S3C2412_IISFIC); - con = readl(regs + S3C2412_IISCON); - mod = readl(regs + S3C2412_IISMOD); - - DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); - - if (on) { - con |= S3C2412_IISCON_TXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE; - con &= ~S3C2412_IISCON_TXDMA_PAUSE; - con &= ~S3C2412_IISCON_TXCH_PAUSE; - - switch (mod & S3C2412_IISMOD_MODE_MASK) { - case S3C2412_IISMOD_MODE_TXONLY: - case S3C2412_IISMOD_MODE_TXRX: - /* do nothing, we are in the right mode */ - break; - - case S3C2412_IISMOD_MODE_RXONLY: - mod &= ~S3C2412_IISMOD_MODE_MASK; - mod |= S3C2412_IISMOD_MODE_TXRX; - break; - - default: - dev_err(i2s->dev, "TXEN: Invalid MODE in IISMOD\n"); - } - - writel(con, regs + S3C2412_IISCON); - writel(mod, regs + S3C2412_IISMOD); - } else { - /* Note, we do not have any indication that the FIFO problems - * tha the S3C2410/2440 had apply here, so we should be able - * to disable the DMA and TX without resetting the FIFOS. - */ - - con |= S3C2412_IISCON_TXDMA_PAUSE; - con |= S3C2412_IISCON_TXCH_PAUSE; - con &= ~S3C2412_IISCON_TXDMA_ACTIVE; - - switch (mod & S3C2412_IISMOD_MODE_MASK) { - case S3C2412_IISMOD_MODE_TXRX: - mod &= ~S3C2412_IISMOD_MODE_MASK; - mod |= S3C2412_IISMOD_MODE_RXONLY; - break; - - case S3C2412_IISMOD_MODE_TXONLY: - mod &= ~S3C2412_IISMOD_MODE_MASK; - con &= ~S3C2412_IISCON_IIS_ACTIVE; - break; - - default: - dev_err(i2s->dev, "TXDIS: Invalid MODE in IISMOD\n"); - } - - writel(mod, regs + S3C2412_IISMOD); - writel(con, regs + S3C2412_IISCON); - } - - fic = readl(regs + S3C2412_IISFIC); - dbg_showcon(__func__, con); - DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); -} - -static void s3c2412_snd_rxctrl(int on) -{ - struct s3c2412_i2s_info *i2s = &s3c2412_i2s; - void __iomem *regs = i2s->regs; - u32 fic, con, mod; - - DBG("%s(%d)\n", __func__, on); - - fic = readl(regs + S3C2412_IISFIC); - con = readl(regs + S3C2412_IISCON); - mod = readl(regs + S3C2412_IISMOD); - - DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); - - if (on) { - con |= S3C2412_IISCON_RXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE; - con &= ~S3C2412_IISCON_RXDMA_PAUSE; - con &= ~S3C2412_IISCON_RXCH_PAUSE; - - switch (mod & S3C2412_IISMOD_MODE_MASK) { - case S3C2412_IISMOD_MODE_TXRX: - case S3C2412_IISMOD_MODE_RXONLY: - /* do nothing, we are in the right mode */ - break; - - case S3C2412_IISMOD_MODE_TXONLY: - mod &= ~S3C2412_IISMOD_MODE_MASK; - mod |= S3C2412_IISMOD_MODE_TXRX; - break; - - default: - dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n"); - } - - writel(mod, regs + S3C2412_IISMOD); - writel(con, regs + S3C2412_IISCON); - } else { - /* See txctrl notes on FIFOs. */ - - con &= ~S3C2412_IISCON_RXDMA_ACTIVE; - con |= S3C2412_IISCON_RXDMA_PAUSE; - con |= S3C2412_IISCON_RXCH_PAUSE; - - switch (mod & S3C2412_IISMOD_MODE_MASK) { - case S3C2412_IISMOD_MODE_RXONLY: - con &= ~S3C2412_IISCON_IIS_ACTIVE; - mod &= ~S3C2412_IISMOD_MODE_MASK; - break; - - case S3C2412_IISMOD_MODE_TXRX: - mod &= ~S3C2412_IISMOD_MODE_MASK; - mod |= S3C2412_IISMOD_MODE_TXONLY; - break; - - default: - dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n"); - } - - writel(con, regs + S3C2412_IISCON); - writel(mod, regs + S3C2412_IISMOD); - } - - fic = readl(regs + S3C2412_IISFIC); - DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); -} - - -/* - * Wait for the LR signal to allow synchronisation to the L/R clock - * from the codec. May only be needed for slave mode. - */ -static int s3c2412_snd_lrsync(void) -{ - u32 iiscon; - unsigned long timeout = jiffies + msecs_to_jiffies(5); - - DBG("Entered %s\n", __func__); - - while (1) { - iiscon = readl(s3c2412_i2s.regs + S3C2412_IISCON); - if (iiscon & S3C2412_IISCON_LRINDEX) - break; - - if (timeout < jiffies) { - printk(KERN_ERR "%s: timeout\n", __func__); - return -ETIMEDOUT; - } - } - - return 0; -} - -/* - * Check whether CPU is the master or slave - */ -static inline int s3c2412_snd_is_clkmaster(void) -{ - u32 iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD); - - DBG("Entered %s\n", __func__); - - iismod &= S3C2412_IISMOD_MASTER_MASK; - return !(iismod == S3C2412_IISMOD_SLAVE); -} - -/* - * Set S3C2412 I2S DAI format - */ -static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai, - unsigned int fmt) -{ - u32 iismod; - - - DBG("Entered %s\n", __func__); - - iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD); - DBG("hw_params r: IISMOD: %x \n", iismod); - - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: - iismod &= ~S3C2412_IISMOD_MASTER_MASK; - iismod |= S3C2412_IISMOD_SLAVE; - break; - case SND_SOC_DAIFMT_CBS_CFS: - iismod &= ~S3C2412_IISMOD_MASTER_MASK; - iismod |= S3C2412_IISMOD_MASTER_INTERNAL; - break; - default: - DBG("unknwon master/slave format\n"); - return -EINVAL; - } - - iismod &= ~S3C2412_IISMOD_SDF_MASK; - - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_RIGHT_J: - iismod |= S3C2412_IISMOD_SDF_MSB; - break; - case SND_SOC_DAIFMT_LEFT_J: - iismod |= S3C2412_IISMOD_SDF_LSB; - break; - case SND_SOC_DAIFMT_I2S: - iismod |= S3C2412_IISMOD_SDF_IIS; - break; - default: - DBG("Unknown data format\n"); - return -EINVAL; - } - - writel(iismod, s3c2412_i2s.regs + S3C2412_IISMOD); - DBG("hw_params w: IISMOD: %x \n", iismod); - return 0; -} - -static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - u32 iismod; - - DBG("Entered %s\n", __func__); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rtd->dai->cpu_dai->dma_data = &s3c2412_i2s_pcm_stereo_out; - else - rtd->dai->cpu_dai->dma_data = &s3c2412_i2s_pcm_stereo_in; - - /* Working copies of register */ - iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD); - DBG("%s: r: IISMOD: %x\n", __func__, iismod); - - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S8: - iismod |= S3C2412_IISMOD_8BIT; - break; - case SNDRV_PCM_FORMAT_S16_LE: - iismod &= ~S3C2412_IISMOD_8BIT; - break; - } - - writel(iismod, s3c2412_i2s.regs + S3C2412_IISMOD); - DBG("%s: w: IISMOD: %x\n", __func__, iismod); - return 0; -} - -static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) -{ - int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); - unsigned long irqs; - int ret = 0; - - DBG("Entered %s\n", __func__); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - /* On start, ensure that the FIFOs are cleared and reset. */ - - writel(capture ? S3C2412_IISFIC_RXFLUSH : S3C2412_IISFIC_TXFLUSH, - s3c2412_i2s.regs + S3C2412_IISFIC); - - /* clear again, just in case */ - writel(0x0, s3c2412_i2s.regs + S3C2412_IISFIC); - - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if (!s3c2412_snd_is_clkmaster()) { - ret = s3c2412_snd_lrsync(); - if (ret) - goto exit_err; - } - - local_irq_save(irqs); - - if (capture) - s3c2412_snd_rxctrl(1); - else - s3c2412_snd_txctrl(1); - - local_irq_restore(irqs); - break; - - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - local_irq_save(irqs); - - if (capture) - s3c2412_snd_rxctrl(0); - else - s3c2412_snd_txctrl(0); - - local_irq_restore(irqs); - break; - default: - ret = -EINVAL; - break; - } - -exit_err: - return ret; -} - -/* default table of all avaialable root fs divisors */ -static unsigned int s3c2412_iis_fs[] = { 256, 512, 384, 768, 0 }; - -int s3c2412_iis_calc_rate(struct s3c2412_rate_calc *info, - unsigned int *fstab, - unsigned int rate, struct clk *clk) -{ - unsigned long clkrate = clk_get_rate(clk); - unsigned int div; - unsigned int fsclk; - unsigned int actual; - unsigned int fs; - unsigned int fsdiv; - signed int deviation = 0; - unsigned int best_fs = 0; - unsigned int best_div = 0; - unsigned int best_rate = 0; - unsigned int best_deviation = INT_MAX; - - - if (fstab == NULL) - fstab = s3c2412_iis_fs; - - for (fs = 0;; fs++) { - fsdiv = s3c2412_iis_fs[fs]; - - if (fsdiv == 0) - break; - - fsclk = clkrate / fsdiv; - div = fsclk / rate; - - if ((fsclk % rate) > (rate / 2)) - div++; - - if (div <= 1) - continue; - - actual = clkrate / (fsdiv * div); - deviation = actual - rate; - - printk(KERN_DEBUG "%dfs: div %d => result %d, deviation %d\n", - fsdiv, div, actual, deviation); - - deviation = abs(deviation); - - if (deviation < best_deviation) { - best_fs = fsdiv; - best_div = div; - best_rate = actual; - best_deviation = deviation; - } - - if (deviation == 0) - break; - } - - printk(KERN_DEBUG "best: fs=%d, div=%d, rate=%d\n", - best_fs, best_div, best_rate); - - info->fs_div = best_fs; - info->clk_div = best_div; - - return 0; -} -EXPORT_SYMBOL_GPL(s3c2412_iis_calc_rate); +static struct s3c_i2sv2_info s3c2412_i2s; /* * Set S3C2412 Clock source @@ -512,10 +85,12 @@ static int s3c2412_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, switch (clk_id) { case S3C2412_CLKSRC_PCLK: + s3c2412_i2s.master = 1; iismod &= ~S3C2412_IISMOD_MASTER_MASK; iismod |= S3C2412_IISMOD_MASTER_INTERNAL; break; case S3C2412_CLKSRC_I2SCLK: + s3c2412_i2s.master = 0; iismod &= ~S3C2412_IISMOD_MASTER_MASK; iismod |= S3C2412_IISMOD_MASTER_EXTERNAL; break; @@ -527,74 +102,6 @@ static int s3c2412_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, return 0; } -/* - * Set S3C2412 Clock dividers - */ -static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai, - int div_id, int div) -{ - struct s3c2412_i2s_info *i2s = &s3c2412_i2s; - u32 reg; - - DBG("%s(%p, %d, %d)\n", __func__, cpu_dai, div_id, div); - - switch (div_id) { - case S3C2412_DIV_BCLK: - reg = readl(i2s->regs + S3C2412_IISMOD); - reg &= ~S3C2412_IISMOD_BCLK_MASK; - writel(reg | div, i2s->regs + S3C2412_IISMOD); - - DBG("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD)); - break; - - case S3C2412_DIV_RCLK: - if (div > 3) { - /* convert value to bit field */ - - switch (div) { - case 256: - div = S3C2412_IISMOD_RCLK_256FS; - break; - - case 384: - div = S3C2412_IISMOD_RCLK_384FS; - break; - - case 512: - div = S3C2412_IISMOD_RCLK_512FS; - break; - - case 768: - div = S3C2412_IISMOD_RCLK_768FS; - break; - - default: - return -EINVAL; - } - } - - reg = readl(s3c2412_i2s.regs + S3C2412_IISMOD); - reg &= ~S3C2412_IISMOD_RCLK_MASK; - writel(reg | div, i2s->regs + S3C2412_IISMOD); - DBG("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD)); - break; - - case S3C2412_DIV_PRESCALER: - if (div >= 0) { - writel((div << 8) | S3C2412_IISPSR_PSREN, - i2s->regs + S3C2412_IISPSR); - } else { - writel(0x0, i2s->regs + S3C2412_IISPSR); - } - DBG("%s: PSR=%08x\n", __func__, readl(i2s->regs + S3C2412_IISPSR)); - break; - - default: - return -EINVAL; - } - - return 0; -} struct clk *s3c2412_get_iisclk(void) { @@ -606,20 +113,16 @@ EXPORT_SYMBOL_GPL(s3c2412_get_iisclk); static int s3c2412_i2s_probe(struct platform_device *pdev, struct snd_soc_dai *dai) { - DBG("Entered %s\n", __func__); + int ret; - s3c2412_i2s.dev = &pdev->dev; + DBG("Entered %s\n", __func__); - s3c2412_i2s.regs = ioremap(S3C2410_PA_IIS, 0x100); - if (s3c2412_i2s.regs == NULL) - return -ENXIO; + ret = s3c_i2sv2_probe(pdev, dai, &s3c2412_i2s, S3C2410_PA_IIS); + if (ret) + return ret; - s3c2412_i2s.iis_pclk = clk_get(&pdev->dev, "iis"); - if (s3c2412_i2s.iis_pclk == NULL) { - DBG("failed to get iis_clock\n"); - iounmap(s3c2412_i2s.regs); - return -ENODEV; - } + s3c2412_i2s.dma_capture = &s3c2412_i2s_pcm_stereo_in; + s3c2412_i2s.dma_playback = &s3c2412_i2s_pcm_stereo_out; s3c2412_i2s.iis_cclk = clk_get(&pdev->dev, "i2sclk"); if (s3c2412_i2s.iis_cclk == NULL) { @@ -628,12 +131,12 @@ static int s3c2412_i2s_probe(struct platform_device *pdev, return -ENODEV; } - clk_set_parent(s3c2412_i2s.iis_cclk, clk_get(NULL, "mpll")); + /* Set MPLL as the source for IIS CLK */ - clk_enable(s3c2412_i2s.iis_pclk); + clk_set_parent(s3c2412_i2s.iis_cclk, clk_get(NULL, "mpll")); clk_enable(s3c2412_i2s.iis_cclk); - s3c2412_i2s.iis_clk = s3c2412_i2s.iis_pclk; + s3c2412_i2s.iis_cclk = s3c2412_i2s.iis_pclk; /* Configure the I2S pins in correct mode */ s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_I2SLRCK); @@ -642,78 +145,18 @@ static int s3c2412_i2s_probe(struct platform_device *pdev, s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_I2SSDI); s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_I2SSDO); - s3c2412_snd_txctrl(0); - s3c2412_snd_rxctrl(0); - return 0; } -#ifdef CONFIG_PM -static int s3c2412_i2s_suspend(struct snd_soc_dai *dai) -{ - struct s3c2412_i2s_info *i2s = &s3c2412_i2s; - u32 iismod; - - if (dai->active) { - i2s->suspend_iismod = readl(i2s->regs + S3C2412_IISMOD); - i2s->suspend_iiscon = readl(i2s->regs + S3C2412_IISCON); - i2s->suspend_iispsr = readl(i2s->regs + S3C2412_IISPSR); - - /* some basic suspend checks */ - - iismod = readl(i2s->regs + S3C2412_IISMOD); - - if (iismod & S3C2412_IISCON_RXDMA_ACTIVE) - pr_warning("%s: RXDMA active?\n", __func__); - - if (iismod & S3C2412_IISCON_TXDMA_ACTIVE) - pr_warning("%s: TXDMA active?\n", __func__); - - if (iismod & S3C2412_IISCON_IIS_ACTIVE) - pr_warning("%s: IIS active\n", __func__); - } - - return 0; -} - -static int s3c2412_i2s_resume(struct snd_soc_dai *dai) -{ - struct s3c2412_i2s_info *i2s = &s3c2412_i2s; - - pr_info("dai_active %d, IISMOD %08x, IISCON %08x\n", - dai->active, i2s->suspend_iismod, i2s->suspend_iiscon); - - if (dai->active) { - writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON); - writel(i2s->suspend_iismod, i2s->regs + S3C2412_IISMOD); - writel(i2s->suspend_iispsr, i2s->regs + S3C2412_IISPSR); - - writel(S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH, - i2s->regs + S3C2412_IISFIC); - - ndelay(250); - writel(0x0, i2s->regs + S3C2412_IISFIC); - - } - - return 0; -} -#else -#define s3c2412_i2s_suspend NULL -#define s3c2412_i2s_resume NULL -#endif /* CONFIG_PM */ - #define S3C2412_I2S_RATES \ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) struct snd_soc_dai s3c2412_i2s_dai = { - .name = "s3c2412-i2s", - .id = 0, - .probe = s3c2412_i2s_probe, - .suspend = s3c2412_i2s_suspend, - .resume = s3c2412_i2s_resume, + .name = "s3c2412-i2s", + .id = 0, + .probe = s3c2412_i2s_probe, .playback = { .channels_min = 2, .channels_max = 2, @@ -727,10 +170,6 @@ struct snd_soc_dai s3c2412_i2s_dai = { .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE, }, .ops = { - .trigger = s3c2412_i2s_trigger, - .hw_params = s3c2412_i2s_hw_params, - .set_fmt = s3c2412_i2s_set_fmt, - .set_clkdiv = s3c2412_i2s_set_clkdiv, .set_sysclk = s3c2412_i2s_set_sysclk, }, }; @@ -738,7 +177,7 @@ EXPORT_SYMBOL_GPL(s3c2412_i2s_dai); static int __init s3c2412_i2s_init(void) { - return snd_soc_register_dai(&s3c2412_i2s_dai); + return s3c_i2sv2_register_dai(&s3c2412_i2s_dai); } module_init(s3c2412_i2s_init); @@ -748,7 +187,6 @@ static void __exit s3c2412_i2s_exit(void) } module_exit(s3c2412_i2s_exit); - /* Module information */ MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>"); MODULE_DESCRIPTION("S3C2412 I2S SoC Interface"); diff --git a/sound/soc/s3c24xx/s3c2412-i2s.h b/sound/soc/s3c24xx/s3c2412-i2s.h index aac08a25e54..92848e54be1 100644 --- a/sound/soc/s3c24xx/s3c2412-i2s.h +++ b/sound/soc/s3c24xx/s3c2412-i2s.h @@ -15,9 +15,11 @@ #ifndef __SND_SOC_S3C24XX_S3C2412_I2S_H #define __SND_SOC_S3C24XX_S3C2412_I2S_H __FILE__ -#define S3C2412_DIV_BCLK (1) -#define S3C2412_DIV_RCLK (2) -#define S3C2412_DIV_PRESCALER (3) +#include "s3c-i2s-v2.h" + +#define S3C2412_DIV_BCLK S3C_I2SV2_DIV_BCLK +#define S3C2412_DIV_RCLK S3C_I2SV2_DIV_RCLK +#define S3C2412_DIV_PRESCALER S3C_I2SV2_DIV_PRESCALER #define S3C2412_CLKSRC_PCLK (0) #define S3C2412_CLKSRC_I2SCLK (1) @@ -26,13 +28,4 @@ extern struct clk *s3c2412_get_iisclk(void); extern struct snd_soc_dai s3c2412_i2s_dai; -struct s3c2412_rate_calc { - unsigned int clk_div; /* for prescaler */ - unsigned int fs_div; /* for root frame clock */ -}; - -extern int s3c2412_iis_calc_rate(struct s3c2412_rate_calc *info, - unsigned int *fstab, - unsigned int rate, struct clk *clk); - #endif /* __SND_SOC_S3C24XX_S3C2412_I2S_H */ diff --git a/sound/soc/s3c24xx/s3c2443-ac97.c b/sound/soc/s3c24xx/s3c2443-ac97.c index 5822d2dd49b..5c7f18a2264 100644 --- a/sound/soc/s3c24xx/s3c2443-ac97.c +++ b/sound/soc/s3c24xx/s3c2443-ac97.c @@ -31,7 +31,7 @@ #include <plat/regs-ac97.h> #include <mach/regs-gpio.h> #include <mach/regs-clock.h> -#include <mach/audio.h> +#include <plat/audio.h> #include <asm/dma.h> #include <mach/dma.h> diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c index 6f54c56f6c5..06e2dbe1fd0 100644 --- a/sound/soc/s3c24xx/s3c24xx-i2s.c +++ b/sound/soc/s3c24xx/s3c24xx-i2s.c @@ -4,7 +4,7 @@ * (c) 2006 Wolfson Microelectronics PLC. * Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com * - * (c) 2004-2005 Simtec Electronics + * Copyright 2004-2005 Simtec Electronics * http://armlinux.simtec.co.uk/ * Ben Dooks <ben@simtec.co.uk> * @@ -30,11 +30,11 @@ #include <mach/hardware.h> #include <mach/regs-gpio.h> #include <mach/regs-clock.h> -#include <mach/audio.h> +#include <plat/audio.h> #include <asm/dma.h> #include <mach/dma.h> -#include <asm/plat-s3c24xx/regs-iis.h> +#include <plat/regs-iis.h> #include "s3c24xx-pcm.h" #include "s3c24xx-i2s.h" diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c24xx-pcm.c index 5d5c73be8e0..e797e862724 100644 --- a/sound/soc/s3c24xx/s3c24xx-pcm.c +++ b/sound/soc/s3c24xx/s3c24xx-pcm.c @@ -4,7 +4,7 @@ * (c) 2006 Wolfson Microelectronics PLC. * Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com * - * (c) 2004-2005 Simtec Electronics + * Copyright 2004-2005 Simtec Electronics * http://armlinux.simtec.co.uk/ * Ben Dooks <ben@simtec.co.uk> * @@ -29,7 +29,7 @@ #include <asm/dma.h> #include <mach/hardware.h> #include <mach/dma.h> -#include <mach/audio.h> +#include <plat/audio.h> #include "s3c24xx-pcm.h" @@ -225,23 +225,16 @@ static int s3c24xx_pcm_prepare(struct snd_pcm_substream *substream) * sync to pclk, half-word transfers to the IIS-FIFO. */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { s3c2410_dma_devconfig(prtd->params->channel, - S3C2410_DMASRC_MEM, S3C2410_DISRCC_INC | - S3C2410_DISRCC_APB, prtd->params->dma_addr); - - s3c2410_dma_config(prtd->params->channel, - prtd->params->dma_size, - S3C2410_DCON_SYNC_PCLK | - S3C2410_DCON_HANDSHAKE); + S3C2410_DMASRC_MEM, + prtd->params->dma_addr); } else { - s3c2410_dma_config(prtd->params->channel, - prtd->params->dma_size, - S3C2410_DCON_HANDSHAKE | - S3C2410_DCON_SYNC_PCLK); - s3c2410_dma_devconfig(prtd->params->channel, - S3C2410_DMASRC_HW, 0x3, - prtd->params->dma_addr); + S3C2410_DMASRC_HW, + prtd->params->dma_addr); } + + s3c2410_dma_config(prtd->params->channel, + prtd->params->dma_size); /* flush the DMA channel */ s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_FLUSH); diff --git a/sound/soc/s3c24xx/s3c24xx_uda134x.c b/sound/soc/s3c24xx/s3c24xx_uda134x.c index a0a4d1832a1..8e79a416db5 100644 --- a/sound/soc/s3c24xx/s3c24xx_uda134x.c +++ b/sound/soc/s3c24xx/s3c24xx_uda134x.c @@ -22,7 +22,7 @@ #include <sound/s3c24xx_uda134x.h> #include <sound/uda134x.h> -#include <asm/plat-s3c24xx/regs-iis.h> +#include <plat/regs-iis.h> #include "s3c24xx-pcm.h" #include "s3c24xx-i2s.h" diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.c b/sound/soc/s3c24xx/s3c64xx-i2s.c new file mode 100644 index 00000000000..bf7c3d73739 --- /dev/null +++ b/sound/soc/s3c24xx/s3c64xx-i2s.c @@ -0,0 +1,185 @@ +/* sound/soc/s3c24xx/s3c64xx-i2s.c + * + * ALSA SoC Audio Layer - S3C64XX I2S driver + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * http://armlinux.simtec.co.uk/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/kernel.h> +#include <linux/gpio.h> +#include <linux/io.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/soc.h> + +#include <plat/regs-s3c2412-iis.h> +#include <plat/gpio-bank-d.h> +#include <plat/gpio-bank-e.h> +#include <plat/gpio-cfg.h> +#include <plat/audio.h> + +#include <mach/map.h> +#include <mach/dma.h> + +#include "s3c24xx-pcm.h" +#include "s3c64xx-i2s.h" + +static struct s3c2410_dma_client s3c64xx_dma_client_out = { + .name = "I2S PCM Stereo out" +}; + +static struct s3c2410_dma_client s3c64xx_dma_client_in = { + .name = "I2S PCM Stereo in" +}; + +static struct s3c24xx_pcm_dma_params s3c64xx_i2s_pcm_stereo_out[2] = { + [0] = { + .client = &s3c64xx_dma_client_out, + .dma_addr = S3C64XX_PA_IIS0 + S3C2412_IISTXD, + .dma_size = 4, + }, + [1] = { + .client = &s3c64xx_dma_client_out, + .dma_addr = S3C64XX_PA_IIS1 + S3C2412_IISTXD, + .dma_size = 4, + }, +}; + +static struct s3c24xx_pcm_dma_params s3c64xx_i2s_pcm_stereo_in[2] = { + [0] = { + .client = &s3c64xx_dma_client_in, + .dma_addr = S3C64XX_PA_IIS0 + S3C2412_IISRXD, + .dma_size = 4, + }, + [1] = { + .client = &s3c64xx_dma_client_in, + .dma_addr = S3C64XX_PA_IIS1 + S3C2412_IISRXD, + .dma_size = 4, + }, +}; + +static struct s3c_i2sv2_info s3c64xx_i2s[2]; + +static int s3c64xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + /* TODO */ + return 0; +} + +static int s3c64xx_i2s_probe(struct platform_device *pdev, + struct snd_soc_dai *dai) +{ + struct device *dev = &pdev->dev; + struct s3c_i2sv2_info *i2s; + int ret; + + dev_dbg(dev, "%s: probing dai %d\n", __func__, pdev->id); + + if (pdev->id < 0 || pdev->id > ARRAY_SIZE(s3c64xx_i2s)) { + dev_err(dev, "id %d out of range\n", pdev->id); + return -EINVAL; + } + + i2s = &s3c64xx_i2s[pdev->id]; + + ret = s3c_i2sv2_probe(pdev, dai, i2s, + pdev->id ? S3C64XX_PA_IIS1 : S3C64XX_PA_IIS0); + if (ret) + return ret; + + i2s->dma_capture = &s3c64xx_i2s_pcm_stereo_in[pdev->id]; + i2s->dma_playback = &s3c64xx_i2s_pcm_stereo_out[pdev->id]; + + i2s->iis_cclk = clk_get(dev, "i2sclk"); + if (i2s->iis_cclk == NULL) { + dev_err(dev, "failed to get i2sclk"); + iounmap(i2s->regs); + return -ENODEV; + } + + /* configure GPIO for i2s port */ + switch (pdev->id) { + case 0: + s3c_gpio_cfgpin(S3C64XX_GPD(0), S3C64XX_GPD0_I2S0_CLK); + s3c_gpio_cfgpin(S3C64XX_GPD(1), S3C64XX_GPD1_I2S0_CDCLK); + s3c_gpio_cfgpin(S3C64XX_GPD(2), S3C64XX_GPD2_I2S0_LRCLK); + s3c_gpio_cfgpin(S3C64XX_GPD(3), S3C64XX_GPD3_I2S0_DI); + s3c_gpio_cfgpin(S3C64XX_GPD(4), S3C64XX_GPD4_I2S0_D0); + break; + case 1: + s3c_gpio_cfgpin(S3C64XX_GPE(0), S3C64XX_GPE0_I2S1_CLK); + s3c_gpio_cfgpin(S3C64XX_GPE(1), S3C64XX_GPE1_I2S1_CDCLK); + s3c_gpio_cfgpin(S3C64XX_GPE(2), S3C64XX_GPE2_I2S1_LRCLK); + s3c_gpio_cfgpin(S3C64XX_GPE(3), S3C64XX_GPE3_I2S1_DI); + s3c_gpio_cfgpin(S3C64XX_GPE(4), S3C64XX_GPE4_I2S1_D0); + } + + return 0; +} + + +#define S3C64XX_I2S_RATES \ + (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) + +#define S3C64XX_I2S_FMTS \ + (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE) + +struct snd_soc_dai s3c64xx_i2s_dai = { + .name = "s3c64xx-i2s", + .id = 0, + .probe = s3c64xx_i2s_probe, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = S3C64XX_I2S_RATES, + .formats = S3C64XX_I2S_FMTS, + }, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = S3C64XX_I2S_RATES, + .formats = S3C64XX_I2S_FMTS, + }, + .ops = { + .set_sysclk = s3c64xx_i2s_set_sysclk, + }, +}; +EXPORT_SYMBOL_GPL(s3c64xx_i2s_dai); + +static int __init s3c64xx_i2s_init(void) +{ + return s3c_i2sv2_register_dai(&s3c64xx_i2s_dai); +} +module_init(s3c64xx_i2s_init); + +static void __exit s3c64xx_i2s_exit(void) +{ + snd_soc_unregister_dai(&s3c64xx_i2s_dai); +} +module_exit(s3c64xx_i2s_exit); + +/* Module information */ +MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>"); +MODULE_DESCRIPTION("S3C64XX I2S SoC Interface"); +MODULE_LICENSE("GPL"); + + + diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.h b/sound/soc/s3c24xx/s3c64xx-i2s.h new file mode 100644 index 00000000000..288b0affe14 --- /dev/null +++ b/sound/soc/s3c24xx/s3c64xx-i2s.h @@ -0,0 +1,28 @@ +/* sound/soc/s3c24xx/s3c64xx-i2s.h + * + * ALSA SoC Audio Layer - S3C64XX I2S driver + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * http://armlinux.simtec.co.uk/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __SND_SOC_S3C24XX_S3C64XX_I2S_H +#define __SND_SOC_S3C24XX_S3C64XX_I2S_H __FILE__ + +#include "s3c-i2s-v2.h" + +#define S3C64XX_DIV_BCLK S3C_I2SV2_DIV_BCLK +#define S3C64XX_DIV_RCLK S3C_I2SV2_DIV_RCLK +#define S3C64XX_DIV_PRESCALER S3C_I2SV2_DIV_PRESCALER + +/* todo - define clock sources */ + +extern struct snd_soc_dai s3c64xx_i2s_dai; + +#endif /* __SND_SOC_S3C24XX_S3C64XX_I2S_H */ |